我的一些用户抱怨他们购买的应用内“高级版”无法恢复。
我联系了其中一个用户,然后我发送了一个包含其他消息的APK。这就是:
Unable to buy item (response: 7: Item already owned)
。这是预期的消息,因为他已经买了它。这是购买的回调:
@Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (result.isFailure()) {
Log.d("debug", "failed - " + result.mMessage);
return;
}
Log.d("debug", "success");
// continue with the purchase validation...
}
这是恢复购买的回调:
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
Log.d("debug", "inventory: failed (" + result.mMessage + ")");
return;
}
if (inventory.hasPurchase(SKU_PREMIUM)) {
Log.d("debug", "success - purchase restored");
}
else {
Log.d("debug", "failure - no purchase found for this user");
}
}
请注意,这种情况只发生在少数用户身上,我经过多次测试,在测试后,我在查询广告资源后收到success - purchase restored
消息。
为了清楚起见,我正在使用v3 API,这个SKU是一个托管项目。我需要检查用户是否已经购买了它(我不想消费它)。
答案 0 :(得分:1)
不幸的是,这是我的错误。
受影响的用户在非常旧的版本上购买了应用内商品,该版本具有相同SKU的不同的开发者有效负载,具体取决于购买的应用位置。
这些不同的开发者有效负载已从最新版本中删除,但它显然打破了以前的购买。
答案 1 :(得分:0)
如果您使用的是应用内购买的v3版本,则版本3 API支持托管的应用内商品和订阅。
托管应用内商品是Google Play跟踪和管理所有权信息的商品。当用户购买托管的应用内商品时,Google Play会按用户存储每个商品的购买信息。这使您可以在以后随时查询Google Play以恢复特定用户购买的商品的状态。即使用户卸载了应用程序或更改了设备,此信息也会在Google Play服务器上保持不变。
如果您使用的是版本3 API,则还可以在应用程序中使用托管项。您通常会对可以多次购买的物品(例如游戏中的货币,燃料或魔法咒语)实施消费。购买后,在您使用该商品之前,通过向Google Play发送消费请求,无法再次购买被管理商品。
要使用项目,请参阅以下方法:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
//consumeItem();
Constant.showProgressDialog(getActivity());
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
同时将此方法添加到您的活动中
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
// We know this is the "gas" sku because it's the only one we consume,
// so we don't check which sku was consumed. If you have more than one
// sku, you probably should check...
if (result.isSuccess()) {
// successfully consumed, so we apply the effects of the item in our
// game world's logic, which in our case means filling the gas tank a bit
Log.d(TAG, "Consumption successful. Provisioning.");
}
else {
complain("Error while consuming: " + result);
}
Log.d(TAG, "End consumption flow.");
}
};
在你的IabHelper.java类中有以下方法:
int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException {
// Query purchases
logDebug("Querying owned items, item type: " + itemType);
logDebug("Package name: " + mContext.getPackageName());
boolean verificationFailed = false;
String continueToken = null;
do {
logDebug("Calling getPurchases with continuation token: " + continueToken);
Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(),
itemType, continueToken);
int response = getResponseCodeFromBundle(ownedItems);
logDebug("Owned items response: " + String.valueOf(response));
if (response != BILLING_RESPONSE_RESULT_OK) {
logDebug("getPurchases() failed: " + getResponseDesc(response));
return response;
}
if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST)
|| !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST)
|| !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) {
logError("Bundle returned from getPurchases() doesn't contain required fields.");
return IABHELPER_BAD_RESPONSE;
}
ArrayList<String> ownedSkus = ownedItems.getStringArrayList(
RESPONSE_INAPP_ITEM_LIST);
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList(
RESPONSE_INAPP_PURCHASE_DATA_LIST);
ArrayList<String> signatureList = ownedItems.getStringArrayList(
RESPONSE_INAPP_SIGNATURE_LIST);
for (int i = 0; i < purchaseDataList.size(); ++i) {
String purchaseData = purchaseDataList.get(i);
String signature = signatureList.get(i);
String sku = ownedSkus.get(i);
if (Security.verifyPurchase(mSignatureBase64, purchaseData, signature)) {
logDebug("Sku is owned: " + sku);
Purchase purchase = new Purchase(itemType, purchaseData, signature);
if (TextUtils.isEmpty(purchase.getToken())) {
logWarn("BUG: empty/null token!");
logDebug("Purchase data: " + purchaseData);
}
// Record ownership and token
inv.addPurchase(purchase);
}
else {
logWarn("Purchase signature verification **FAILED**. Not adding item.");
logDebug(" Purchase data: " + purchaseData);
logDebug(" Signature: " + signature);
verificationFailed = true;
}
}
continueToken = ownedItems.getString(INAPP_CONTINUATION_TOKEN);
logDebug("Continuation token: " + continueToken);
} while (!TextUtils.isEmpty(continueToken));
return verificationFailed ? IABHELPER_VERIFICATION_FAILED : BILLING_RESPONSE_RESULT_OK;
}
在此处您将获得拥有物品清单