2
votes

When I try to get the subscriptions I have the following error:

Signature verification failed. signature does not match data. In-app billing warning: Purchase signature verification FAILED. Not adding item.

My code is:

String base64EncodedPublicKey = MY_KEY;

        // compute your public key and store it in base64EncodedPublicKey
        mHelper = new IabHelper(this, base64EncodedPublicKey);

        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            public void onIabSetupFinished(IabResult result) {
                if (!result.isSuccess()) {
                    // Oh noes, there was a problem.
                    MyDialogFragment alertDialog_generalError = MyDialogFragment.newInstance(getString(R.string.dialog_alert),getString(R.string.error_general));
                    alertDialog_generalError.show(getSupportFragmentManager(), DIALOG_GENERALERROR);
                } //End if   
                mHelper.queryInventoryAsync(mGotInventoryListener);
            }
        });  

//************************************************* 

//Get App price
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory)   {
        if (result.isFailure()) {
            // handle error
            MyDialogFragment alertDialog_generalError = MyDialogFragment.newInstance(getString(R.string.dialog_alert),getString(R.string.error_general));
            alertDialog_generalError.show(getSupportFragmentManager(), DIALOG_GENERALERROR);
            return;
        }//End if
        String premiumPrice = inventory.getSkuDetails(SKU_PREMIUM).getPrice();
        btnPrice.setText(getResources().getString(R.string.btn_suscription) + " " + premiumPrice);
        myProfile.setPriceApp(premiumPrice);
    }
};

//  //************************************

@Override
public void inapp() {
    //myProfile.getGUID()
    //mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, myProfile.getGUID() );
    mHelper.launchSubscriptionPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, myProfile.getGUID() );
}

//************************************
@Override
public void getPrice(Button btnPrice) {
    Log.i(LOGTAG, "getPrice");
    this.btnPrice = btnPrice;
    List additionalSkuList = new ArrayList();
    additionalSkuList.add(SKU_PREMIUM);
    mHelper.queryInventoryAsync(true, additionalSkuList, mQueryFinishedListener);   
}

//************************************************

// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {


        // if we were disposed of in the meantime, quit.
        if (mHelper == null) return;

        if (result.isFailure()) {
            if (result.getResponse() != -1005){
                MyDialogFragment alertDialog_errorInApp = MyDialogFragment.newInstance(getString(R.string.dialog_alert),getString(R.string.error_inApp));
                alertDialog_errorInApp.show(getSupportFragmentManager(), DIALOG_GENERALERROR);  
            }
            return;
        }
        if (!verifyDeveloperPayload(purchase)) {
            MyDialogFragment alertDialog_errorInApp = MyDialogFragment.newInstance(getString(R.string.dialog_alert),getString(R.string.error_inApp));
            alertDialog_errorInApp.show(getSupportFragmentManager(), DIALOG_GENERALERROR);  
            return;
        }

        myProfile.setIsPremium(1);
        loginPrefsEditor.putInt("IsPremium", 1);    

    }
};

//**********************************

//User PREMIUM or NOT PREMIUM
// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        // Have we been disposed of in the meantime? If so, quit.
        Log.i(LOGTAG, result.getMessage());
        //if (mHelper == null) return;

        //Is it a failure?
        if (result.isFailure()) {
            MyDialogFragment alertDialog_generalError = MyDialogFragment.newInstance(getString(R.string.dialog_alert),getString(R.string.error_general));
            alertDialog_generalError.show(getSupportFragmentManager(), DIALOG_GENERALERROR);
            return;
        }

        /*
         * Check for items we own. Notice that for each purchase, we check
         * the developer payload to see if it's correct! See
         * verifyDeveloperPayload().
         */

        // Do we have the premium upgrade?
        Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
        mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
        if (mIsPremium == true){
            myProfile.setIsPremium(1);
            loginPrefsEditor.putInt("IsPremium", 1);
        }else{
            loginPrefsEditor.putInt("IsPremium", 0);
            myProfile.setIsPremium(0);
        }//End if-else
    }
};

//****************************************

/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
    String payload = p.getDeveloperPayload();
    if (!payload.equals(myProfile.getGUID())){
        return false;
    }else{
        return true;
    }//End if-else
}   

I get the base64EncodedPublicKey from console developer and I sign my project with Export Wizard.

3
Have you tried these things? ==> stackoverflow.com/a/9193392Robert Harvey
Yes, I follow these steps. But I can`t found the errorsuanido
Make sure the base64EncodedPublicKey is the same as the one in your developer console.Deqing

3 Answers

5
votes

This error can happen if you have ever gone through the purchase flow using one of the testing SKUs (android.test.purchased ...). These purchases are not real and so they are not signed but they are part of the result returned when you try to do anything with IAB like query the inventory. Unfortunately, IAB quits as soon as something in the result fails verification which these fake purchases will do.

There are two ways around this. First, make verification always pass in testing. The safest way to do this is to change verifyPurchase in IABUtils>Security to return true only on debug builds.

public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
    if (BuildConfig.DEBUG) {
        return true;
    }
    if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
            TextUtils.isEmpty(signature)) {
        Log.e(TAG, "Purchase verification failed: missing data.");
        return false;
    }

    PublicKey key = Security.generatePublicKey(base64PublicKey);
    return Security.verify(key, signedData, signature);
}

The second way around this is to not run verification on these fake purchases. Even better: only do this on a debug build too. One way to do this would be to change the signature of verifyPurchase to accept the SKU and use this as part of the check. You would then have to change IabHelper to make use of this extra parameter.

public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature, String sku) {
    if (BuildConfig.DEBUG) {
        if (skuMatchesTestingSku(sku)) {
            return true;
        }
    }

    ...
}

private static boolean skuMatchesTestingSku(String sku) {
    return sku.equals("android.test.purchased") ||
            sku.equals("android.test.canceled") ||
            sku.equals("android.test.refunded") ||
            sku.equals("android.test.item_unavailable");
}

It's very messy, but thanks to the way In App Billing is implemented, there's not much better we can do.

0
votes

In my case the base64EncodedPublicKey was wrong.

-2
votes

set return value to true In

public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
    return true;
}

after tesing undo the change