提醒:从 2021 年 8 月 2 日起,所有新应用都必须使用结算库版本 3 或更新版本。截至 2021 年 11 月 1 日,对现有应用的所有更新都必须使用结算库版本 3 或更新版本。了解详情

将 Google Play 结算库集成到您的应用中

本主题介绍了如何将 Google Play 结算库集成到您的应用中以开始销售商品。在阅读本主题之前,请确保您已按照做好准备中的步骤提前设置了 Google Play 配置。

本主题包含一些代码示例,它们基于 GitHub 上的官方示例应用。如需查看您在集成时可使用的示例应用和其他资源的完整列表,请参阅其他资源

购买交易的生命周期

下面是一次性购买或订阅的典型购买流程。

  • 向用户展示他们可以购买什么。
  • 启动购买流程,以便用户接受购买交易。
  • 在您的服务器上验证购买交易。
  • 向用户提供内容,并确认内容已传送给用户。还可以选择性地将商品标记为已消费,以便用户可以再次购买商品。

订阅会自动续订,直到被取消。订阅可处于下面这几种状态:

  • 活跃:用户信誉良好,可享用订阅内容。
  • 已取消:用户已取消订阅,但在到期前仍可享用订阅内容。
  • 处于宽限期:用户遇到了付款问题,但仍可享用订阅内容,同时 Google 会重新尝试通过相应的付款方式扣款。
  • 暂时保留:用户遇到了付款问题,不能再享用订阅内容,同时 Google 会重新尝试通过相应的付款方式扣款。
  • 已暂停:用户暂停了其订阅,在恢复之前不能享用订阅内容。
  • 已到期:用户已取消且不能再享用订阅内容。用户在订阅到期时会被视为流失。

购买令牌和订单 ID

Google Play 使用购买令牌和订单 ID 跟踪商品和交易。

  • 购买令牌是一个字符串,表示买家对 Google Play 上的商品的权利。它表明 Google 用户有权使用由 SKU 表示的特定商品。您可以将购买令牌与 Google Play Developer API 一起使用。
  • 订单 ID 是一个字符串,表示 Google Play 上的财务交易。此字符串会包含在通过电子邮件发送给买家的收据中。您可以在销售和付款报告中使用订单 ID 来管理退款。

每当发生财务交易时,系统都会创建订单 ID。只有当用户完成购买流程时,系统才会生成购买令牌。

  • 对于一次性商品,每次购买交易都会创建一个新的购买令牌。大多数购买交易还会生成一个新的订单 ID。这条规则有一种例外情况,那就是不向用户收取任何费用时,如促销代码中所述。
  • 对于订阅,首次购买交易会创建一个购买令牌和一个订单 ID。对于每个连续的结算周期,购买令牌将保持不变,但系统会发放一个新的订单 ID。升级、降级、替换和重新注册都会创建新的购买令牌和订单 ID。

对于订阅,请注意以下几点:

  • 订阅升级、降级和其他订阅购买流程会生成购买令牌,这些令牌必须替换之前的购买令牌。您必须使出现在 Google Play Developer API 的 linkedPurchaseToken 字段中的购买令牌无效。如需了解详情,请参阅正确实现 linkedPurchaseToken 以防止重复订阅
  • 订阅续订的订单号包含一个额外的整数,它表示具体是第几次续订。例如,初始订阅的订单 ID 可能是 GPA.1234-5678-9012-34567,后续订单 ID 是 GPA.1234-5678-9012-34567..0(第一次续订)、GPA.1234-5678-9012-34567..1(第二次续订),依此类推。

错误处理

Google Play 结算库会以 BillingResult 的形式返回错误。BillingResult 包含一个 BillingResponseCode,它用于对您的应用可能会遇到的与结算相关的错误进行分类。例如,如果您收到 SERVICE_DISCONNECTED 错误代码,则您的应用应重新初始化与 Google Play 的连接。此外,BillingResult 还包含一条调试消息,它对于在开发过程中诊断错误很有用。

连接到 Google Play

与 Google Play 结算系统集成的第一步是将该库添加到您的应用中并初始化连接。

添加 Google Play 结算库依赖项

将 Google Play 结算库依赖项添加到应用的 build.gradle 文件中,如下所示:

dependencies {
    def billing_version = "3.0.0"

    implementation "com.android.billingclient:billing:$billing_version"
}

如果您使用的是 Kotlin,Play 结算库 KTX 模块包含了 Kotlin 扩展程序和协程支持,可让您在使用 Google Play 结算库时编写惯用的 Kotlin 代码。如需将这些扩展程序包含在项目中,请将以下依赖项添加到应用的 build.gradle 文件中,如下所示:

dependencies {
    def billing_version = "3.0.0"

    implementation "com.android.billingclient:billing-ktx:$billing_version"
}

初始化 BillingClient

添加对 Google Play 结算库的依赖关系后,您需要初始化 BillingClient 实例。BillingClient 是 Google Play 结算库与应用的其余部分之间进行通信的主接口。BillingClient 为许多常见的结算操作提供了方便的方法,既有同步方法,又有异步方法。

如需创建 BillingClient,请使用 newBuilder()。为了接收有关购买交易的更新,您还必须调用 setListener(),并传递对 PurchasesUpdatedListener 的引用。此监听器可接收应用中所有购买交易的更新。

Kotlin

private val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(activity)
   .setListener(purchasesUpdatedListener)
   .enablePendingPurchases()
   .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .build();

与 Google Play 建立连接

创建 BillingClient 后,您需要与 Google Play 建立连接。

如需连接到 Google Play,请调用 startConnection()。连接过程是异步进行的,因此您必须实现 BillingClientStateListener,以便在客户端的设置完成后且它准备好发出进一步的请求时接收回调。

此外,您还必须实现重试逻辑,以处理与 Google Play 失去连接的问题。如需实现重试逻辑,请替换 onBillingServiceDisconnected() 回调方法,并确保 BillingClient 先调用 startConnection() 方法以重新连接到 Google Play,然后再发出进一步的请求。

以下示例演示了如何启动连接并测试它是否已准备就绪可供使用:

Kotlin

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

展示可供购买的商品

与 Google Play 建立连接后,您就可以查询可售的商品并将其展示给用户了。如需向 Google Play 查询应用内商品详情,请调用 querySkuDetailsAsync()。在将商品展示给用户之前,查询 SKU 详情是非常重要的一步,因为查询会返回本地化的商品信息。对于订阅,请确保您的商品展示符合所有 Play 政策

调用 querySkuDetailsAsync() 时,应传递 SkuDetailsParams 的实例,用于指定在 Google Play 管理中心创建的商品 ID 字符串的列表以及 SkuTypeSkuType 可以是 SkuType.INAPP(针对一次性商品),也可以是 SkuType.SUBS(针对订阅)。

为了处理该异步操作的结果,您还必须指定实现 SkuDetailsResponseListener 接口的监听器。然后,您可以替换 onSkuDetailsResponse(),该方法会在查询完成时通知监听器,如以下示例所示:

Kotlin

fun querySkuDetails() {
    val skuList = ArrayList<String>()
    skuList.add("premium_upgrade")
    skuList.add("gas")
    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(SkuType.INAPP)
    val skuDetailsResult = withContext(Dispatchers.IO) {
        billingClient.querySkuDetails(params.build())
    }
    // Process the result.
}

Java

List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                List<SkuDetails> skuDetailsList) {
            // Process the result.
        }
    });

Google Play 结算库会将查询结果存储在 SkuDetails 对象的 List 中。您随后可以对该列表中的每个 SkuDetails 对象调用各种方法,以查看应用内商品的相关信息,如其价格或说明。如需查看可用的商品详情,请参阅 SkuDetails 类中的方法列表。

在提供待售商品之前,检查用户是否尚未拥有该商品。如果用户的消耗型商品仍在他们的商品库中,他们必须先消耗掉该商品,然后才能再次购买。

在提供订阅之前,验证用户是否尚未订阅。

启动购买流程

如需从应用发起购买请求,请从应用的主线程调用 launchBillingFlow() 方法。此方法接受对 BillingFlowParams 对象的引用,该对象包含通过调用 querySkuDetailsAsync() 获取的相关 SkuDetails 对象。如需创建 BillingFlowParams 对象,请使用 BillingFlowParams.Builder 类。

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...;

// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
val flowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build();
int responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode();

// Handle the result.

launchBillingFlow() 方法会返回 BillingClient.BillingResponseCode 中列出的几个响应代码之一。请务必检查此结果,以确保在启动购买流程时没有错误。BillingResponseCodeOK 表示成功启动。

成功调用 launchBillingFlow() 时,系统会显示 Google Play 购买屏幕。图 1 显示了一次性商品的购买屏幕:

Google Play 购买屏幕显示了可供购买的一次性商品
图 1. Google Play 购买屏幕显示了可供购买的一次性商品。

Google Play 会调用 onPurchasesUpdated(),以将购买操作的结果传送给实现 PurchasesUpdatedListener 接口的监听器。您可以在初始化客户端时使用 setListener() 方法指定监听器。

您必须实现 onPurchasesUpdated() 来处理可能的响应代码。以下示例展示了如何替换 onPurchasesUpdated()

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

如果成功购买商品,系统会显示 Google Play 购买成功屏幕,类似于图 2。

Google Play 的购买成功屏幕
图 2. Google Play 的购买成功屏幕。

如果成功购买商品,系统还会生成购买令牌,它是一个唯一标识符,表示用户及其所购应用内商品的商品 ID。您的应用可以在本地存储购买令牌,不过我们建议您将令牌传递到安全的后端服务器,您随后可以在该服务器上验证购买交易及防范欺诈行为。下一部分对此过程进行了详细说明。

用户还会收到包含交易收据的电子邮件,其中包含订单 ID 或交易的唯一 ID。用户每次购买一次性商品时,都会收到包含唯一订单 ID 的电子邮件。此外,用户最初购买订阅时以及后续定期自动续订时,也会收到这样的电子邮件。您可以在 Google Play 管理中心内使用订单 ID 来管理退款。

处理购买交易

用户完成购买交易后,您的应用需要处理该购买交易。在大多数情况下,您的应用会通过 PurchasesUpdatedListener 收到购买交易的通知。但在某些情况下,您的应用通过调用 BillingClient.queryPurchases() 得知购买交易,如提取购买交易中所述。

您的应用应按以下方式处理购买交易:

  1. 验证购买交易。
  2. 向用户提供内容,并确认内容已传送给用户。还可以选择性地将商品标记为已消费,以便用户可以再次购买商品。

如需验证购买交易,请先检查购买交易的状态是否为 PURCHASED。如果购买交易的状态为 PENDING,则您应按照处理待处理的交易中的说明处理购买交易。对于通过 onPurchaseUpdated()queryPurchases 接收的购买交易,您应在应用授予权利之前进一步验证购买交易以确保其合法性。如需了解如何正确验证购买交易,请参阅在授予权利前验证购买交易

一旦您验证了购买交易,您的应用就可以向用户授予权利了。授予权利后,您的应用必须确认购买交易。此确认会告知 Google Play 您已授予购买权。

授予权利并确认购买交易的流程取决于购买的是非消耗型商品、消耗型商品,还是订阅。

对于消耗型商品,consumeAsync() 方法满足确认要求,并且表明您的应用已授予用户权利。此外,通过此方法,您的应用可让一次性商品可供再次购买。

如需表明某件一次性商品已被消耗,请调用 consumeAsync() 并添加 Google Play 应在用户重新购买时提供的购买令牌。您还必须传递一个实现 ConsumeResponseListener 接口的对象。该对象用于处理消耗操作的结果。您可以替换 onConsumeResponse() 方法,Google Play 结算库会在消耗操作完成时调用该方法。

以下示例展示了如何使用关联的购买令牌来消耗商品:

Kotlin

fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
    val purchase : Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

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

    billingClient.consumeAsync(consumeParams, { billingResult, outToken ->
        if (billingResult.responseCode == BillingResponseCode.OK) {
            // Handle the success of the consume operation.
        }
    })
}

Java

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
    Purchase purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

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

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);
}

如需确认非消耗型商品的购买交易,请使用结算库中的 BillingClient.acknowledgePurchase() 或 Google Play Developer API 中的 Product.Purchases.Acknowledge。在确认购买交易之前,您的应用应使用 Google Play 结算库中的 isAcknowledged() 方法或 Google Play Developer API 中的 acknowledgementState 字段检查该购买交易是否已经过确认。

以下示例展示了如何使用 Google Play 结算库来确认购买交易:

Kotlin

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

fun handlePurchase() {
    if (purchase.purchaseState === PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged) {
            val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
            val ackPurchaseResult = withContext(Dispatchers.IO) {
               client.acknowledgePurchase(acknowledgePurchaseParams.build())
            }
        }
     }
}

Java

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

订阅的处理方式与非消耗型商品类似。您可以使用 Google Play 结算库中的 BillingClient.acknowledgePurchase() 或 Google Play Developer API 中的 Purchases.Subscriptions.Acknowledge 确认订阅。所有初始订阅购买交易都需要确认。订阅续订不需要确认。如需详细了解订阅何时需要确认,请参阅销售订阅内容主题。

提取购买交易

使用 PurchasesUpdatedListener 监听购买交易更新不足以确保您的应用会处理所有购买交易。有时您的应用可能不知道用户进行的部分购买交易。在下面这几种情况下,您的应用可能会跟踪不到或不知道购买交易:

  • 在购买过程中出现网络问题:用户成功购买了商品并收到了 Google 的确认消息,但他们的设备在通过 PurchasesUpdatedListener 收到购买交易的通知之前失去了网络连接。
  • 多台设备:用户在一台设备上购买了一件商品,然后在切换设备时期望看到该商品。
  • 处理在您的应用外进行的购买交易:某些购买交易(如促销活动兑换)可能会在您的应用外进行。

为了处理这些情况,请确保您的应用在 onResume()onCreate() 方法中调用 BillingClient.queryPurchases(),以确保所有购买交易都得到成功处理,如处理购买交易中所述。

处理在您的应用外进行的购买交易

某些购买交易(如促销活动兑换)可能发生在您的应用外。当用户在您的应用外进行购买交易时,他们希望您的应用显示应用内消息,或使用某种通知机制告知用户应用已正确接收并处理该购买交易。下面是一些可接受的机制:

  • 显示应用内弹出式消息。
  • 将消息传送到应用内消息箱,并清楚地指出应用内消息箱中有新消息。
  • 使用操作系统通知消息。

请注意,当您的应用识别出购买交易时,它有可能处于任何状态。甚至有可能在用户进行购买交易时根本没有安装您的应用。无论应用处于什么状态,用户都希望在继续使用应用时收到其所购商品。

无论用户进行购买交易时应用处于什么状态,您都必须检测购买交易。不过,在一些例外情况下,不立即通知用户已收到商品或许是可以接受的。例如:

  • 当用户在玩游戏时,显示消息可能会让用户分心。在这种情况下,必须待游戏结束后再通知用户。
  • 在出现过场动画时,显示消息可能会让用户分心。在这种情况下,必须待过场动画结束后再通知用户。
  • 当用户在游戏中学习初始教程和进行用户设置时。我们建议您在新用户打开游戏后立即将奖励通知用户,或在用户进行初始设置期间通知他们。不过,您也可以等到用户正式进入游戏环节时再通知用户。

在斟酌何时以及如何通知用户在您的应用外进行的购买交易时,一定要把用户放在心上。如果用户没有立即收到通知,他们可能会感到困惑,并且可能会停止使用您的应用,与用户支持团队联系,或在社交媒体上抱怨。

处理待处理的交易

Google Play 支持待处理的交易,即从用户发起购买交易到购买交易的付款方式得到处理期间需要执行一个或多个额外步骤的交易。在 Google 通知您已通过用户的付款方式成功扣款之前,您的应用不得授予对这些类型的购买交易的权利。

例如,用户可以选择现金作为付款方式来创建应用内商品的 PENDING 购买交易。然后,用户可以选择在一家实体店完成交易,并通过通知和电子邮件收到一个代码。当用户到达实体店时,他们可以在收银员处兑换该代码并用现金支付。Google 随后会通知您和用户已收到现金。您的应用随后就可以授予用户权利了。

为了支持待处理的购买交易,请在初始化您的应用期间调用 enablePendingPurchases()

当您的应用通过 PurchasesUpdatedListener 或由于调用 queryPurchases() 而收到新的购买交易时,使用 getPurchaseState() 方法确定购买交易的状态是 PURCHASED 还是 PENDING。请注意,只有在状态为 PURCHASED 时,您才能授予权利。如果您的应用在用户完成购买交易时正在运行,系统会再次调用 PurchasesUpdatedListener,并且 PurchaseState 现在为 PURCHASED。此时,您的应用可以使用处理一次性购买的标准方法处理购买交易。此外,您的应用还应在其 onResume()onCreate() 方法中调用 queryPurchases(),以处理您的应用未在运行时过渡到 PURCHASED 状态的购买交易。

您的应用还可以通过监听 OneTimeProductNotifications,将实时开发者通知与待处理的购买交易一起使用。当购买交易从 PENDING 过渡到 PURCHASED 时,您的应用会收到 ONE_TIME_PRODUCT_PURCHASED 通知。如果购买交易被取消,您的应用会收到 ONE_TIME_PRODUCT_CANCELED 通知。如果客户没有在规定的时间范围内完成付款,就会发生这种情况。当收到这些通知时,您可以使用 Google Play Developer API,该 API 包含 Purchases.productsPENDING 状态。