Comment vérifier l'achat d'une application Android côté serveur (Google Play dans App Billing v3)


96

J'ai une application simple (nécessite une connexion utilisateur avec un compte). Je propose des fonctionnalités premium aux utilisateurs payants, comme plus de contenu d'actualité.

Je dois enregistrer si l'utilisateur a acheté cet article dans la base de données de mon serveur. Lorsque je fournis du contenu de données à l'appareil de l'utilisateur, je peux alors vérifier l'état de l'utilisateur et fournir un contenu différent pour l'utilisateur payant.

J'ai vérifié l'exemple officiel de Trivialdrive fourni par Google, il ne fournit aucun exemple de code pour la vérification côté serveur, voici mes questions.

  1. J'ai trouvé que l'exemple utilise la clé publique de mon application à l'intérieur pour vérifier l'achat, cela ne semble pas bon, je pense que je peux simplement déplacer le processus de vérification sur mon serveur combiné avec les informations de connexion de l'utilisateur pour voir si l'achat de l'utilisateur est terminé, puis mettre à jour la base de données.
  2. Il existe également une API d'achat que je peux utiliser pour interroger, ce dont j'ai besoin est de transmettre le ticket d'achat de l'utilisateur au serveur.

Je ne suis pas sûr de la méthode à suivre pour vérifier l'achat de l'utilisateur et marquer le statut de l'utilisateur dans ma base de données, peut-être les deux?

Et j'ai peur qu'il y ait une situation, si un utilisateur a acheté cet article sur google play, mais pour une raison quelconque, juste à ce moment-là, lorsque mon application a lancé la vérification sur mon serveur, la connexion réseau est en panne ou mon propre serveur est en panne , l'utilisateur vient de payer l'argent dans google play mais je n'ai pas enregistré l'achat sur mon serveur? Que dois-je faire, comment puis-je gérer cette situation.


Vous devriez probablement supprimer l'indicateur ios de cette question.
Gustavo Guevara

Réponses:


160

Il semble que ce que vous recherchez est un moyen de vérifier si l'utilisateur a des fonctionnalités premium activées sur son compte, c'est donc par là que je commencerais;

Assurez-vous qu'il existe un indicateur d'une sorte sur votre base de données indiquant si l'utilisateur dispose de fonctionnalités premium et incluez-le dans la charge utile de réponse de l'API lors de la demande d'informations sur le compte. Ce drapeau sera votre autorité principale pour les «fonctionnalités premium».

Lorsqu'un utilisateur effectue un achat dans l'application, mettez en cache les détails (jeton, ID de commande et ID de produit) localement sur le client (c'est-à-dire l'application), puis envoyez-les à votre API.

Votre API doit ensuite envoyer le purchaseTokenà l' API Google Play Developer pour validation.

Quelques choses peuvent se produire à partir d'ici:

  1. Le reçu est valide, votre API répond au client avec un code de statut 200 Ok
  2. Le reçu n'est pas valide, votre API répond au client avec un code d'état 400 Bad Request
  3. L'API Google Play est en panne, votre API répond avec un code d'état 502 Bad Gateway

Dans le cas de 1. ou 2. (codes de statut 2xx ou 4xx), votre client efface le cache des détails d'achat car il n'en a plus besoin car l'API a indiqué qu'il a été reçu.

Après une validation réussie (cas 1), vous devez définir l' premiumindicateur sur true pour l'utilisateur.

Dans le cas de 3. (code d'état 5xx) ou d'un délai d'expiration du réseau, le client doit continuer d'essayer jusqu'à ce qu'il reçoive un code d'état 2xx ou 4xx de votre API.

En fonction de vos besoins, vous pouvez le faire attendre quelques secondes avant de l'envoyer à nouveau ou simplement envoyer les détails à votre API chaque fois que l'application est lancée à nouveau ou sort de l'arrière-plan si les détails de l'achat sont présents dans le cache de l'application.

Cette approche doit prendre en charge les délais d'expiration du réseau, les serveurs indisponibles, etc.

Il y a maintenant quelques questions à considérer:

Que doit-il se passer immédiatement après un achat? L'application doit-elle attendre que la validation soit réussie avant de fournir un contenu premium ou doit-elle provisoirement accorder l'accès et le supprimer si la validation échoue?

L'octroi d'un accès provisoire aux fonctionnalités premium facilite le processus pour une majorité de vos utilisateurs, mais vous accorderez également l'accès à un certain nombre d'utilisateurs frauduleux pendant que votre API valide le purchaseToken.

Pour le dire d'une autre manière: l'achat est valide jusqu'à ce qu'il soit prouvé frauduleux ou; frauduleux jusqu'à preuve de la validité?

Afin d'identifier si l'utilisateur a toujours un abonnement valide lorsque sa période d'abonnement arrive pour le renouvellement, vous devrez planifier une revalidation sur le purchaseTokenà exécuter au expiryTimeMillisqui a été retourné dans le résultat .

Si le expiryTimeMillisest dans le passé, vous pouvez définir l' premiumindicateur sur false. Si c'est dans le futur, reprogrammez-le à nouveau pour le nouveau expiryTimeMillis.

Enfin, pour vous assurer que l'utilisateur dispose d'un accès premium (ou non), votre application doit interroger votre API pour obtenir les détails de l'utilisateur lors du lancement de l'application ou lorsqu'elle sort de l'arrière-plan.


Pour une application payante, comment vais-je recevoir le reçu de Google?
Merbin Joe

2
Salut! Il n'y a aucun moyen d'accéder à l'historique des abonnements sur Google? Comment éviter de perdre le fait de l'utilisation de cubscription déjà achetée dans le cas où l'application plante au moment du stockage de purchaseToken?
scythargon

2
J'ai un problème similaire ... au lieu de laisser l'application envoyer le jeton à l'API ne serait pas plus fiable de demander au serveur de développement Google de le faire avec une notification push directement à mon API?
Gianluca Ghettini

Pour les abonnements qui ont été annulés, l'API Google Play Developer en renverra toujours 200 après l'annulation, si le même ancien jeton d'achat est utilisé pour la validation.
Cezar Cobuz le

Donc, pour un abonnement, vous suggérez qu'après le premier appel sur le serveur, nous stockions le jeton d'achat et l'ID du produit, et planifions un autre appel de vérification (réexécutez la même demande) lorsque expiryTimeMillis se produit? Est-ce ainsi que nous sommes censés vérifier la validité de l'abonnement? Existe-t-il des directives d'Android sur la façon de procéder? Apple a obtenu une vidéo de la WWDC à ce sujet qui en explique assez clairement les bonnes pratiques, mais ne trouve pas grand-chose sur le Play Store.
schankam

26

La documentation à ce sujet est déroutante et étrangement verbeuse avec les choses qui sont presque sans importance tout en laissant la documentation réellement importante presque sans lien et très difficile à trouver. Cela devrait fonctionner parfaitement sur la plate-forme de serveur la plus populaire capable d'exécuter les bibliothèques clientes de google api, notamment Java, Python, .Net et NodeJS. Remarque: j'ai testé uniquement le client api Python comme indiqué ci-dessous.

Etapes nécessaires:

  1. Créez un projet d'API, à partir du lien d'accès à l'API dans votre console Google Play

  2. Créez un nouveau compte de service, enregistrez la clé privée JSON qui est générée. Vous devrez apporter ce fichier sur votre serveur.

  3. Appuyez sur Terminé dans la section du compte de service de la console Play pour actualiser, puis autoriser l'accès au compte de service

  4. Obtenez une bibliothèque cliente google api pour votre plate-forme serveur à partir de https://developers.google.com/api-client-library

  5. Utilisez la bibliothèque client de votre plate-forme particulière pour créer une interface de service et lire directement le résultat de votre vérification d'achat.

Vous n'avez pas besoin de vous soucier des portées d'autorisation, des appels de demandes personnalisées, de l'actualisation des jetons d'accès, etc., la bibliothèque cliente de l'API s'occupe de tout. Voici un exemple d'utilisation de la bibliothèque Python pour vérifier un abonnement:

Tout d'abord, installez le client google api dans votre pipenv comme ceci:

$ pipenv install google-api-python-client

Ensuite, vous pouvez configurer les informations d'identification du client API à l'aide du fichier json de clé privée pour authentifier le compte de service.

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

Vous pouvez désormais vérifier directement les achats d'abonnements ou de produits à l'aide de la bibliothèque.

#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}

La documentation de l'interface de service de plate-forme pour l'API Play Developer n'est pas liée de manière facile à trouver, pour certains, elle est carrément difficile à trouver . Voici les liens vers les plateformes populaires que j'ai trouvées:

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


5
D'accord, la documentation est horrible ... Des idées sur la façon de faire cela avec Firebase (Firestore) et les fonctions Cloud en tant que backend?
Jeff Padgett

Si vos fonctions Cloud sont dans NodeJS, vous pouvez peut-être utiliser le lien NodeJS ci-dessus pour faire fonctionner la bibliothèque cliente API?
Dhiraj Gupta

17

Exemple complet d'utilisation de la bibliothèque cliente des API Google pour PHP :

  1. Configurez votre projet Google et accédez à Google Play pour votre compte de service comme décrit dans la réponse de Marc ici https://stackoverflow.com/a/35138885/1046909 .

  2. Installez la bibliothèque: https://developers.google.com/api-client-library/php/start/installation .

  3. Vous pouvez maintenant vérifier votre reçu de la manière suivante:

    $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);

    Après cet achat $ est une instance de Google_Service_AndroidPublisher_SubscriptionPurchase

    $purchase->getAutoRenewing();
    $purchase->getCancelReason();
    ...

Cela ne fonctionne pas, je continue à obtenir (401) Connexion requise et setAuthConfig n'accepte pas les informations d'identification du compte de service json
Raulnd

Celui-ci a fonctionné pour moi putenv ('GOOGLE_APPLICATION_CREDENTIALS = credentials.json'); $ client = nouveau Google_Client (); $ client-> useApplicationDefaultCredentials (); $ client-> addScope (' googleapis.com/auth/androidpublisher' ); $ service = nouveau Google_Service_AndroidPublisher ($ client); $ purchase = $ service-> achats_products-> get ($ packageName, $ productId, $ token); var_dump ($ achat);
Raulnd

Cette chose est en cas de facturation inapp. Que faire si je veux obtenir orderId dans ma base de données chaque fois que l'utilisateur achète mon application sur le Play Store au lieu de inapp?
Ankesh kumar Jaisansaria

stackoverflow.com/questions/48662787/… Veuillez consulter cette question. Je cherche une réponse à cette question. Cela a également une prime active
Ankesh kumar Jaisansaria

@MingalevME Que faire si le format du jeton n'est pas valide et que PHP obtient une erreur fatale, comment puis-je détecter cette erreur?
alexx0186

12

Vous pouvez essayer d'utiliser Purchases.subscriptions: get server-side. Il prend packageName, subscriptionId et token comme paramètres et nécessite une autorisation .

Vérifie si l'achat d'abonnement d'un utilisateur est valide et renvoie son heure d'expiration.

En cas de succès, cette méthode retourne une ressource Purchases.subscriptions dans le corps de la réponse.


9
J'ai de sérieux problèmes pour faire fonctionner l'autorisation.

8
Sérieusement. Pour le niveau critique des achats pour certaines applications, le support et la documentation sont insensés. Voici ce que vous devez faire sur le serveur: github.com/google/… . Plus d'informations ici: stackoverflow.com/questions/35127086/…
utilisateur

0

Je réponds à cette préoccupation

la connexion réseau est en panne ou mon propre serveur est en panne, l'utilisateur vient de payer l'argent dans google play mais je n'ai pas enregistré l'achat sur mon serveur? Que dois-je faire, comment puis-je gérer cette situation.

La situation est:

L'utilisateur achète l'élément 'abc' à l'aide du service Google Play -> retourne OK -> échoue à vérifier avec le serveur pour certaines raisons telles que l'absence de connexion Internet.

La solution est:

Côté client, avant d'afficher le bouton "Google Wallet", vous vérifiez si l'élément "abc" est déjà détenu.

  • si oui, vérifiez à nouveau avec le serveur
  • si non, affichez le bouton "Google Wallet".

Achat achat = 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


4
Je ne comprends pas pourquoi la validation sur le serveur est plus sécurisée que la validation sur l'application. En fin de compte, c'est l'application qui déverrouille les fonctionnalités, il est donc toujours possible de supprimer ou d'inverser le code de l'application qui vérifie si la réponse du serveur est "OK"
Gianluca Ghettini

2
@GianlucaGhettini parce que, parfois, le serveur est ce qui fournit le service acheté, pas l'application d'ailleurs, l'application pourrait être rétro-ingénierie et avec un peu de difficulté, le processus de vérification pourrait être piraté.
Mohyaddin Alaoddin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.