Android in app purchase: Signature verification failed

Solution 1:

This problem is still going on in the current Google billing version. Basically the android.test.purchased is broken; After you buy android.test.purchased the verifyPurchase function in Security.java will always fail and the QueryInventoryFinishedListener will stop at the line if (result.isFailure()); this is because the android.test.purchased item always fails the TextUtils.isEmpty(signature) check in Security.java as it is not a real item and has no signature returned by the server.

My advice (from lack of any other solution) is to NEVER use "android.test.purchased". There are various code tweaks on the net but none of them work 100%.

If you have used the android.test.purchased then one way to get rid of the error is to do the following:-

  1. Edit Security.java and change the "return false" line in the verifyPurchase to "return true" - this is temporary, we'll be putting it back in a minute.
  2. In your QueryInventoryFinishedListener, after the "if (result.isFailure()) {...}" lines add the following to consume and get rid of your never ending android.test.purchased item:

    if (inventory.hasPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD)) {  
       mHelper.consumeAsync(inventory.getPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD),null);
       }
    
  3. Run your app so the consunmeAsync happens, this gets rid of the "android.test.purchased" item on the server.

  4. Remove the consumeAsync code (or comment it out).
  5. Back in the Security.java, change the "return true" back to "return false".

Your QueryInventoryFinishedListener will no longer error on the verify, everything is back to "normal" (if you can call it that). Remember - don't bother using android.test.purchased again as it will just cause this error again... it's broke! The only real way to test your purchasing it to upload an APK, wait for it to appear, and then test it (the same APK) on your device with logging enabled.

Solution 2:

Yes, the problem still occurs. After I bought android.test.purchased I start getting the error on quering the inventory. It is possible to fix your phone by just clearing data of Google Play Store application and running Google Play one time. When you clear data of Google Play it forgets that you bought android.test.purchased

Solution 3:

Please check that base64EncodedPublicKey and the one from the Play Developer Console are equal. Once you re-upload the APK in the Developer Console, the public key may change, if so update your base64EncodedPublicKey.

Solution 4:

You can skip the verifying process for those "android.test.*" product ids. If you are using the sample code from the TrivialDrive example, open IabHelper.java, find the following line code, change it from

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

into

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

It's harmless, even if you forgot to rollback the code. So, you can continue to test the further workflow step.

Solution 5:

Based on GMTDev's answer, this is what I do in order to fix the testing issues when consuming products in the simplest possible way. In Security.java, replace the verifyPurchase() method with this:

public static boolean verifyPurchase(String base64PublicKey, String signedData, String 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);
}

I only modified one line (see comment), and this way you can keep the code like that for debugging and still publish your release versions safely.