1. 简介
在此 Codelab 中,您将重点学习如何创建一次性商品、将应用与 Play 结算库 (PBL) 集成,以及分析购买交易中断的原因。
观众
本 Codelab 适用于正在使用 Play 结算库 (PBL) 或想要使用 PBL 通过一次性商品创收的 Android 应用开发者。
学习内容…
- 如何在 Google Play 管理中心内创建一次性商品。
- 如何将应用与 PBL 集成。
- 如何在 PBL 中处理消耗型和非消耗型一次性商品购买交易。
- 如何分析购买流程中的放弃情况。
所需条件…
- 使用开发者账号访问 Google Play 管理中心。如果您没有开发者账号,则需要创建账号。
- 此 Codelab 的示例应用,您可以从 GitHub 下载。
- Android Studio。
2. 构建示例应用
此示例应用旨在成为一款功能齐全的 Android 应用,其源代码完整,可展示以下方面:
- 将应用与 PBL 集成
- 获取一次性商品
- 启动一次性商品的购买流程
- 导致以下结算响应的购买场景:
BILLING_UNAVAILABLE
USER_CANCELLED
OK
ITEM_ALREADY_OWNED
以下演示视频展示了示例应用在部署和运行后的外观和行为。
前提条件
在构建和部署示例应用之前,请执行以下操作:
- 创建 Google Play 管理中心开发者账号。如果您已有开发者账号,请跳过此步骤。
- 在 Play 管理中心内创建新应用。创建应用时,您可以为示例应用指定任意应用名称。
- 安装 Android Studio。
构建
此构建步骤的目标是生成示例应用的已签名 Android App Bundle 文件。
如需生成 Android App Bundle,请执行以下步骤:
- 从 GitHub 下载示例应用。
- 构建示例应用。在构建之前,请更改示例应用的软件包名称,然后进行构建。如果您的 Play 管理中心内有其他应用的软件包,请确保您为示例应用提供的软件包名称是唯一的。
注意:构建示例应用只会创建一个可用于本地测试的 APK 文件。不过,运行应用不会提取商品和价格,因为您尚未在 Play 管理中心内配置商品,而这正是您在本 Codelab 中要进一步完成的操作。 - 生成已签名的 Android App Bundle。
下一步是将 Android app bundle 上传到 Google Play 管理中心。
3. 在 Play 管理中心内创建一次性商品
如需在 Google Play 管理中心内创建一次性商品,您需要在 Play 管理中心内拥有一个应用。在 Play 管理中心内创建应用,然后上传之前创建的已签名 app bundle。
创建应用
如需创建应用,请执行以下操作:
- 使用您的开发者账号登录 Google Play 管理中心。
- 点击创建应用。系统随即会打开创建应用页面。
- 输入应用名称,选择默认语言,以及其他与应用相关的详细信息。
- 点击创建应用。这会在 Google Play 管理中心内创建一个应用。
现在,您可以上传示例应用的已签名 app bundle。
上传已签名的 app bundle
- 将已签名的 app bundle 上传到 Google Play 管理中心的内部测试轨道。只有在上传后,您才能在 Play 管理中心内配置与创收相关的功能。
- 依次点击测试和发布 > 测试 > 内部版本 > 创建新的发布版本。
- 输入版本名称,然后上传已签名的应用包文件。
- 点击下一步,然后点击保存并发布。
现在,您可以创建一次性商品了。
创建一次性商品
如需创建一次性商品,请执行以下操作:
- 在 Google Play 管理中心的左侧导航菜单中,依次前往借助 Play 变现 > 商品 > 一次性商品。
- 点击创建一次性商品。
- 输入以下商品详情:
- 商品 ID:输入唯一的商品 ID。输入
one_time_product_01
。 - (可选)标记:添加相关标记。
- 名称:输入商品名称。例如
Product name
。 - 说明:输入商品说明。例如
Product description
。 - (可选)添加图标图片:上传可代表您产品的图标。
- 商品 ID:输入唯一的商品 ID。输入
- 点击下一步。
- 添加购买选项并配置其地区供应情况。一次性商品需要至少一个购买选项,用于定义使用权的授予方式、价格和地区供应情况。在此 Codelab 中,我们将为商品添加标准的购买选项。在购买选项部分中,输入以下详细信息:
- 购买选项 ID:输入购买选项 ID。例如
buy
。 - 购买类型:选择购买。
- (可选)标记:添加特定于此购买选项的标记。
- (可选)点击高级选项以配置高级选项。在此 Codelab 中,您可以跳过高级选项配置。
- 购买选项 ID:输入购买选项 ID。例如
- 在供应情况和定价部分中,依次点击设置价格 > 批量修改价格。
- 选择国家 / 地区选项。此操作会选择所有地区。
- 点击继续。系统随即会打开一个用于输入价格的对话框。输入 10 美元,然后点击应用。
- 点击保存,然后点击启用。系统随即会创建并启用购买选项。
在此 Codelab 中,请创建 3 个额外的一次性商品,并使用以下商品 ID:
- consumable_product_01
- consumable_product_02
- consumable_product_03
示例应用已配置为使用这些商品 ID。您可以提供不同的商品 ID,在这种情况下,您必须修改示例应用,以使用您提供的商品 ID。
在 Google Play 管理中心内打开示例应用,然后依次前往通过 Play 创收 > 商品 > 一次性商品。然后点击创建一次性商品,并重复第 3 步到第 9 步。
一次性商品创建视频
以下示例视频展示了之前介绍的一次性商品创建步骤。
4. 与 PBL 集成
现在,我们将了解如何将应用与 Play 结算库 (PBL) 集成。本部分介绍了集成的高级步骤,并为每个步骤提供了代码段。您可以将这些代码段作为实现实际集成的指南。
如需将应用与 PBL 集成,请执行以下步骤:
- 向示例应用添加 Play 结算库依赖项。
dependencies { val billing_version = "8.0.0" implementation("com.android.billingclient:billing-ktx:$billing_version") }
- 初始化 BillingClient。BillingClient 是位于应用中并与 Play 结算库通信的客户端 SDK。以下代码段展示了如何初始化结算客户端。
protected BillingClient createBillingClient() { return BillingClient.newBuilder(activity) .setListener(purchasesUpdatedListener) .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()) .enableAutoServiceReconnection() .build(); }
- 连接到 Google Play。以下代码段展示了如何连接到 Google Play。
public void startBillingConnection(ImmutableList<Product> productList) { Log.i(TAG, "Product list sent: " + productList); Log.i(TAG, "Starting connection"); billingClient.startConnection( new BillingClientStateListener() { @Override public void onBillingSetupFinished(BillingResult billingResult) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // Query product details to get the product details list. queryProductDetails(productList); } else { // BillingClient.enableAutoServiceReconnection() will retry the connection on // transient errors automatically. // We don't need to retry on terminal errors (e.g., BILLING_UNAVAILABLE, // DEVELOPER_ERROR). Log.e(TAG, "Billing connection failed: " + billingResult.getDebugMessage()); Log.e(TAG, "Billing response code: " + billingResult.getResponseCode()); } } @Override public void onBillingServiceDisconnected() { Log.e(TAG, "Billing Service connection lost."); } }); }
- 获取一次性商品详情。将应用与 PBL 集成后,您必须将一次性商品详情提取到应用中。以下代码段展示了如何在应用中提取一次性商品详情。
提取private void queryProductDetails(ImmutableList<Product> productList) { Log.i(TAG, "Querying products for: " + productList); QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(productList).build(); billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { @Override public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResponse) { // check billingResult Log.i(TAG, "Billing result after querying: " + billingResult.getResponseCode()); // process returned productDetailsList Log.i( TAG, "Print unfetched products: " + productDetailsResponse.getUnfetchedProductList()); setupProductDetailsMap(productDetailsResponse.getProductDetailsList()); billingServiceClientListener.onProductDetailsFetched(productDetailsMap); } }); }
ProductDetails
,会返回类似于如下所示的响应:{ "productId": "consumable_product_01", "type": "inapp", "title": "Shadow Coat (Yolo's Realm | Play Samples)", "name": "Shadow Coat", "description": "A sleek, obsidian coat for stealth and ambushes", "skuDetailsToken": "<---skuDetailsToken--->", "oneTimePurchaseOfferDetails": {}, "oneTimePurchaseOfferDetailsList": [ { "priceAmountMicros": 1990000, "priceCurrencyCode": "USD", "formattedPrice": "$1.99", "offerIdToken": "<--offerIdToken-->", "purchaseOptionId": "buy", "offerTags": [] } ] }, { "productId": "consumable_product_02", "type": "inapp", "title": "Emperor Den (Yolo's Realm | Play Samples)", "name": "Emperor Den", "description": "A fair lair glowing with molten rock and embers", "skuDetailsToken": "<---skuDetailsToken--->", "oneTimePurchaseOfferDetails": {}, "oneTimePurchaseOfferDetailsList": [ { "priceAmountMicros": 2990000, "priceCurrencyCode": "USD", "formattedPrice": "$2.99", "offerIdToken": "<--offerIdToken-->", "purchaseOptionId": "buy", "offerTags": [] } ] }
- 启动结算流程。
public void launchBillingFlow(String productId) { ProductDetails productDetails = productDetailsMap.get(productId); if (productDetails == null) { Log.e( TAG, "Cannot launch billing flow: ProductDetails not found for productId: " + productId); billingServiceClientListener.onBillingResponse( BillingResponseCode.ITEM_UNAVAILABLE, BillingResult.newBuilder().setResponseCode(BillingResponseCode.ITEM_UNAVAILABLE).build()); return; } ImmutableList<ProductDetailsParams> productDetailsParamsList = ImmutableList.of( ProductDetailsParams.newBuilder().setProductDetails(productDetails).build()); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); billingClient.launchBillingFlow(activity, billingFlowParams); }
- 检测和处理购买交易。在此步骤中,您需要执行以下操作:
- 验证购买交易
- 向用户授予使用权
- 通知用户
- 向 Google 通知购买流程
private void handlePurchase(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. if (purchase.getPurchaseState() == PurchaseState.PURCHASED) { for (String product : purchase.getProducts()) { Log.d(TAG, product + " purchased successfully! "); } } // Step 4: Notify Google the purchase was processed. // For one-time products, acknowledge the purchase. // This sample app (client-only) uses billingClient.acknowledgePurchase(). // For consumable one-time products, consume the purchase // This sample app (client-only) uses billingClient.consumeAsync() // If you have a secure backend, you must acknowledge purchases on your server using the // server-side API. // See https://developer.android.com/google/play/billing/security#acknowledge if (purchase.getPurchaseState() == PurchaseState.PURCHASED && !purchase.isAcknowledged()) { if (shouldConsume(purchase)) { ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build(); billingClient.consumeAsync(consumeParams, consumeResponseListener); } else { AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); billingClient.acknowledgePurchase( acknowledgePurchaseParams, acknowledgePurchaseResponseListener); } } }
5. 分析购买流程中的放弃情况
到目前为止,在此 Codelab 中,Play 结算服务响应主要侧重于有限的几种情形,例如 USER_CANCELLED、BILLING_UNAVAILABLE、OK 和 ITEM_ALREADY_OWNED 响应。不过,Play 结算可以返回 13 种不同的响应代码,这些代码可能会因各种实际因素而触发。
本部分详细说明了 USER_CANCELLED
和 BILLING_UNAVAILABLE
错误响应的原因,并建议了您可以采取的可能纠正措施。
USER_CANCELED 响应错误代码
此响应代码表示用户在完成购买交易之前放弃了购买流程界面。
可能的原因 | 您可以采取哪些措施? |
|
|
BILLING_UNAVAILABLE 响应错误代码
此响应代码表示,由于用户的付款服务提供商或其所选的付款方式存在问题,因此无法完成购买交易。例如,用户的信用卡已过期,或者用户位于不受支持的国家/地区。此代码并不表示 Play 结算系统本身存在错误。
可能的原因 | 您可以采取哪些措施? |
|
|
针对响应错误代码的重试策略
针对 Play 结算库 (PBL) 中的可恢复错误的有效重试策略因上下文而异,例如用户在会话期间的互动(如购买期间)与后台操作(如在应用恢复时查询购买交易)。实现这些策略非常重要,因为某些 BillingResponseCode
值表示可以通过重试解决的临时问题,而其他值表示永久性问题,不需要重试。
对于用户在会话中遇到的错误,建议采用简单的重试策略,并设置最大尝试次数,以尽可能减少对用户体验的干扰。相反,对于不需要立即执行的后台操作(例如确认新购买交易),建议采用指数退避算法。
如需详细了解特定响应代码及其对应的建议重试策略,请参阅处理 BillingResult 响应代码。
6. 后续步骤
- 了解如何最大限度地利用 Play 结算集成。
- 请务必遵循验证和处理购买交易的最佳实践,在用户开始购买这些商品后,在您的安全后端执行这些操作。
参考文档
7. 恭喜!
恭喜!您已成功在 Google Play 管理中心内创建新的一次性商品、测试结算响应代码,并分析了购买流程中的用户流失情况。
调查问卷
我们非常重视您对此 Codelab 的反馈。请考虑花几分钟时间填写我们的调查问卷。