Android Billing:ConsumeParams消费失败时如何取消购买?
问题解答:消费失败时的购买状态处理
首先明确:已完成的购买(PURCHASED状态且已确认)无法直接取消,但你遇到的「多次购买商品变成无法再次购买」的问题,本质是消费操作未成功导致Google Play仍判定用户拥有该商品,而非真的变成了一次性购买。可以通过以下方案解决:
核心修复思路
1. 消费失败时添加重试机制
网络不稳定是消费失败的主要原因,不要单次失败就放弃,通过指数退避重试提升成功率。比如第一次失败后等2秒,第二次等4秒,最多重试3-5次。
2. 持久化未消费的购买记录
如果用户在消费过程中关闭应用,下次启动时必须主动查询未消费的购买记录,重新发起消费。可以将未消费的purchaseToken存在SharedPreferences或本地数据库中。
3. 完善消费回调的失败处理逻辑
在消费失败时,不要静默忽略,要记录状态并提示用户,同时触发重试流程。
代码修改示例
修改CHECK_PURCHASE_TYPE方法,添加重试逻辑
private void CHECK_PURCHASE_TYPE(Purchase purchase) { String sku = purchase.getProducts().get(0); if (sku.equals("sku0")) { consumePurchaseWithRetry(purchase, 0); } else if (sku.equals("sku1") || sku.equals("sku2")) { SAVE_PREF(purchase); } } // 带重试的消费方法 private void consumePurchaseWithRetry(Purchase purchase, int retryCount) { int maxRetries = 3; if (retryCount >= maxRetries) { // 重试次数耗尽,记录未消费的token,等待下次启动处理 saveUnconsumedPurchaseToken(purchase.getPurchaseToken()); // 提示用户购买已完成但未生效,稍后会自动处理 new Handler(Looper.getMainLooper()).post(() -> binding.PurchaseTxtMessage.setText(R.string.DPurchase_Consume_Failed_Retry) ); return; } ConsumeParams consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); billingClient.consumeAsync(consumeParams, (billingResult, s) -> { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { SAVE_PREF(purchase); // 消费成功,移除未消费记录(如果有的话) removeUnconsumedPurchaseToken(purchase.getPurchaseToken()); } else { // 计算重试延迟,指数退避 long delayMillis = (long) Math.pow(2, retryCount) * 1000; new Handler(Looper.getMainLooper()).postDelayed(() -> consumePurchaseWithRetry(purchase, retryCount + 1), delayMillis ); } }); } // 持久化未消费的token private void saveUnconsumedPurchaseToken(String token) { SharedPreferences prefs = getSharedPreferences("BillingPrefs", MODE_PRIVATE); Set<String> unconsumedTokens = prefs.getStringSet("unconsumed_tokens", new HashSet<>()); unconsumedTokens.add(token); prefs.edit().putStringSet("unconsumed_tokens", unconsumedTokens).apply(); } // 移除已消费的token private void removeUnconsumedPurchaseToken(String token) { SharedPreferences prefs = getSharedPreferences("BillingPrefs", MODE_PRIVATE); Set<String> unconsumedTokens = prefs.getStringSet("unconsumed_tokens", new HashSet<>()); unconsumedTokens.remove(token); prefs.edit().putStringSet("unconsumed_tokens", unconsumedTokens).apply(); }
应用启动时检查未消费记录
在你的BillingClient初始化完成后,添加查询未消费购买的逻辑:
// 查询所有已购买未消费的商品 private void queryUnconsumedPurchases() { QueryPurchasesParams params = QueryPurchasesParams.newBuilder() .setProductType(BillingClient.ProductType.INAPP) .build(); billingClient.queryPurchasesAsync(params, (billingResult, purchases) -> { if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { for (Purchase purchase : purchases) { // 检查是否是多次购买的sku0,且未被消费 if (purchase.getProducts().get(0).equals("sku0") && !purchase.isConsumed()) { consumePurchaseWithRetry(purchase, 0); } } } }); }
额外注意事项
- 确保
verifyValidSignature逻辑正确,不要误判合法的购买记录。 - 对于PENDING状态的购买,不要直接标记为取消,应该后续通过查询接口确认最终状态,避免漏处理。
- 一次性购买的商品(sku1、sku2)不需要消费,只需要确认即可,你的现有逻辑是正确的。
内容的提问来源于stack exchange,提问作者Espada
相关产品推荐
相关产品推荐

