应用内结算,签名验证失败

时间:2013-09-09 17:37:14

标签: android in-app-purchase in-app-billing signature

当我尝试获取订阅时,我遇到以下错误:

签名验证失败。 签名与数据不匹配。 应用内结算警告:购买签名验证 FAILED 。不添加项目。

我的代码是:

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
}   

我从控制台开发人员那里获得了base64EncodedPublicKey,并使用导出向导签署了我的项目。

3 个答案:

答案 0 :(得分:5)

如果您使用其中一个测试SKU(android.test.purchased ...)完成了购买流程,则会发生此错误。这些购买不是真实的,因此它们没有签名,但是当您尝试使用IAB执行任何操作(如查询库存)时,它们是返回结果的一部分。不幸的是,一旦结果中的某些内容无法验证这些虚假购买会做什么,IAB就会退出。

有两种解决方法。首先,使验证始终通过测试。最安全的方法是更改​​verifyPurchase中的IABUtils>Security以仅在调试版本上返回true。

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

第二种方法是不对这些假购买进行验证。更好的是:只在调试版本上执行此操作。一种方法是更改​​verifyPurchase的签名以接受SKU并将其用作检查的一部分。然后,您必须更改IabHelper才能使用此额外参数。

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");
}

这非常混乱,但多亏了应用程序结算的实施方式,我们无法做得更好。

答案 1 :(得分:0)

在我的情况下,base64EncodedPublicKey是错误的。

答案 2 :(得分:-2)

将返回值设置为true在

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

测试后撤消更改