Jak zweryfikować zakup dla aplikacji na Androida po stronie serwera (Google Play w rozliczeniach aplikacji v3)

104

Mam prostą aplikację (wymaga logowania użytkownika za pomocą konta). Zapewniam kilka funkcji premium dla płatnych użytkowników, takich jak więcej treści informacyjnych.

Muszę odnotować, czy użytkownik kupił ten przedmiot w bazie danych mojego serwera. Kiedy dostarczam zawartość danych do urządzenia użytkownika, mogę następnie sprawdzić status użytkownika i udostępnić różne treści dla płatnego użytkownika.

Sprawdziłem oficjalny przykład Trivialdrive dostarczony przez Google, nie zawiera żadnego przykładowego kodu do weryfikacji po stronie serwera, oto moje pytania.

  1. Zauważyłem, że próbka używa klucza publicznego mojej aplikacji w celu weryfikacji zakupu, nie wygląda dobrze, myślę, że mogę po prostu przenieść proces weryfikacji na mój serwer w połączeniu z danymi logowania użytkownika, aby sprawdzić, czy zakup użytkownika został zakończony, a następnie zaktualizować bazę danych.
  2. Istnieje również API zakupów, którego mogę użyć do zapytania, czego potrzebuję, aby przekazać zakup tokena użytkownika na serwer.

Nie jestem pewien, jaką metodę powinienem zastosować, aby zweryfikować zakup użytkownika i oznaczyć status użytkownika w mojej bazie danych, a może oba?

I obawiam się, że jest taka sytuacja, że ​​użytkownik kupił ten element w Google Play, ale z jakiegoś powodu, właśnie w tym czasie, gdy moja aplikacja uruchomiła weryfikację na moim serwerze, połączenie sieciowe jest wyłączone lub mój własny serwer jest wyłączony , użytkownik właśnie zapłacił pieniądze w Google Play, ale nie zarejestrowałem zakupu na swoim serwerze? Co powinienem zrobić, jak sobie poradzić w tej sytuacji.

virsir
źródło

Odpowiedzi:

163

Wygląda na to, że szukasz sposobu na sprawdzenie, czy użytkownik ma włączone funkcje premium na swoim koncie, więc od tego powinienem zacząć;

Upewnij się, że w bazie danych znajduje się jakaś flaga wskazująca, czy użytkownik ma funkcje premium, i uwzględnij ją w ładunku odpowiedzi interfejsu API podczas żądania informacji o koncie. Ta flaga będzie Twoim głównym uprawnieniem do korzystania z „funkcji premium”.

Gdy użytkownik dokonuje zakupu w aplikacji, buforuj szczegóły (token, identyfikator zamówienia i identyfikator produktu) lokalnie na kliencie (tj. W aplikacji), a następnie wyślij je do swojego interfejsu API.

Twój interfejs API powinien następnie wysłać purchaseTokendo Google Play Developer API w celu weryfikacji.

Z tego miejsca może się wydarzyć kilka rzeczy:

  1. Potwierdzenie jest ważne, twoje API odpowiada klientowi kodem statusu 200 Ok
  2. Potwierdzenie jest nieprawidłowe, Twój interfejs API odpowiada klientowi kodem statusu 400 Bad Request
  3. Interfejs API Google Play nie działa, Twój interfejs API odpowiada kodem stanu 502 Bad Gateway

W przypadku 1. lub 2. (kody statusu 2xx lub 4xx) klient czyści pamięć podręczną szczegółów zakupu, ponieważ nie potrzebuje ich już, ponieważ API wskazało, że został odebrany.

Po pomyślnym sprawdzeniu poprawności (przypadek 1.) należy ustawić premiumflagę na wartość true dla użytkownika.

W przypadku 3. (kod stanu 5xx) lub przekroczenia limitu czasu sieci klient powinien próbować, aż otrzyma kod stanu 2xx lub 4xx z interfejsu API.

W zależności od wymagań możesz poczekać kilka sekund przed ponownym wysłaniem lub po prostu wysłać szczegóły do ​​interfejsu API, gdy aplikacja zostanie ponownie uruchomiona lub wyjdzie z tła, jeśli szczegóły zakupu znajdują się w pamięci podręcznej aplikacji.

Takie podejście powinno zająć się przekroczeniem limitu czasu sieci, niedostępnością serwerów itp.

Należy teraz rozważyć kilka pytań:

Co powinno się stać zaraz po zakupie? Czy aplikacja powinna czekać, aż weryfikacja zakończy się pomyślnie, zanim udostępni zawartość premium, czy też powinna wstępnie przyznać dostęp i odebrać ją, jeśli weryfikacja się nie powiedzie?

Przyznanie wstępnego dostępu do funkcji premium usprawnia proces większości użytkowników, ale podczas sprawdzania poprawności przez interfejs API będzie można również przyznać dostęp wielu nieuczciwym użytkownikom purchaseToken.

Innymi słowy: zakup jest ważny do czasu udowodnienia oszustwa lub; oszukańcze, dopóki nie okaże się ważne?

Aby określić, czy użytkownik nadal ma ważną subskrypcję, gdy zbliża się okres jej odnowienia, należy zaplanować ponowną weryfikację w purchaseTokencelu uruchomienia w momencie expiryTimeMilliszwrócenia w wyniku .

Jeśli expiryTimeMillisto przeszłość, możesz ustawić premiumflagę na fałsz. Jeśli będzie w przyszłości, zmień harmonogram na nowy expiryTimeMillis.

Wreszcie, aby upewnić się, że użytkownik ma dostęp premium (lub nie), Twoja aplikacja powinna wysyłać zapytania do interfejsu API o szczegółowe informacje o uruchomieniu aplikacji lub wyjściu z tła.

Marc Greenstock
źródło
W jaki sposób otrzymam rachunek z Google w przypadku płatnej aplikacji?
Merbin Joe
2
Cześć! Nie ma sposobu, aby uzyskać dostęp do historii subskrypcji w Google? Jak zapobiec utracie faktu, że wykorzystałeś już zakupiony abonament w przypadku awarii aplikacji w momencie zapisywania zakupionego tokena?
scythargon
2
Mam podobny problem… zamiast pozwolić aplikacji na wysyłanie tokena do interfejsu API, nie byłoby bardziej niezawodne, aby poinstruować serwer programisty Google, aby zrobił to z powiadomieniem push prosto do mojego interfejsu API?
Gianluca Ghettini
W przypadku subskrypcji, które zostały anulowane, Google Play Developer API będzie zwracać 200 po anulowaniu, jeśli do weryfikacji zostanie użyty ten sam stary token zakupu.
Cezar Cobuz
Więc w przypadku subskrypcji sugerujesz, aby po pierwszym wywołaniu na serwerze przechowywać token zakupu i identyfikator produktu oraz zaplanować inne połączenie weryfikacyjne (ponowne uruchomienie tego samego żądania), gdy nastąpi expiryTimeMillis? Czy w ten sposób mamy weryfikować ważność subskrypcji? Czy są jakieś wskazówki od Androida, jak to zrobić? Apple dostało wideo WWDC na ten temat, które dość wyraźnie wyjaśnia dobre praktyki, ale nie może znaleźć wiele o Sklepie Play.
schankam
30

Dokumentacja na ten temat jest myląca i dziwnie rozwlekła w stosunku do rzeczy, które są prawie nieistotne, podczas gdy faktycznie ważna dokumentacja jest prawie niepołączona i bardzo trudna do znalezienia. Powinno to działać świetnie na najpopularniejszej platformie serwerowej, na której można uruchamiać biblioteki klienta Google API, w tym między innymi Java, Python, .Net i NodeJS. Uwaga: przetestowałem tylko klienta Python API, jak pokazano poniżej.

Niezbędne kroki:

  1. Stwórz projekt API, korzystając z linku API Access w konsoli Google Play

  2. Utwórz nowe konto usługi, zapisz wygenerowany klucz prywatny JSON. Musisz przenieść ten plik na swój serwer.

  3. Naciśnij Gotowe w sekcji konta usługi konsoli Play, aby odświeżyć, a następnie przyznać dostęp do konta usługi

  4. Pobierz bibliotekę klienta API Google dla swojej platformy serwerowej ze strony https://developers.google.com/api-client-library

  5. Skorzystaj z biblioteki klienta swojej platformy, aby zbudować interfejs usługi i bezpośrednio odczytać wynik weryfikacji zakupu.

Zdajesz nie trzeba męczyć się z zakresów zezwoleń, wysyłać żądania niestandardowych połączeń, orzeźwiający tokeny dostępu itp biblioteka klient api dba o wszystko. Oto przykład użycia biblioteki Python do weryfikacji subskrypcji:

Najpierw zainstaluj klienta Google API w swoim pipenv w następujący sposób:

$ pipenv install google-api-python-client

Następnie można skonfigurować poświadczenia klienta interfejsu API przy użyciu pliku json klucza prywatnego do uwierzytelniania konta usługi.

credentials = service_account.Credentials.from_service_account_file("service_account.json")

Teraz możesz bezpośrednio zweryfikować zakup subskrypcji lub zakup produktów za pomocą biblioteki.

#Build the "service" interface to the API you want
service = googleapiclient.discovery.build("androidpublisher", "v3", credentials=credentials)

#Use the token your API got from the app to verify the purchase
result = service.purchases().subscriptions().get(packageName="your.app.package.id", subscriptionId="sku.name", token="token-from-app").execute()
#result is a python object that looks like this ->
# {'kind': 'androidpublisher#subscriptionPurchase', 'startTimeMillis': '1534326259450', 'expiryTimeMillis': '1534328356187', 'autoRenewing': False, 'priceCurrencyCode': 'INR', 'priceAmountMicros': '70000000', 'countryCode': 'IN', 'developerPayload': '', 'cancelReason': 1, 'orderId': 'GPA.1234-4567-1234-1234..5', 'purchaseType': 0}

Dokumentacja interfejsu usługi platformy dla Play Developer API nie jest łatwo dostępna, a dla niektórych wręcz trudna do znalezienia . Oto linki do popularnych platform, które znalazłem:

Python | Java | .NET | PHP | NodeJS (Github TS) | Idź (Github JSON)

Dhiraj Gupta
źródło
6
Zgadzam się, dokumentacja jest okropna ... Jakieś pomysły, jak to zrobić z Firebase (Firestore) i chmurą jako zaplecze?
Jeff Padgett,
Jeśli twoje funkcje w chmurze są w NodeJS, być może możesz użyć powyższego łącza NodeJS, aby uruchomić bibliotekę klienta API?
Dhiraj Gupta,
17

Kompletny przykład użycia biblioteki klienta interfejsu API Google dla PHP :

  1. Skonfiguruj swój projekt Google i dostęp do Google Play dla swojego konta usługi, jak opisano w odpowiedzi Marca tutaj https://stackoverflow.com/a/35138885/1046909 .

  2. Zainstaluj bibliotekę: https://developers.google.com/api-client-library/php/start/installation .

  3. Teraz możesz zweryfikować swój rachunek w następujący sposób:

    $client = new \Google_Client();
    $client->setAuthConfig('/path/to/service/account/credentials.json');
    $client->addScope('https://www.googleapis.com/auth/androidpublisher');
    $service = new \Google_Service_AndroidPublisher($client);
    $purchase = $service->purchases_subscriptions->get($packageName, $productId, $token);
    

    Po tym $ zakup jest wystąpieniem Google_Service_AndroidPublisher_SubscriptionPurchase

    $purchase->getAutoRenewing();
    $purchase->getCancelReason();
    ...
    
MingalevME
źródło
To nie działa, ciągle otrzymuję (401) Wymagane logowanie, a setAuthConfig nie akceptuje danych logowania konta usługi json
Raulnd
Ten pracował dla mnie putenv ('GOOGLE_APPLICATION_CREDENTIALS = credentials.json'); $ client = nowy Google_Client (); $ client-> useApplicationDefaultCredentials (); $ client-> addScope (' googleapis.com/auth/androidpublisher' ); $ service = new Google_Service_AndroidPublisher ($ client); $ zakup = $ usługa-> zakup_produktów-> pobierz ($ packageName, $ productId, $ token); var_dump (zakup $);
Raulnd
Tak jest w przypadku rozliczeń w aplikacji. Co się stanie, jeśli chcę otrzymywać orderId do mojej bazy danych za każdym razem, gdy użytkownik kupi moją aplikację w sklepie Play zamiast w aplikacji?
Ankesh kumar Jaisansaria
stackoverflow.com/questions/48662787/… Zobacz to pytanie. Szukam odpowiedzi na to pytanie. Ma to również aktywną nagrodę
Ankesh kumar Jaisansaria,
@MingalevME Co się stanie, jeśli format tokena jest nieprawidłowy, a PHP otrzyma błąd krytyczny, jak mogę wyłapać ten błąd?
alexx0186
12

Możesz spróbować użyć Purchases.subscriptions: get server-side. Pobiera PackageName, subscriptionId oraz token jako parametry i wymaga autoryzacji .

Sprawdza, czy zakup subskrypcji użytkownika jest ważny i zwraca jej czas wygaśnięcia.

Jeśli się powiedzie, ta metoda zwraca zasób Purchases.subscriptions w treści odpowiedzi.

losowy
źródło
9
Mam poważne problemy, aby zezwolenie działało.
9
Poważnie. Jeśli chodzi o krytyczne znaczenie zakupów dla niektórych aplikacji, wsparcie i dokumentacja są bezsensowne. Oto, co musisz zrobić na serwerze: github.com/google/… . Więcej informacji tutaj: stackoverflow.com/questions/35127086/…
użytkownik
0

Odpowiadam na ten niepokój

połączenie sieciowe jest wyłączone lub mój własny serwer nie działa, użytkownik właśnie zapłacił pieniądze w Google Play, ale nie zarejestrowałem zakupu na swoim serwerze? Co mam zrobić, jak sobie radzić w tej sytuacji.

Sytuacja jest taka:

Użytkownik kupuje element „abc” za pomocą usługi Google Play -> wróć OK -> nie udało się zweryfikować na serwerze z pewnych powodów, takich jak brak połączenia z Internetem.

Rozwiązanie to:

Po stronie klienta, przed wyświetleniem przycisku „Portfel Google”, sprawdzasz, czy pozycja „abc” nie jest już posiadana.

  • jeśli tak, sprawdź ponownie z serwerem
  • jeśli nie, pokaż przycisk „Portfel Google”.

Zakup zakup = mInventory.getPurchase ('abc');

if (purchase != null) // Verify with server 

else // show Google Wallet button

https://developer.android.com/google/play/billing/billing_reference.html#getSkuDetails

thanhbinh84
źródło
4
Nie rozumiem, dlaczego sprawdzanie poprawności na serwerze jest bezpieczniejsze niż sprawdzanie w aplikacji. Ostatecznie to aplikacja odblokowuje funkcje, więc nadal można usunąć lub odwrócić kod w aplikacji, który sprawdza, czy odpowiedź serwera jest „OK”
Gianluca Ghettini
3
@GianlucaGhettini, ponieważ czasami to serwer zapewnia zakupioną usługę, a nie aplikacja, poza tym aplikacja może zostać poddana inżynierii wstecznej, a następnie przy pewnych trudnościach proces weryfikacji może zostać zhakowany.
Mohyaddin Alaoddin
0

Odpowiedź Marca Greenstocka jest zdecydowanie pouczająca, ale kilka rzeczy, na które należy zwrócić uwagę, zajęło mi dużo czasu, zanim się zorientowałem (przynajmniej o wiele więcej niż się spodziewałem):

  1. Musiałem zaznaczyć opcję „Włącz delegowanie dostępu w całej domenie G Suite” w ustawieniach konta usługi. Bez tego ciągle pojawia się ten błąd: „Bieżący użytkownik ma niewystarczające uprawnienia do wykonania żądanej operacji” Obraz z zaznaczoną opcją Włącz delegowanie w całej domenie G Suite

  2. Dla celów testowych można utworzyć Token JWT dla konta serwisowego tutaj , tylko nie zapomnij wybrać RS256 algorytmu.

  3. Klucz publiczny to „private_key_id” z pobranego pliku JSON. Ma również następujący format:

    ----- BEGIN PUBLIC KEY ----- {private_key_id} ----- END PUBLIC KEY -----

  4. Klucz prywatny to „private_key” z pobranego pliku JSON

  5. Wymagane roszczenia pokolenia JWT opisane są tutaj .

  6. Nie wiesz, czym dokładnie jest token JWT i jak jest składany? Nie wstydź się, sprawdź ten link . Możliwe, że jesteś taki jak ja i długo zastanawiałem się, co to jest, to jest (o wiele) prostsze niż się wydaje.

Leonardo Bilck
źródło