将 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.1.1"

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

Kotlin

dependencies {
    val billing_version = "7.1.1"

    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 的连接,以确保您的应用及时处理购买操作。这可以通过使用 ActivityLifecycleCallbacks(通过 registerActivityLifecycleCallbacks 注册)并在首次检测到 Activity 恢复时监听 onActivityResumed 来初始化连接。有关为何应遵循此最佳实践的更多详细信息,请参阅处理购买操作的部分。另请记住在应用关闭时结束连接。

要创建 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 结算库将查询结果存储在 List 形式的 ProductDetails 对象中。然后,您可以对列表中的每个 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 显示了订阅的购买屏幕:

the google play purchase screen shows a subscription that is
            available for purchase
图 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.
    }
}

成功购买会生成类似于图 2 的 Google Play 购买成功屏幕。

google play's purchase success screen
图 2. Google Play 的购买成功屏幕。

成功购买还会生成一个购买令牌,这是一个唯一标识符,代表用户及其购买的应用内产品的产品 ID。您的应用可以在本地存储购买令牌,但我们强烈建议将令牌传递到您的安全后端服务器,您可以在该服务器上验证购买并防范欺诈。此过程在检测和处理购买操作中进一步描述。

用户还会收到包含订单 ID 或交易唯一 ID 的交易收据电子邮件。用户会收到包含唯一订单 ID 的电子邮件,其中包含每次一次性产品购买的信息,以及初始订阅购买和随后的定期自动续订信息。您可以使用订单 ID 在 Google Play 管理中心管理退款。

表明个性化价格

如果您的应用可以分发给欧盟用户,请在调用 launchBillingFlow 时使用 setIsOfferPersonalized() 方法,向用户披露商品价格是使用自动化决策过程个性化确定的。

The Google Play purchase screen indicating that the price was customized for the user.
图 3. Google Play 购买屏幕,表明价格是为用户定制的。

您必须查阅《消费者权利指令》2011/83/EU 第 6(1)(ea) 条 CRD,以确定您向用户提供的价格是否为个性化价格。

setIsOfferPersonalized() 接受布尔值输入。当为 true 时,Play 界面会包含披露信息。当为 false 时,界面会省略披露信息。默认值为 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 收到购买通知之前,他们的设备失去了网络连接。
  • 多设备:用户可能在一台设备上购买了一件商品,然后希望在切换设备时看到该商品。
  • 处理在应用外部进行的购买:某些购买操作,例如促销兑换,可以在应用外部进行。
  • 处理购买状态转换:用户可能在您的应用未运行时完成了 PENDING 购买的付款,并期望在打开应用时收到已完成购买的确认。

一旦您的应用检测到新的或已完成的购买,您的应用应该:

  • 验证购买操作。
  • 为已完成购买的用户授予内容。
  • 通知用户。
  • 通知 Google 您的应用已处理已完成的购买操作。

这些步骤将在以下部分详细讨论,然后是总结所有步骤的部分。

验证购买操作

您的应用应始终验证购买操作,以确保它们是合法的,然后才能向用户授予权益。这可以通过遵循授予权利前验证购买中描述的指南来完成。只有在验证购买之后,您的应用才能继续处理购买并向用户授予权利,这将在下一节讨论。

授予用户权利

一旦您的应用验证了购买操作,就可以继续向用户授予权利并通知用户。在授予权利之前,请确保您的应用正在检查购买状态是否为 PURCHASED。如果购买处于 PENDING 状态,您的应用应通知用户他们仍需完成操作才能完成购买,然后才能授予权利。仅当购买从 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 开发者 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 时,您的实时开发者通知客户端会收到 ONE_TIME_PRODUCT_PURCHASEDSUBSCRIPTION_PURCHASED 通知。如果购买被取消,您将收到 ONE_TIME_PRODUCT_CANCELEDSUBSCRIPTION_PENDING_PURCHASE_CANCELED 通知。如果您的客户未在规定时间内完成付款,可能会发生这种情况。请注意,您始终可以使用 Google Play 开发者 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 商店中出现的样子:

the Google Play Store screen shows a
    purchase prompt for a previously abandoned purchase
图 2. Google Play 商店屏幕显示了之前放弃的购买的购买提示。

the Google Play Store screen shows a
    purchase prompt for a previously abandoned purchase
图 3. Google Play 商店屏幕显示了之前放弃的购买的购买提示。