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

本主题介绍了如何将 Google Play 结算库集成到您的应用中以开始销售商品。

购买交易的生命周期

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

  1. 向用户展示他们可以购买什么。
  2. 启动购买流程,以便用户接受购买交易。
  3. 在您的服务器上验证购买交易。
  4. 向用户提供内容。
  5. 确认内容已传送给用户。对于消耗型商品,用户要先消耗掉已购商品,才能再次购买。

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

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

初始化与 Google Play 的连接

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

添加 Google Play 结算库依赖项

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

Groovy

dependencies {
    def billing_version = "7.0.0"

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

Kotlin

dependencies {
    val billing_version = "7.0.0"

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

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

Groovy

dependencies {
    def billing_version = "7.1.1"

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

Kotlin

dependencies {
    val billing_version = "7.1.1"

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

初始化 BillingClient

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

  • 建议您一次打开一个活跃的 BillingClient 连接,以避免对某一个事件进行多次 PurchasesUpdatedListener 回调。
  • 建议您在应用启动或进入前台时为 BillingClient 发起连接,以确保您的应用能够及时处理购买交易。为此,您可以使用 registerActivityLifecycleCallbacks 注册的 ActivityLifecycleCallbacks,并监听 onActivityResumed,以便在首次检测到 activity 被恢复时初始化连接。如需详细了解为何应遵循此最佳实践,请参阅处理购买交易部分。此外,请记得在关闭应用时结束连接。

如需创建 BillingClient,请使用 newBuilder。您可以将任何上下文传递给 newBuilder()BillingClient 则使用前者来获取应用上下文。这意味着您不必担心内存泄漏。为了接收有关购买交易的更新,您还必须调用 setListener,并传递对 PurchasesUpdatedListener 的引用。此监听器可接收应用中所有购买交易的更新。

Kotlin

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

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   // Configure other settings.
   .build()

Java

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

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    // Configure other settings.
    .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 建立连接后,您就可以查询可售的商品并将其展示给用户了。

在将商品展示给用户之前,查询商品详情是非常重要的一步,因为查询会返回本地化的商品信息。对于订阅,请确保您的商品展示符合所有 Play 政策

如需查询应用内商品详情,请调用 queryProductDetailsAsync

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

Kotlin

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    productDetailsList ->
      // check billingResult
      // process returned productDetailsList
}

Java

QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build();

billingClient.queryProductDetailsAsync(
    queryProductDetailsParams,
    new ProductDetailsResponseListener() {
        public void onProductDetailsResponse(BillingResult billingResult,
                List<ProductDetails> productDetailsList) {
            // check billingResult
            // process returned productDetailsList
        }
    }
)

查询商品详情时,应传递 QueryProductDetailsParams 的实例,用于指定在 Google Play 管理中心内创建的商品 ID 字符串列表以及 ProductTypeProductType 可以是 ProductType.INAPP(针对一次性商品),也可以是 ProductType.SUBS(针对订阅)。

使用 Kotlin 扩展程序进行查询

如果您使用 Kotlin 扩展,可以通过调用 queryProductDetails() 扩展函数查询应用内商品详情。

queryProductDetails() 会利用 Kotlin 协程,因此您无需定义单独的监听器。相反,该函数会挂起,直到查询完成,然后您可以处理查询结果:

suspend fun processPurchases() {
    val productList = listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("product_id_example")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )
    val params = QueryProductDetailsParams.newBuilder()
    params.setProductList(productList)

    // leverage queryProductDetails Kotlin extension function
    val productDetailsResult = withContext(Dispatchers.IO) {
        billingClient.queryProductDetails(params.build())
    }

    // Process the result.
}

在极少数情况下,某些设备无法支持 ProductDetailsqueryProductDetailsAsync(),这通常是因为 Google Play 服务版本已过时。为确保对此场景提供适当的支持,请参阅 Play 结算库版本 5 迁移指南,了解如何使用向后兼容的功能。

处理查询结果

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

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

在提供订阅之前,验证用户是否尚未订阅。此外,还请注意以下事项:

  • queryProductDetailsAsync() 会返回订阅商品详情,并且每项订阅最多包含 50 个优惠。
  • queryProductDetailsAsync() 仅返回用户有资格享受的优惠。如果用户尝试购买其没有资格享受的优惠(例如,应用显示的是过时的可享优惠列表),Play 会通知用户其不符合优惠条件,并且用户可以改为选择购买基础方案。

启动购买流程

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

Kotlin

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

val productDetailsParamsList = listOf(
    BillingFlowParams.ProductDetailsParams.newBuilder()
        // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
        .setProductDetails(productDetails)
        // For One-time products, "setOfferToken" method shouldn't be called.
        // For subscriptions, to get an offer token, call ProductDetails.subscriptionOfferDetails()
        // for a list of offers that are available to the user
        .setOfferToken(selectedOfferToken)
        .build()
)

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

// Launch the billing flow
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

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

ImmutableList<ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // For one-time products, "setOfferToken" method shouldn't be called.
            // For subscriptions, to get an offer token, call
            // ProductDetails.subscriptionOfferDetails() for a list of offers
            // that are available to the user.
            .setOfferToken(selectedOfferToken)
            .build()
    );

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

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

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) {
           // Process the purchase as described in the next section.
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user canceling 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) {
            // Process the purchase as described in the next section.
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user canceling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

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

Google Play 的购买成功界面
图 2. Google Play 的购买成功界面。

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

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

标明个性化价格

如果应用可能会面向欧盟用户分发,请在调用 launchBillingFlow 时使用 setIsOfferPersonalized() 方法向用户披露您的商品价格已通过自动化决策进行了个性化设置。

Google Play 购买屏幕指明系统已为用户提供自定义价格。
图 3. Google Play 购买屏幕指明系统已为用户提供自定义价格。

您必须参阅《欧盟消费者权益指令》2011/83/EU 6 (1) (ea) CRD 条款,确定您向用户提供的价格是否进行了个性化设置。

setIsOfferPersonalized() 接受布尔值输入。当该值为 true 时,Play 界面会包含披露声明。当该值为 false 时,Play 界面会忽略披露声明。默认值为 false

如需了解详情,请参阅消费者帮助中心

附加用户标识符

在您启动购买流程时,您的应用可以使用 obfuscatedAccountIdobfuscatedProfileId 附加您为进行购买交易的用户拥有的任何用户标识符。标识符示例可以是系统中用户登录信息的经过混淆处理的版本。设置这些参数有助于 Google 检测欺诈。此外,它还有助于您确保将购买交易归因于正确的用户,如向用户授予使用权中所述。

检测和处理购买交易

本部分介绍的购买交易检测和处理适用于所有类型的购买交易,包括促销活动兑换等应用外购买交易。

您的应用可以通过以下任一方式检测新的购买交易和已完成的待处理购买交易

  1. 当您的应用调用 launchBillingFlow 而导致调用 onPurchasesUpdated 时(如前一部分所述),或者当您的应用在有待处理的购买交易完成或在应用之外发生购买交易时,应用正在运行且与结算库保持有效连接时。例如,家庭成员在另一部设备上批准待处理的购买交易。
  2. 当您的应用调用 queryPurchasesAsync 来查询用户的购买交易时。

对于第 1 种情况,只要您的应用正在运行且与 Google Play 结算库保持有效连接,系统就会自动针对新购买交易或已完成的购买交易调用 onPurchasesUpdated。如果您的应用未运行或您的应用没有有效的 Google Play 结算库连接,系统将不会调用 onPurchasesUpdated。请注意,建议您的应用在前台运行时尽量保持有效连接,以确保应用及时收到购买交易更新。

对于第 2 种情况,您必须调用 BillingClient.queryPurchasesAsync(),以确保您的应用处理所有购买交易。建议您在应用成功与 Google Play 结算库建立连接时执行此操作(如初始化 BillingClient 中所述,建议在应用启动或进入前台时执行此操作)。为此,您可以在收到 onServiceConnected 的成功结果时调用 queryPurchasesAsync。遵循此建议对于处理以下事件和情况至关重要:

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

您的应用检测到新的或已完成的购买交易后,应执行以下操作:

  • 验证购买交易。
  • 向用户授予已完成购买交易的内容。
  • 通知用户。
  • 通知 Google 您的应用已处理已完成的购买交易。

以下各部分将详细介绍这些步骤,最后一个部分将对所有步骤进行总结。

验证购买交易

在向用户授予福利之前,您的应用应始终验证购买交易以确保其合法性。为此,您可以遵循在授予权利前验证购买交易中所述的指南。只有在验证购买交易后,您的应用才能继续处理购买交易并向用户授予权限,下一部分将对此进行讨论。

向用户授予使用权

您的应用验证购买交易后,就可以继续向用户授予使用权并通知用户。在授予权利之前,请确保您的应用会检查购买交易的状态是否为 PURCHASED。如果购买交易处于“待处理”状态,您的应用应通知用户,他们仍需执行操作来完成购买交易,然后才能获得使用权。仅在购买交易从“PENDING”转换为“SUCCESS”时授予权利。 如需了解详情,请参阅处理待处理的交易

如果您已按照附加用户标识符中所述的步骤为购买交易附加了用户标识符,则可以检索这些标识符,并将其用于在您的系统中归因于正确的用户。当您的应用可能丢失了有关购买交易的用户上下文时,此方法非常有用。请注意,在您的应用外进行的购买交易不会设置这些标识符。在这种情况下,您的应用可以向已登录的用户授予使用权,也可以提示用户选择首选账号。

通知用户

向用户授予使用权后,您的应用应显示一条通知,确认购买交易已成功。这可确保用户不会对购买交易是否成功完成感到困惑,否则可能会导致用户停止使用您的应用,与用户支持团队联系,或在社交媒体上抱怨。请注意,您的应用可能会在应用生命周期的任何时间检测购买交易更新。例如,家长在另一部设备上批准了待处理的购买交易,在这种情况下,您的应用可能希望延迟到适当的时间通知用户。以下是一些适合延迟的示例:

  • 在游戏或过场动画的动作部分,显示消息可能会让用户分心。在这种情况下,必须待游戏结束后再通知用户。
  • 当用户在游戏中学习初始教程和进行用户设置时。例如,用户可能在安装您的应用之前在应用之外进行过购买交易。我们建议您在新用户打开游戏后立即将奖励告知用户,或在用户进行初始设置期间告知他们。如果您的应用要求用户先创建账号或登录,然后才能向用户授予使用权限,建议您告知用户需要完成哪些步骤才能声明其购买交易。这一点至关重要,因为如果您的应用未处理购买交易,系统会在 3 天后退款。

在向用户发送购买交易通知时,Google Play 建议使用以下机制:

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

通知应告知用户他们获得了哪些福利。例如,“您购买了 100 个金币!”此外,如果购买交易是由于 Play Pass 等计划提供的福利而促成的,您的应用应向用户说明这一点。例如“已收到商品!您刚刚使用 Play Pass 兑换了 100 颗宝石。 继续。”每个计划可能都有关于向用户显示的推荐文本的指导,以便传达福利。

通知 Google 购买交易已处理完毕

您的应用向用户授予使用权并通知用户交易成功后,需要通知 Google 已成功处理购买交易。方法是确认购买交易,并且必须在 三天内完成,以确保系统不会自动退款并撤消使用权。以下部分介绍了确认不同类型的购买交易的流程。

消耗型商品

对于消耗型商品,如果您的应用具有安全后端,我们建议您使用 Purchases.products:consume 可靠地消耗所购商品。若要确保所购商品未被消耗掉,请查看 Purchases.products:get 调用结果中的 consumptionState。如果您的应用只有客户端而没有后端,请使用 Google Play 结算库中的 consumeAsync()。这两种方法都符合确认要求,并且表明您的应用已将使用权授予用户。这些方法也支持您的应用提供与输入购买令牌对应的一次性商品,供用户再次购买。如果使用 consumeAsync(),您还必须传递一个实现 ConsumeResponseListener 接口的对象。该对象用于处理消耗操作的结果。您可以替换 onConsumeResponse() 方法,Google Play 结算库会在消耗操作完成时调用该方法。

以下示例展示了如何使用关联的购买令牌,通过 Google Play 结算库来消耗商品:

Kotlin

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }

Java

    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);

非消耗型商品

如需确认非消耗型商品的购买交易,如果您的应用具有安全后端,我们建议您使用 Purchases.products:acknowledge 可靠地确认购买交易。若要确保购买交易尚未确认,请查看 Purchases.products:get 调用结果中的 acknowledgementState

如果您的应用只有客户端,请在应用中使用 Google Play 结算库中的 BillingClient.acknowledgePurchase()。在确认购买交易之前,您的应用应检查它是否已通过使用 Google Play 结算库中的 isAcknowledged() 方法进行确认。

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

Kotlin

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

val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchase.purchaseToken)
val ackPurchaseResult = withContext(Dispatchers.IO) {
     client.acknowledgePurchase(acknowledgePurchaseParams.build())
}

Java

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

AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
 client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

订阅

订阅的处理方式与非消耗型商品类似。如果可能,请使用 Google Play Developer API 中的 Purchases.subscriptions.acknowledge 通过安全后端可靠地确认购买交易。若要验证购买交易尚未确认,请通过 Purchases.subscriptions:get 查看购买资源中的 acknowledgementState。否则,您也可以在查看 isAcknowledged() 后,使用 Google Play 结算库中的 BillingClient.acknowledgePurchase() 确认订阅。所有初始订阅购买交易都需要确认。续订购买交易不需要确认。如需详细了解订阅何时需要确认,请参阅销售订阅内容主题。

回顾

以下代码段总结了这些步骤。

Kotlin

fun handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify
.
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Java

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

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify

    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

如需验证您的应用是否已正确实现这些步骤,您可以按照测试指南操作。

处理待处理的交易

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

例如,用户可以选择一家实体店来发起交易,并在稍后使用现金付款。用户会通过通知和电子邮件收到一个代码。当用户到达实体店时,可以在收银员处兑换该代码并用现金支付。Google 随后会通知您和用户已收到付款。您的应用随后就可以授予用户权利了。

在初始化 BillingClient 的过程中,调用 enablePendingPurchases() 以便为您的应用启用待处理的交易。您的应用必须启用并支持一次性商品的待处理交易。在添加支持之前,请务必了解待处理交易的购买生命周期

当应用通过 PurchasesUpdatedListener 或由于调用 queryPurchasesAsync 而收到新的购买交易时,使用 getPurchaseState() 方法确定购买交易的状态是 PURCHASED 还是 PENDING。只有在状态为 PURCHASED 时,您才能授予使用权。

如果应用在用户完成购买交易时正在运行且您有有效的 Play 结算库连接,系统会再次调用 PurchasesUpdatedListener,并且 PurchaseState 现在为 PURCHASED。此时,您的应用可以使用检测和处理购买交易的标准方法处理购买交易。此外,应用还应在其 onResume() 方法中调用 queryPurchasesAsync(),以处理在应用未运行时已转换为 PURCHASED 状态的购买交易。

当购买交易从 PENDING 过渡到 PURCHASED 时,您的 real_time_developer_notifications 客户端会收到 ONE_TIME_PRODUCT_PURCHASEDSUBSCRIPTION_PURCHASED 通知。如果购买交易被取消,您会收到 ONE_TIME_PRODUCT_CANCELEDSUBSCRIPTION_PENDING_PURCHASE_CANCELED 通知。如果客户没有在规定的时间范围内完成付款,就会发生这种情况。请注意,您可以随时使用 Google Play Developer API 检查购买交易的当前状态。

处理多件购买交易

Google Play 允许客户在一笔交易中购买多件相同的应用内商品,只需在购物车中指定商品数量即可(4.0 及更高版本的 Google Play 结算库支持该功能)。应用应根据指定的购买数量来处理多件购买交易并授予使用权。

为了实现多件购买,应用的配置逻辑需要检查商品数量。您可以通过以下 API 访问 quantity 字段:

添加用于处理多件购买交易的逻辑后,您需要在 Google Play 管理中心的应用内商品管理页面上为相应的商品启用多件购买功能。

查询用户的结算配置

getBillingConfigAsync() 提供用户在 Google Play 中使用的国家/地区设置。

您可以在创建 BillingClient 后查询用户的结算配置。以下代码段说明了如何调用 getBillingConfigAsync()。通过实现 BillingConfigResponseListener 来处理响应。此监听器可接收应用中发起的所有结算配置查询的更新。

如果返回的 BillingResult 不含错误,您可以查看 BillingConfig 对象中的 countryCode 字段,以获取用户的 Play 国家/地区信息。

Kotlin

// Use the default GetBillingConfigParams.
val getBillingConfigParams = GetBillingConfigParams.newBuilder().build()
billingClient.getBillingConfigAsync(getBillingConfigParams,
    object : BillingConfigResponseListener {
        override fun onBillingConfigResponse(
            billingResult: BillingResult,
            billingConfig: BillingConfig?
        ) {
            if (billingResult.responseCode == BillingResponseCode.OK
                && billingConfig != null) {
                val countryCode = billingConfig.countryCode
                ...
            } else {
                // TODO: Handle errors
            }
        }
    })

Java

// Use the default GetBillingConfigParams.
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(getBillingConfigParams,
    new BillingConfigResponseListener() {
      public void onBillingConfigResponse(
          BillingResult billingResult, BillingConfig billingConfig) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK
            && billingConfig != null) {
            String countryCode = billingConfig.getCountryCode();
            ...
         } else {
            // TODO: Handle errors
        }
      }
    });

Google Play 游戏首页中的购物车放弃提醒(默认处于启用状态)

对于通过应用内商品 (IAP) 创收的游戏开发者,在 Google Play 管理中心内有效的商品目录 (SKU) 可以在应用之外销售,其中一种方式就是使用“购物车放弃提醒”功能,该功能会在用户浏览 Google Play 商店时提醒他们完成之前放弃的购买交易。这些购买交易是在 Google Play 商店的 Google Play 游戏首页中完成的,而非在您的应用中。

此功能默认处于启用状态,可帮助用户从上次中断的地方继续,并帮助开发者最大限度地提高销量。不过,您可以通过提交“弃购提醒”功能停用表单为您的应用停用此功能。如需了解在 Google Play 管理中心内管理 SKU 的最佳实践,请参阅创建应用内商品

以下图片显示了 Google Play 商店中显示的购物车放弃提醒:

Google Play 商店界面显示了针对之前放弃的购买交易的购买提示
图 2. Google Play 商店界面显示了针对之前放弃的购买交易的购买提示。

Google Play 商店界面显示了针对之前放弃的购买交易的购买提示
图 3. Google Play 商店界面显示了针对之前放弃的购买交易的购买提示。