本主题介绍如何将 Google Play 结算库集成到您的应用中以开始销售产品。
购买的生命周期
以下是针对一次性购买或订阅的典型购买流程。
- 向用户展示他们可以购买的内容。
- 启动购买流程,以便用户接受购买。
- 在您的服务器上验证购买。
- 向用户提供内容。
- 确认内容的交付。对于消耗性产品,请消耗购买,以便用户可以再次购买该商品。
订阅会自动续订,直到取消。订阅可以经历以下状态
- 有效:用户状况良好,并且可以访问订阅。
- 已取消:用户已取消,但仍可以访问,直到过期。
- 宽限期内:用户遇到付款问题,但在 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
,请使用 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 Console 中创建的产品 ID 字符串列表以及 ProductType
。ProductType
可以是 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.
}
在极少数情况下,某些设备无法支持 ProductDetails
和 queryProductDetailsAsync()
,这通常是由于 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 product, "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
中列出的多个响应代码之一。请务必检查此结果,以确保启动购买流程时未出现任何错误。BillingResponseCode
为 OK
表示启动成功。
成功调用 launchBillingFlow()
后,系统会显示 Google Play 购买屏幕。图 1 显示了订阅的购买屏幕
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. } }
成功购买会生成类似于图 2 的 Google Play 购买成功屏幕。
成功购买还会生成购买令牌,该令牌是表示用户以及他们购买的应用内购买产品的产品 ID 的唯一标识符。您的应用可以在本地存储购买令牌,但我们建议将令牌传递到您的安全后端服务器,您可以在其中验证购买并防止欺诈。以下部分将进一步介绍此流程。
用户还会收到包含订单 ID 或交易唯一 ID 的交易收据电子邮件。对于每次一次性产品购买以及初始订阅购买和后续定期自动续订,用户都会收到包含唯一订单 ID 的电子邮件。您可以使用订单 ID 在 Google Play Console 中管理退款。
指示个性化价格
如果您的应用可以分发给欧盟的用户,请使用 setIsOfferPersonalized()
方法向用户披露商品价格是否使用自动化决策进行了个性化定制。
您必须查阅《消费者权利指令 2011/83/EU》第 6 条 (1) (ea) 款,以确定您提供给用户的价格是否为个性化价格。
setIsOfferPersonalized()
采用布尔值输入。当值为 true
时,Play UI 会包含披露信息。当值为 false
时,UI 会省略披露信息。默认值为 false
。
如需了解更多信息,请参阅 消费者帮助中心。
处理购买
用户完成购买后,您的应用需要处理该购买。在大多数情况下,您的应用会通过 PurchasesUpdatedListener
收到购买通知。但是,在某些情况下,您的应用会通过调用 BillingClient.queryPurchasesAsync()
来获取购买信息,如 获取购买信息 中所述。
此外,如果您在安全的后台服务器中拥有 实时开发者通知 客户端,则可以通过接收 subscriptionNotification
或 oneTimeProductNotification
通知来注册新的购买。收到这些通知后,请调用 Google Play 开发者 API 获取完整状态并更新您自己的后台状态。
您的应用应按照以下方式处理购买
- 验证购买。
- 向用户提供内容,并确认内容已交付。可选地,可以将商品标记为已消费,以便用户可以再次购买该商品。
要验证购买,首先检查 购买状态 是否为 PURCHASED
。如果购买状态为 PENDING
,则应按照 处理待处理交易 中所述处理购买。对于从 onPurchasesUpdated()
或 queryPurchasesAsync()
收到的购买,您应进一步验证购买以确保其合法性,然后您的应用才能授予权限。要了解如何正确验证购买,请参阅 在授予权限之前验证购买。
验证购买后,您的应用即可向用户授予权限。与购买关联的用户帐户可以通过 ProductPurchase.obfuscatedExternalAccountId
(应用内商品购买时由 Purchases.products:get
返回)或 SubscriptionPurchase.obfuscatedExternalAccountId
(订阅时由 Purchases.subscriptions:get
返回,在服务器端)或 obfuscatedAccountId
(在客户端,如果在购买时使用 setObfuscatedAccountId
设置了该值,则来自 Purchase.getAccountIdentifiers()
)进行识别。
授予权限后,您的应用必须确认购买。此确认操作会通知 Google Play 您已授予购买权限。
授予权限和确认购买的过程取决于购买的是消耗型商品、非消耗型商品还是订阅。
消耗型商品
对于消耗型商品,如果您的应用拥有安全的后台服务器,我们建议您使用 Purchases.products:consume
来可靠地消费购买。请确保购买尚未被消费,方法是检查调用 Purchases.products:get
的结果中的 consumptionState
。如果您的应用是仅限客户端的,没有后台服务器,请使用 Google Play 结算库中的 consumeAsync()
。这两种方法都满足确认要求,并表明您的应用已向用户授予权限。这些方法还使您的应用能够使与输入购买令牌对应的单次购买商品再次可供购买。使用 consumeAsync()
,您还必须传递一个实现了 ConsumeResponseListener
接口的对象。此对象处理消耗操作的结果。您可以重写 onConsumeResponse()
方法,Google Play 结算库在操作完成后会调用此方法。
以下示例说明了如何使用 Google Play 结算库和关联的购买令牌来消费商品
Kotlin
suspend fun handlePurchase(purchase: Purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync 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() val consumeResult = withContext(Dispatchers.IO) { client.consumePurchase(consumeParams) } }
Java
void handlePurchase(Purchase purchase) { // Purchase retrieved from BillingClient#queryPurchasesAsync 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); }
非消耗型商品
要确认非消耗型商品购买,如果您的应用拥有安全的后台服务器,我们建议您使用 Purchases.products:acknowledge
来可靠地确认购买。请确保购买尚未被确认,方法是检查调用 Purchases.products:get
的结果中的 acknowledgementState
。
如果您的应用是仅限客户端的,请在您的应用中使用 Google Play 结算库中的 BillingClient.acknowledgePurchase()
。在确认购买之前,您的应用应使用 Google Play 结算库中的 isAcknowledged()
方法检查它是否已被确认。
以下示例显示了如何使用 Google Play 结算库确认购买
Kotlin
val client: BillingClient = ... val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ... suspend 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 开发者 API 中的 Purchases.subscriptions.acknowledge
从您的安全后端可靠地确认购买。通过检查 Purchases.subscriptions:get
中购买资源的 acknowledgementState
来验证购买是否已被确认。否则,您可以使用 Google Play 结算库中的 BillingClient.acknowledgePurchase()
在检查 isAcknowledged()
后确认订阅。所有初始订阅购买都需要确认。订阅续订不需要确认。有关何时需要确认订阅的更多信息,请参阅 销售订阅 主题。
获取购买信息
使用 PurchasesUpdatedListener
监听购买更新不足以确保您的应用处理所有购买。您的应用可能无法获知用户进行的所有购买。以下是一些您的应用可能丢失或无法获知购买信息的场景
- 购买期间网络问题:用户成功购买并收到 Google 的确认,但在其设备通过
PurchasesUpdatedListener
收到购买通知之前,其设备断开了网络连接。 - 多个设备:用户在一个设备上购买商品,然后希望在切换设备时看到该商品。
- 处理在应用外部进行的购买:某些购买(例如促销兑换)可以在应用外部进行。
为了处理这些情况,请确保您的应用在 onResume()
方法中调用 BillingClient.queryPurchasesAsync()
,以确保所有购买都已成功处理,如 处理购买 中所述。
以下示例显示了如何获取用户的订阅购买信息。请注意,queryPurchasesAsync()
仅返回活动的订阅和未消费的单次购买。
Kotlin
val params = QueryPurchasesParams.newBuilder() .setProductType(ProductType.SUBS) // uses queryPurchasesAsync Kotlin extension function val purchasesResult = billingClient.queryPurchasesAsync(params.build()) // check purchasesResult.billingResult // process returned purchasesResult.purchasesList, e.g. display the plans user owns
Java
billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder() .setProductType(ProductType.SUBS) .build(), new PurchasesResponseListener() { public void onQueryPurchasesResponse(BillingResult billingResult, List<Purchase> purchases) { // check billingResult // process returned purchase list, e.g. display the plans user owns } } );
处理在应用外部进行的购买
某些购买可能在应用外部进行,例如促销兑换或 Google Play 游戏应用内购买 (IAP) 的购物车放弃提醒。当用户在应用外部进行购买时,他们希望您的应用显示应用内消息,或使用某种通知机制让用户知道应用已正确接收并处理了购买。一些可接受的机制包括
- 显示应用内弹出窗口。
- 将消息发送到应用内消息框,并明确说明应用内消息框中有新消息。
- 使用操作系统通知消息。
请记住,当您的应用识别购买时,它可能处于任何状态。甚至可能在购买时尚未安装您的应用。用户希望在恢复应用时收到其购买,无论应用处于何种状态。
您必须检测购买,无论应用在购买时处于何种状态。但是,在某些情况下,可能可以接受不立即通知用户已收到商品。例如
- 在游戏的动作部分,显示消息可能会分散用户的注意力。在这种情况下,您必须在动作部分结束后通知用户。
- 在过场动画期间,显示消息可能会分散用户的注意力。在这种情况下,您必须在过场动画结束后通知用户。
- 在游戏的初始教程和用户设置部分。我们建议您在新用户打开游戏或在初始用户设置期间立即通知他们奖励。但是,可以等到主要游戏序列可用后再通知用户。
在决定何时以及如何通知用户在应用外部进行的购买时,请始终牢记用户。任何时候用户没有立即收到通知,他们可能会感到困惑,并可能停止使用您的应用,联系用户支持或在社交媒体上抱怨。
Google Play 游戏主页中的购物车放弃提醒(默认启用)
对于通过应用内购买进行获利的游戏开发者来说,Google Play Console 中处于活动状态的库存单位 (SKU) 在应用外部销售的一种方式是购物车放弃提醒功能,该功能会在用户浏览 Google Play 商店时提醒他们完成之前放弃的购买。这些购买发生在您的应用外部,来自 Google Play 商店中的 Google Play 游戏主页。
此功能默认启用,以帮助用户从他们离开的地方继续操作,并帮助开发者最大化销售额。但是,您可以通过提交 购物车放弃提醒功能退出表单 来选择退出此功能。有关在 Google Play Console 中管理 SKU 的最佳实践,请参阅 创建应用内产品。
以下图片显示了 Google Play 商店中出现的购物车放弃提醒
处理待处理交易
Google Play 支持待处理交易,或在用户发起购买与处理购买的付款方式之间需要一个或多个额外步骤的交易。在 Google 通知您用户的付款方式已成功收费之前,您的应用不应授予此类购买的权利。
例如,用户可以通过选择一家他们稍后将用现金支付的实体店来发起交易。用户通过通知和电子邮件接收验证码。当用户到达实体店时,他们可以使用收银员兑换验证码并用现金支付。然后,Google 会通知您和用户已收到付款。然后,您的应用可以授予用户权利。
在初始化 BillingClient
的过程中,调用 enablePendingPurchases()
以启用应用的待处理交易。您的应用必须为一次性产品启用并支持待处理交易。在添加支持之前,请确保您了解待处理交易的 购买生命周期。
当您的应用通过您的 PurchasesUpdatedListener
或作为调用 queryPurchasesAsync()
的结果收到新的购买时,请使用 getPurchaseState()
方法确定购买状态是 PURCHASED
还是 PENDING
。您应该仅在状态为 PURCHASED
时授予权利。
如果您的应用在用户完成购买时正在运行,则会再次调用您的 PurchasesUpdatedListener
,并且 PurchaseState
现在为 PURCHASED
。此时,您的应用可以使用 处理购买 的标准方法处理购买。您的应用还应在应用的 onResume()
方法中调用 queryPurchasesAsync()
,以处理在您的应用未运行时已转换为 PURCHASED
状态的购买。
当购买从 PENDING
转换为 PURCHASED
时,您的 实时开发者通知 客户端会收到 ONE_TIME_PRODUCT_PURCHASED
或 SUBSCRIPTION_PURCHASED
通知。如果购买被取消,您将收到 ONE_TIME_PRODUCT_CANCELED
或 SUBSCRIPTION_PENDING_PURCHASE_CANCELED
通知。如果您的客户未在规定的时间内完成付款,则可能会发生这种情况。请注意,您始终可以使用 Google Play 开发者 API 检查购买的当前状态。
处理多数量购买
Google Play 结算库 4.0 及更高版本中支持,Google Play 允许客户通过从购物车中指定数量,在一笔交易中购买多个相同的应用内产品。您的应用应处理多数量购买并根据指定的购买数量授予权利。
要处理多数量购买,您的预配逻辑需要检查商品数量。您可以从以下 API 中访问 quantity
字段
- 来自 Google Play 结算库的
getQuantity()
。 - 来自 Google Play 开发者 API 的
Purchases.products.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
}
}
});