Android Studio 中 acknowledgePurchase 返回错误 5
结论
responseCode = 5 表示 Google Play Billing 认为当前确认请求无效。最常见的原因是商品类型与处理方式不一致。
- 一次性消耗型商品应调用
consumeAsync()。 - 非消耗型商品和订阅应调用
acknowledgePurchase()。 - 只有状态为
Purchase.PurchaseState.PURCHASED、尚未确认并且来自当前应用的有效购买,才能确认或消耗。
示例中出现了 "Consumed!",很可能是把消耗型商品传给了 acknowledgePurchase()。如果是这种情况,应改用 consumeAsync()。消耗成功后,该购买也会被视为已确认。
先查看具体错误信息
除了检查 responseCode,还应查看 BillingResult.getDebugMessage(),它通常能提供更明确的排查线索:
@Override
public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
int responseCode = billingResult.getResponseCode();
String debugMessage = billingResult.getDebugMessage();
Log.e(
"Billing",
"Acknowledge result: code=" + responseCode
+ ", message=" + debugMessage
);
if (responseCode == BillingClient.BillingResponseCode.OK) {
Toast.makeText(
MainActivity.this,
"Purchase acknowledged",
Toast.LENGTH_LONG
).show();
}
}
DEVELOPER_ERROR 的含义比较宽泛,只看数字 5 通常无法确定具体原因。还需要结合 debugMessage、商品类型和购买状态来判断。
非消耗型商品或订阅的正确处理
调用 acknowledgePurchase() 前,先确认购买已经完成,而且尚未确认:
private void acknowledgeIfRequired(Purchase purchase) {
if (purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) {
return;
}
if (purchase.isAcknowledged()) {
return;
}
AcknowledgePurchaseParams params =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(
params,
billingResult -> {
int responseCode = billingResult.getResponseCode();
Log.d(
"Billing",
"Acknowledge response: code=" + responseCode
+ ", message=" + billingResult.getDebugMessage()
);
if (responseCode == BillingClient.BillingResponseCode.OK) {
Toast.makeText(
MainActivity.this,
"Purchase acknowledged",
Toast.LENGTH_LONG
).show();
} else {
Log.e("Billing", "Failed to acknowledge purchase");
}
}
);
}
不要确认处于 PENDING 状态的购买。必须等交易状态变为 PURCHASED,才能交付商品并完成确认。
消耗型商品应使用 consumeAsync()
金币、次数和道具包等可以重复购买的商品通常属于消耗型商品,应调用 consumeAsync():
private void consumePurchase(Purchase purchase) {
if (purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) {
return;
}
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.consumeAsync(
consumeParams,
(billingResult, purchaseToken) -> {
int responseCode = billingResult.getResponseCode();
Log.d(
"Billing",
"Consume response: code=" + responseCode
+ ", message=" + billingResult.getDebugMessage()
);
if (responseCode == BillingClient.BillingResponseCode.OK) {
Toast.makeText(
MainActivity.this,
"Purchase consumed",
Toast.LENGTH_LONG
).show();
} else {
Log.e("Billing", "Failed to consume purchase");
}
}
);
}
不要对同一个购买令牌同时调用 acknowledgePurchase() 和 consumeAsync()。具体使用哪个接口,取决于商品的业务类型。
仍然返回错误 5 时的排查顺序
1. 确认传入的是购买令牌
setPurchaseToken() 接收的是 purchase.getPurchaseToken(),不能传入商品 ID、订单号或其他字符串。
同时要确认这个 Purchase 对象来自当前有效的购买回调或 queryPurchasesAsync() 查询结果,而不是缓存中的旧对象。
2. 检查购买状态
只处理已经完成的购买:
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
// acknowledge or consume
}
如果状态是 PENDING,需要等待 Google Play 后续通知购买完成。
3. 避免重复处理同一购买
确认前先检查:
if (!purchase.isAcknowledged()) {
// acknowledgePurchase()
}
对于消耗型商品,也不要针对同一个 token 并发或重复调用 consumeAsync()。可以在应用或服务端记录购买令牌的处理状态,并保证交付逻辑具有幂等性。
4. 确认商品类型和业务定义一致
不同商品应采用不同的处理方式:
- 消耗型一次性商品调用
consumeAsync()。 - 非消耗型一次性商品调用
acknowledgePurchase()。 - 订阅在购买完成且尚未确认时调用
acknowledgePurchase()。
Google Play Console 中的一次性商品不会仅靠控制台配置自动具有“消耗型”或“非消耗型”的业务含义,具体类型取决于应用如何履约和处理购买令牌。
5. 检查应用与测试环境
逐项确认以下信息:
- 当前安装包的
applicationId与 Google Play Console 中的应用包名一致。 - 商品属于当前应用。
- 测试账号具有购买权限,并通过 Google Play 安装了正确的测试版本。
- 当前购买令牌确实由这个应用和账号产生。
- 调用确认或消耗接口前,
BillingClient已成功连接。
如果应用是本地直接安装的,或者使用了错误的签名、包名或其他应用产生的购买数据,Google Play 可能无法正确识别请求。
6. 区分“确认”和“消耗”
acknowledgePurchase() 只表示应用已经识别并处理了这笔购买,不会让商品恢复为可购买状态。
consumeAsync() 会消耗对应的一次性商品,使其可以再次购买,同时满足这笔购买的确认要求。成功提示也应分别使用 "Purchase acknowledged" 和 "Purchase consumed",以免在调试时混淆两种操作。
推荐的处理结构
在购买回调中先检查状态,再根据商品类型选择处理方式:
private void processPurchase(Purchase purchase, boolean consumable) {
if (purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) {
return;
}
// 实际项目中应先验证购买并安全地交付权益。
if (consumable) {
consumePurchase(purchase);
} else if (!purchase.isAcknowledged()) {
acknowledgeIfRequired(purchase);
}
}
正式应用最好在可信服务端验证购买令牌、记录履约结果并完成确认。无论由客户端还是服务端处理,都要保证同一笔购买只交付一次,并在 Google Play 规定的期限内完成确认,否则购买可能被自动退款。
备注:内容仅供参考。
