PAYATHON 2026

Google Play Billing Library 5 一次性购买如何传递 token?

数据小许

结论

一次性购买(ProductType.INAPP)不需要 offerToken。构造 BillingFlowParams.ProductDetailsParams 时,只需设置 ProductDetails,不要调用 setOfferToken(),也不用传入 null 或空字符串。

BillingFlowParams.ProductDetailsParams productDetailsParams =
        BillingFlowParams.ProductDetailsParams.newBuilder()
                .setProductDetails(productDetails)
                .build();

offerToken 用于指定订阅商品的基础方案或优惠,不适用于 Google Play Billing Library 5 中的一次性商品。

启动一次性购买流程

查询商品时使用 ProductType.INAPP。取得对应的 ProductDetails 后,可以直接启动购买流程:

ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
        ImmutableList.of(
                BillingFlowParams.ProductDetailsParams.newBuilder()
                        .setProductDetails(productDetails)
                        .build()
        );

BillingFlowParams billingFlowParams =
        BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(productDetailsParamsList)
                .build();

BillingResult billingResult =
        billingClient.launchBillingFlow(activity, billingFlowParams);

同时检查同步返回的 BillingResult:

if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK) {
    Log.e(
            "Billing",
            "Unable to launch billing flow: "
                    + billingResult.getDebugMessage()
    );
}

不要为了照搬示例代码而创建 selectedOfferToken。文档中的 setOfferToken(selectedOfferToken) 是订阅购买的示例逻辑,并非所有商品类型都需要执行。

offerToken 和 purchaseToken 不是同一个值

这两种 token 容易混淆:

  • offerToken:发起订阅购买前,通过 ProductDetails.SubscriptionOfferDetails#getOfferToken() 获取,用来指定订阅基础方案或优惠。
  • purchaseToken:购买成功后由 Google Play 返回,用于确认购买、消耗商品和服务端校验。

一次性购买不需要提前传入 offerToken,但购买成功后仍会产生 purchaseToken。可以从 Purchase 对象中获取:

PurchasesUpdatedListener purchasesUpdatedListener =
        (billingResult, purchases) -> {
            if (billingResult.getResponseCode()
                    == BillingClient.BillingResponseCode.OK
                    && purchases != null) {

                for (Purchase purchase : purchases) {
                    String purchaseToken = purchase.getPurchaseToken();
                    handlePurchase(purchase, purchaseToken);
                }
            }
        };

Billing Library 使用 Purchase 表示购买结果,使用 ProductDetails 表示待售商品信息。如果 “PurchaseDetails token” 指购买成功后的凭证,实际应调用:

purchase.getPurchaseToken()

不要从 ProductDetails 中查找。

购买成功后的处理

非消耗型一次性商品应在验证购买状态后确认购买:

private void handlePurchase(Purchase purchase, String purchaseToken) {
    if (purchase.getPurchaseState() != Purchase.PurchaseState.PURCHASED) {
        return;
    }

    if (!purchase.isAcknowledged()) {
        AcknowledgePurchaseParams acknowledgeParams =
                AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchaseToken)
                        .build();

        billingClient.acknowledgePurchase(
                acknowledgeParams,
                billingResult -> {
                    if (billingResult.getResponseCode()
                            != BillingClient.BillingResponseCode.OK) {
                        Log.e(
                                "Billing",
                                "Acknowledge failed: "
                                        + billingResult.getDebugMessage()
                        );
                    }
                }
        );
    }
}

如果商品是可重复购买的消耗品,应使用 consumeAsync() 消耗该购买,不能只执行确认:

ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();

billingClient.consumeAsync(
        consumeParams,
        (billingResult, consumedPurchaseToken) -> {
            if (billingResult.getResponseCode()
                    != BillingClient.BillingResponseCode.OK) {
                Log.e(
                        "Billing",
                        "Consume failed: "
                                + billingResult.getDebugMessage()
                );
            }
        }
);

注意事项

不要混淆下面几种值:

  • ProductDetails#getProductId() 是商品 ID。
  • SubscriptionOfferDetails#getOfferToken() 是订阅优惠 token。
  • Purchase#getPurchaseToken() 是购买完成后生成的购买凭证。

一次性购买的流程是:查询 INAPP 类型的 ProductDetails,构造购买参数时省略 setOfferToken(),购买成功后再从 Purchase 获取 purchaseToken。生产环境中,应先验证购买真实性,再发放商品并确认或消耗该购买。

备注:内容仅供参考。