我已经尝试了好几天来解决这个问题,使用SDK附带的地牢演示代码。 我试图谷歌的答案,但无法找到一个。
- 在地牢演示中,我通过从开发者控制台我的公钥。
- 签名的APK并上传到控制台而不发布。
- 测试两个
android.test.purchased
在控制台上创建与发布的订阅和产品列表(主要特点我要为我的应用程序)。
但我仍然得到一个错误Signature verification failed
后的签名不匹配的数据。 我该如何解决这个问题?
public static ArrayList<VerifiedPurchase> verifyPurchase(String signedData, String 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)) {
String 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, String signedData, String 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;
}
Answer 1:
这个问题仍然是当前谷歌的计费版本回事。 基本上android.test.purchased坏; 你买android.test.purchased后在Security.java的verifyPurchase函数总是失败,QueryInventoryFinishedListener将在该行停止,如果(result.isFailure()); 这是因为android.test.purchased项目总是失败在Security.java的TextUtils.isEmpty(签名)检查,因为它不是一个真正的项目,并没有通过服务器返回的签名。
我的建议(由于缺乏任何其他解决方案)是永远不会使用“android.test.purchased”。 有在网络上的各种代码的调整,但他们没有工作100%。
如果你已经使用了android.test.purchased再一个办法摆脱错误的是要做到以下几点: -
- 编辑Security.java并修改“返回false”线在verifyPurchase为“返回true” - 这是暂时的,我们会装回去一分钟。
在你QueryInventoryFinishedListener,经过“如果(result.isFailure()){...}”行添加以下消耗和摆脱你的永不落幕的android.test.purchased项目:
if (inventory.hasPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD)) { mHelper.consumeAsync(inventory.getPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD),null); }
运行您的应用程序,以便在发生consunmeAsync,这摆脱了服务器上的“android.test.purchased”项目。
- 卸下consumeAsync代码(或将其注释掉)。
- 早在Security.java,改变“返回true”回“返回false”。
您QueryInventoryFinishedListener将上不再错误的验证,一切都恢复到“正常”(如果你可以称呼它)。 请记住 - 不要打扰再次使用android.test.purchased,因为它只会再次引起这个错误......这是爆发了! 测试的唯一途径,你购买它来上传APK,等待它出现,然后你的设备上测试它(相同的APK)与启用日志记录。
Answer 2:
是的,问题仍然存在。 之后我买android.test.purchased我开始越来越对quering清单中的错误。 有可能通过清除只是谷歌的数据Play商店应用程序和运行谷歌播放一个时间来修复您的手机。 当你清楚谷歌的Play的数据,它忘记了,你买android.test.purchased
Answer 3:
请检查base64EncodedPublicKey
,并从Play开发者控制台是相等的一个。 一旦你在开发者控制台重新上传APK,公钥可能会改变,如果是更新base64EncodedPublicKey
。
Answer 4:
你可以跳过那些“android.test。*”的产品ID的验证过程。 如果您正在使用从TrivialDrive示例中的示例代码,开放IabHelper.java,找到下面这行代码,它改变
if (Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }
成
boolean verifySignature = !sku.startsWith("android.test."); // or inplace the condition in the following line
if (verifySignature && !Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) { ... }
这是无害的,即使你忘了回滚代码。 所以,你可以继续测试其它工作流程的步骤。
Answer 5:
基于GMTDev的答案,这是我为了消费产品以最简单的方式 ,当固定测试问题做。 在Security.java,代替与此verifyPurchase()方法:
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);
}
我只修改了一条线(见注释),这样一来你可以保持这样的代码,对调试和仍然发布发行版本安全。
Answer 6:
该错误是由于错误的许可证造成的。 也许许可证密钥可能是从其他应用程序。
解决的办法是使用正确的许可证密钥来自:
游戏控制台>应用程序>开发工具>许可和应用内结算
Answer 7:
该解决方案为我工作。 我与旧购类换了新verifyPurchase方法。
Answer 8:
什么工作对我来说,在使用应用内结算v3和附带的实用工具类,是消费返回的onActivityResult调用内测试购买。
都需要IabHelper,安全,或任何应用内结算UTIL类的没有变化,以避免这为未来的测试购买。
如果你已经尝试过购买测试产品,现在被困在购买签名验证失败的错误,你有可能是因为你是这个错误查找答案,那么你应该:
- 作出这样的GMTDev推荐的变化
- 运行应用程序,以确保它消耗的产品
- 删除/撤消GMTDev的变化
- 实施以下内onActivityResult的代码。
这不仅让购买测试过程是流体,但是这也应该避免任何冲突的问题与IAB试图回购测试产品时,返回“ 项目已经拥有 ”的错误。
如果这是从片段中调用和你的片段的onActivityResult不会被调用,那么一定在必要时从父ActivityFragment调用YourFragmentName.onActivityResult(requestCode,resultCode为,数据)。 此进行更详细的说明中调用从片段startIntentSenderForResult(Android电子帐单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);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
// Strip out getActivity() if not being used within a fragment
if (resultCode == getActivity().RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("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();
}
}
}
}
如果它的SKU是“android.test.purchased”,所以它应该是安全的使用它只会删除购买。
Answer 9:
签名验证只为默认的测试产品的失败。 快速修复:
- 转到IabHelper类。
- 反转的条件,如果
Security.verifyPurchase
。
而已!
记住恢复的变更时,测试产品由实际产品所取代
Answer 10:
遇到同样的问题(签名验证,并摆脱了测试购买的)今日(2018年10月30日)。
签名的问题可能是由事实是,这些测试的SKU是不是真的你的应用程序的一部分,因此没有你的应用程序的签名造成的。 我没有打开与谷歌一票,但不知道他们是否可以解决这个问题。 解决方法,因为别人指出的那样,是替代代码
if (verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
同
if (verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature()) ||
(purchase.getSku().startsWith("android.test.")) ) {
关于“如何摆脱购买android.test.purchased SKU的”,我发现该设备的简单的重新启动,然后等待一分钟左右,和/或重新启动您的应用程序几次固定它对我来说(即我没有到“消费”通过代码购买者除外)。 我猜测,需要等待,以便在Play商店完成了与谷歌的服务器同步。 (不知道这是否会继续这种工作方式在未来,但如果它现在为你工作,这可能会帮助你前进。)
Answer 11:
检查这个答案 :
是主帐户在测试设备上为您的谷歌Play开发者帐户一样吗?
如果没有,你不会得到在android.test签名。*静态回复,除非应用程序之前已经公布的游戏。
见桌上http://developer.android.com/guide/market/billing/billing_testing.html#static-responses-table了全套的条件。
而且它的评论:
我不认为静态ID返回签名了。 见https://groups.google.com/d/topic/android-developers/PCbCJdOl480/discussion
此外,以前的示例代码从谷歌(许多大型应用中使用)播放结算图书馆允许一个空的签名。 这就是为什么它是静态的采购在那里工作。
但它是一个安全漏洞,所以当它被发布 ,谷歌提交了更新 。
Answer 12:
我有同样的问题,并按照@Deadolus说,根据https://www.gaffga.de/implementing-in-app-billing-for-android/
关键的一点是我们需要做的SKU消耗品甚至库存查询结果失败了。 下面是我怎么做的样品。
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().
*/
}
};
在上面的代码与你的应用的应用包名称替换PACKAGE_NAME。
Answer 13:
这是对我工作:
- 呼叫BillingClient.querySkuDetailsAsync来查询项(如果可用)
- 等待SkuDetailsResponseListener.onSkuDetailsResponse
- 等待另一500ms的
- 使用BillingClient.launchBillingFlow开始购买...
因为当我收到onSkuDetailsResponse它应该是确定的,但它没有,不得不等待一点点的第3步不应该是必要的。 该购买作品后,没有更多的“项目不可用错误”。 这是我测试过它:
- 清除我的应用程序数据
- 明确谷歌Play资料
- 运行应用程序
- 购买android.test.purchased
- 尽量购买我的产品(它失败的项目不适用)
- 用我的解决方案上面,它的工作原理
Answer 14:
对于科尔多瓦和混合的应用程序,你需要使用this.iap.subscribe(this.productId)方法来订阅InAppPurchase。
以下是代码的工作对我罚款:
getProdutIAP() {
this.navCtrl.push('subscribeDialogPage');
this.iap
.getProducts(['productID1']).then((products: any) => {
this.buy(products);
})
.catch((err) => {
console.log(JSON.stringify(err));
alert('Finished Purchase' + JSON.stringify(err));
console.log(err);
});
}
buy(products: any) {
// this.getProdutIAP();
// alert(products[0].productId);
this.iap.subscribe(products[0].productId).then((buydata: any) => {
alert('buy Purchase' + JSON.stringify(buydata));
// this.sub();
}).catch((err) => {
// this.navCtrl.push('subscribeDialogPage');
alert('buyError' + JSON.stringify(err));
});
}
sub() {
this.platform.ready().then(() => {
this.iap
.subscribe(this.productId)
.then((data) => {
console.log('subscribe Purchase' + JSON.stringify(data));
alert('subscribe Purchase' + JSON.stringify(data));
this.getReceipt();
}).catch((err) => {
this.getReceipt();
alert('subscribeError' + JSON.stringify(err));
console.log(err);
});
})
}
文章来源: Android in app purchase: Signature verification failed