PAYATHON 2026

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 规定的期限内完成确认,否则购买可能被自动退款。

备注:内容仅供参考。