Android in app purchase: La vérification de la signature a échoué

J’ai essayé pendant plusieurs jours de résoudre ce problème, en utilisant le code de démonstration Dungeons fourni avec le SDK. J’ai essayé de trouver une réponse à Google mais je n’en trouve pas.

  • Dans la démo de Donjons, j’ai transmis ma clé publique depuis la console de développement.
  • Signé le apk et téléchargé sur la console sans publier.
  • Test à la fois pour android.test.purchased et liste de produits créée sur la console avec publication pour abonnement (la fonctionnalité principale que je souhaite pour mon application).

Mais je reçois toujours une erreur de Signature verification failed de Signature verification failed et la signature ne correspond pas aux données. Comment puis-je resoudre ceci?

 public static ArrayList verifyPurchase(Ssortingng signedData, Ssortingng signature) { if (signedData == null) { Log.e(TAG, "data is null"); return null; } if (Consts.DEBUG) { Log.i(TAG, "signedData: " + signedData); } boolean verified = false; if (!TextUtils.isEmpty(signature)) { Ssortingng base64EncodedPublicKey = "MIIBIjA....AQAB"; PublicKey key = Security.generatePublicKey(base64EncodedPublicKey); verified = Security.verify(key, signedData, signature); if (!verified) { Log.w(TAG, "signature does not match data."); return null; } } } public static boolean verify(PublicKey publicKey, Ssortingng signedData, Ssortingng signature) { if (Consts.DEBUG) { Log.i(TAG, "signature: " + signature); } Signature sig; try { sig = Signature.getInstance(SIGNATURE_ALGORITHM); sig.initVerify(publicKey); sig.update(signedData.getBytes()); if (!sig.verify(Base64.decode(signature))) { Log.e(TAG, "Signature verification failed."); return false; } return true; } catch (NoSuchAlgorithmException e) { Log.e(TAG, "NoSuchAlgorithmException."); } catch (InvalidKeyException e) { Log.e(TAG, "Invalid key specification."); } catch (SignatureException e) { Log.e(TAG, "Signature exception."); } catch (Base64DecoderException e) { Log.e(TAG, "Base64 decoding failed."); } return false; } 

Ce problème persiste dans la version de facturation actuelle de Google. Fondamentalement, l’ android.test.purchased est cassé; Après l’achat de android.test.purchased, la fonction verifyPurchase dans Security.java échouera toujours et QueryInventoryFinishedListener s’arrêtera à la ligne if (result.isFailure ()) ; En effet, l’élément android.test.purchased échoue toujours à la vérification TextUtils.isEmpty (signature) dans Security.java car il ne s’agit pas d’un article réel et aucune signature n’a été renvoyée par le serveur.

Mon conseil (faute de toute autre solution) est de ne JAMAIS utiliser “android.test.purchased”. Il existe différents réglages de code sur le net mais aucun ne fonctionne à 100%.

Si vous avez utilisé android.test.purchased, une des façons de vous débarrasser de l’erreur consiste à: –

  1. Editez Security.java et modifiez la ligne “return false” dans verifyPurchase pour “retourner true” – ceci est temporaire, nous le remettrons dans une minute.
  2. Dans votre QueryInventoryFinishedListener, après les lignes “if (result.isFailure ()) {…}”, ajoutez ce qui suit pour consumr et vous débarrasser de votre article sans fin android.test.purchased:

     if (inventory.hasPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD)) { mHelper.consumeAsync(inventory.getPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD),null); } 
  3. Exécutez votre application afin que le consunmeAsync se produise, cela supprime l’élément “android.test.purchased” sur le serveur.

  4. Supprimez le code consumeAsync (ou mettez-le en commentaire).
  5. De retour dans Security.java, modifiez le “return true” en “return false”.

Votre QueryInventoryFinishedListener ne sera plus en erreur lors de la vérification, tout est revenu à “normal” (si vous pouvez l’appeler ainsi). Rappelez-vous – ne vous embêtez pas à utiliser à nouveau android.test.purchased car il ne fera que causer cette erreur à nouveau … c’est cassé! Le seul moyen réel de tester votre achat pour télécharger un APK, attendez qu’il apparaisse, puis testez-le (le même APK) sur votre appareil avec la connexion activée.

Oui, le problème persiste. Après avoir acheté android.test.purchased, je commence à avoir l’erreur de consulter l’inventaire. Il est possible de réparer votre téléphone en effaçant simplement les données de l’application Google Play Store et en exécutant une seule fois Google Play. Lorsque vous effacez des données de Google Play, il oublie que vous avez acheté android.test.purchased

Veuillez vérifier que base64EncodedPublicKey et celui de Play Developer Console sont égaux. Une fois que vous téléchargez à nouveau l’APK dans la console du développeur , la clé publique peut être modifiée, dans ce cas, mettez à jour votre base64EncodedPublicKey .

Sur la base de la réponse de GMTDev, c’est ce que je fais afin de résoudre les problèmes de test lors de la consommation de produits de la manière la plus simple possible . Dans Security.java, remplacez la méthode verifyPurchase () par ceci:

 public static boolean verifyPurchase(Ssortingng base64PublicKey, Ssortingng signedData, Ssortingng signature) { if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) || TextUtils.isEmpty(signature)) { Log.e(TAG, "Purchase verification failed: missing data."); return BuildConfig.DEBUG; // Line modified by Cristian. Original line was: return false; } PublicKey key = Security.generatePublicKey(base64PublicKey); return Security.verify(key, signedData, signature); } 

J’ai seulement modifié une ligne (voir commentaire), et de cette façon vous pouvez garder le code comme ça pour le débogage et toujours publier vos versions en toute sécurité.

Vous pouvez ignorer le processus de vérification pour les identifiants de produit “android.test. *”. Si vous utilisez l’exemple de code de l’exemple TrivialDrive, ouvrez IabHelper.java, recherchez le code de ligne suivant, remplacez-le par

  if (Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... } 

dans

  boolean verifySignature = !sku.startsWith("android.test."); // or inplace the condition in the following line if (verifySignature && !Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... } 

C’est inoffensif, même si vous avez oublié de restaurer le code. Ainsi, vous pouvez continuer à tester la prochaine étape du workflow.

L’erreur est due à une clé de licence incorrecte. La clé de licence provient probablement de votre autre application.

La solution consiste à utiliser la clé de licence appropriée à partir de:

Play console> App> Outils de développement> Licence et facturation intégrée

Cette solution a fonctionné pour moi. J’ai changé la nouvelle méthode verifyPurchase en classe d’achat avec l’ancienne.

Ce qui a fonctionné pour moi, lors de l’utilisation de la facturation intégrée à l’application v3 et des classes d’utilitaires incluses, consistait à utiliser l’achat de test dans l’appel onActivityResult renvoyé.

Aucune modification apscope à IabHelper, Security ou à l’une des classes de fonctions de facturation In-App n’est nécessaire pour éviter cela lors des futurs achats de test.

Si vous avez déjà essayé d’acheter le produit de test et que vous êtes maintenant bloqué sur l’erreur de vérification de la signature d’achat, ce qui est probablement le cas puisque vous recherchez des réponses à cette erreur, vous devez:

  1. apporter les modifications recommandées par GMTDev
  2. lance l’application pour s’assurer qu’elle consum le produit
  3. supprimer / annuler les modifications de GMTDev
  4. implémenter le code ci-dessous dans onActivityResult.

Cela permet non seulement au processus de test d’achat d’être fluide, mais cela devrait également éviter tout problème contradictoire avec iab renvoyant l’erreur « Élément déjà possédé » lors de la tentative de rachat du produit de test.

Si elle est appelée à partir d’un fragment et que le fragment onActivityResult de votre fragment n’est pas appelé, assurez-vous d’appeler YourFragmentName.onActivityResult (requestCode, resultCode, data) à partir de votre ActivityFragment parent si nécessaire. Ceci est expliqué plus en détail dans Appel de startIntentSenderForResult à partir de fragment (Android Billing v3) .

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_PURCHASE) { //this ensures that the mHelper.flagEndAsync() gets called //prior to starting a new async request. mHelper.handleActivityResult(requestCode, resultCode, data); //get needed data from Intent extra to recreate product object int responseCode = data.getIntExtra("RESPONSE_CODE", 0); Ssortingng purchaseData = data.getSsortingngExtra("INAPP_PURCHASE_DATA"); Ssortingng dataSignature = data.getSsortingngExtra("INAPP_DATA_SIGNATURE"); // Ssortingp out getActivity() if not being used within a fragment if (resultCode == getActivity().RESULT_OK) { try { JSONObject jo = new JSONObject(purchaseData); Ssortingng sku = jo.getSsortingng("productId"); //only auto consume the android.test.purchased product if (sku.equals("android.test.purchased")) { //build the purchase object from the response data Purchase purchase = new Purchase("inapp", purchaseData, dataSignature); //consume android.test.purchased mHelper.consumeAsync(purchase,null); } } catch (JSONException je) { //failed to parse the purchase data je.printStackTrace(); } catch (IllegalStateException ise) { //most likely either disposed, not setup, or //another billing async process is already running ise.printStackTrace(); } catch (Exception e) { //unexpected error e.printStackTrace(); } } } } 

Il ne supprimera l’achat que s’il s’agit de “android.test.purchased”, il convient donc de l’utiliser en toute sécurité.

La vérification de signature échoue uniquement pour le produit de test par défaut. Une solution rapide:

  • Aller à la classe IabHelper.
  • Inversez les conditions if de Security.verifyPurchase .

C’est tout!

N’oubliez pas d’inverser les modifications lorsque le produit de test est remplacé par le produit réel

Cochez cette réponse :

Le compte principal de votre périphérique de test est-il identique à votre compte de développeur Google Play?

Sinon, vous n’obtiendrez pas de signatures sur les réponses statiques android.test. * À moins que l’application n’ait été publiée sur Play auparavant.

Voir le tableau à l’ adresse http://developer.android.com/guide/market/billing/billing_testing.html#static-responses-table pour connaître l’ensemble des conditions.

Et c’est un commentaire:

Je ne pense pas que les identifiants statiques renvoient plus la signature. Voir https://groups.google.com/d/topic/android-developers/PCbCJdOl480/discussion

En outre, auparavant, l’exemple de code (utilisé par de nombreuses grandes applications) de la bibliothèque de facturation Google Play autorisait une signature vide. C’est pourquoi les achats statiques ont fonctionné là.
Mais c’était une faille de sécurité, alors quand il a été publié , Google a soumis une mise à jour .

J’ai le même problème et suivez @Deadolus dit basé sur https://www.gaffga.de/implementing-in-app-billing-for-android/

Le point clé est que nous devons faire en sorte que le SKU soit consommable même si le résultat de la requête d’inventaire a échoué. Ci-dessous, voici comment j’ai fait cela.

 IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { Log.d(TAG, "Query inventory finished."); // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // Is it a failure? if (result.isFailure()) { try { Purchase purchase = new Purchase("inapp", "{\"packageName\":\"PACKAGE_NAME\","+ "\"orderId\":\"transactionId.android.test.purchased\","+ "\"productId\":\"android.test.purchased\",\"developerPayload\":\"\",\"purchaseTime\":0,"+ "\"purchaseState\":0,\"purchaseToken\":\"inapp:PACKAGE_NAME :android.test.purchased\"}", ""); } catch (JSONException e) { e.printStackTrace(); } mHelper.consumeAsync(purchase, null); complain("Failed to query inventory: " + result); return; } Log.d(TAG, "Query inventory was successful."); /* * Check for items we own. Notice that for each purchase, we check * the developer payload to see if it's correct! See * verifyDeveloperPayload(). */ } }; 

Remplacez PACKAGE_NAME dans le code ci-dessus par le nom du package de votre application.

C’est ce qui a fonctionné pour moi:

  1. Appelez BillingClient.querySkuDetailsAsync pour demander si l’élément est disponible
  2. Attendez SkuDetailsResponseListener.onSkuDetailsResponse
  3. Attendez encore 500ms
  4. Lancer l’achat en utilisant BillingClient.launchBillingFlow …

L’étape 3 ne devrait pas être nécessaire car quand j’ai reçu onSkuDetailsResponse, ça devrait aller, mais ce n’est pas le cas, j’ai dû attendre un peu. Après cet achat fonctionne, pas plus “Erreur d’article non disponible”. Voici comment je l’ai testé:

  1. effacer les données de mon application
  2. effacer les données Google Play
  3. exécuter l’application
  4. acheter android.test.purchased
  5. essayer d’acheter mes articles (il échoue avec l’article non disponible)
  6. utiliser ma solution ci-dessus, ça marche