为一次性商品添加预订优惠

1. 简介

在此 Codelab 中,您将重点学习如何创建一次性商品 (OTP) 并为该商品添加预订优惠。

注意:在开始此 Codelab 之前,您需要填写一次性商品抢先体验计划意向调查表,申请使用预订功能。

观众群

此 Codelab 面向熟悉 一次性商品 并希望了解如何为一次性商品添加预订优惠的 Android 应用开发者。

前提条件

如果您不熟悉一次性商品,建议您先完成利用区域化产品定价开拓新市场 Codelab。

学习内容…

  • 如何使用 Google Play 管理中心 为一次性商品创建预订优惠。
  • 如何使用 Play 结算库 API 查询一次性商品和相应的预订优惠详情。

所需条件…

2. 构建示例应用

此 Codelab 使用示例 Android 应用教您如何管理一次性商品。该示例应用旨在成为一个功能齐全的 Android 应用,其中包含完整的源代码,展示了以下方面:

  • 将应用与 PBL 集成。
  • 提取一次性商品和相关的预订优惠。
  • 执行区域定价的购买流程。

以下演示视频展示了示例应用在部署和运行后的外观和行为。

如果您已经熟悉一次性商品和 Play 结算库 (PBL),则可以下载示例应用并进行试用。

前提条件

在构建和部署示例应用之前,请执行以下操作:

构建

此构建步骤的目标是生成示例应用的已签名 Android App Bundle 文件。

如需生成 Android App Bundle,请执行以下步骤:

  1. 从 GitHub 下载示例应用
  2. 构建示例应用。在构建之前,请更改示例应用的软件包名称,然后进行构建。如果您的 Play 管理中心内有其他应用的软件包,请确保您为示例应用提供的软件包名称是唯一的。

    注意:构建示例应用只会创建一个 APK 文件,您可以使用该文件进行本地测试。但是,运行该应用不会提取商品和价格,因为这些商品尚未在 Play 管理中心内配置。
  3. 生成已签名的 Android App Bundle。
    1. 生成上传密钥和密钥库
    2. 使用上传密钥为应用签名
    3. 配置 Play 应用签名功能

下一步是将 Android App Bundle 上传到 Google Play 管理中心。

3. 在 Play 管理中心内创建包含预订功能的一次性商品

如需在 Google Play 管理中心内创建一次性商品 (OTP),您需要在 Play 管理中心内拥有一个应用。在 Play 管理中心内创建一个应用,然后上传之前创建的已签名 App Bundle。

创建应用

如需创建应用,请执行以下操作:

  1. 使用您的开发者账号登录 Google Play 管理中心
  2. 点击创建应用 。系统随即会打开创建应用 页面。
  3. 输入应用名称、选择默认语言,以及其他与应用相关的详细信息。
  4. 点击创建应用 。系统随即会在 Google Play 管理中心内创建一个应用。

现在,您可以上传示例应用的已签名 App Bundle 了。

上传已签名的 App Bundle

  1. 将已签名的 App Bundle 上传到 Google Play 管理中心的内部测试轨道。只有在上传后,您才能在 Play 管理中心内配置与创收相关的功能。
    1. 依次点击测试和发布 > 测试 > 内部版本 > 创建新版本
    2. 输入版本名称并上传已签名的 APK 文件。
    3. 点击下一步 ,然后点击保存并发布

现在,您可以创建一次性商品了。

创建一次性商品

现在,创建您希望用户购买的一次性商品。

  1. Google Play 管理中心内打开示例应用,然后依次前往 Monetize with Play > Products > One-time products
  2. 点击创建一次性商品
  3. 输入以下商品详情:
    • 商品 ID :输入唯一 ID。例如,upcoming_movie_1
    • (可选)标签 :添加相关标签。
    • 名称 :输入商品名称。例如,Product Movie
    • 说明 :输入商品说明。例如,Product Description
    • (可选)添加图标图片 :上传代表您产品的图标。
    注意:出于此 Codelab 的目的,您可以跳过配置税务、合规性和计划 部分。
  4. 点击下一步
  5. 添加购买选项并配置其地区供应情况。一次性商品需要至少一个购买选项,该选项用于定义授予使用权的 方式、价格和地区供应情况。在此 Codelab 中,我们将为商品添加标准的购买 选项。在购买选项 部分中,输入以下详细信息:
    • 购买选项 ID :输入唯一 ID。例如,buy-movie
    • 购买类型 :选择购买
    • (可选)标签 :添加特定于此购买选项的标签。
    • (可选)点击高级选项 以配置高级选项。出于此 Codelab 的目的,您可以跳过高级选项配置。
  6. 接下来,您必须配置购买选项的地区供应情况和价格。在地区供应情况中,您将指定您的产品可在哪些地区销售,甚至包括您的应用尚未发布到的地区。默认情况下,购买选项在所有地区均供应。在供应情况和定价 部分中,点击修改供应状态和可预订状态
    1. 选择设为不供应
    请注意,系统会自动选择所有地区,并将它们设置为供应
    1. 仅取消选择 United States 国家/地区,然后点击设为不供应 。现在,一次性商品将仅在 United States 供应。
    2. 所有地区 下拉列表中,选择可供应的国家/地区 。您应该只会看到 United States
    3. 点击价格 图标。系统随即会显示一个对话框,用于设置价格。
    4. 输入 10 美元,然后点击保存
  7. 点击保存为草稿

注意:暂时请勿激活购买选项。我们将在配置预订优惠后激活它。这是因为您无法为已设置地区供应情况的已激活购买选项添加预订优惠。

添加预订优惠

现在,您将为之前创建的“购买”类型的购买选项添加预订优惠。借助预订优惠,用户可以在您的商品正式发布之前购买该商品。请注意,预订优惠仅适用于购买 类型的购买选项,并且只能针对某个区域的新产品进行配置。

添加预订优惠涉及以下 2 个步骤:

  1. 为预订优惠准备购买 购买选项。
  2. 为购买选项添加预订优惠。

为预订优惠准备“购买”类型的购买选项

  1. Google Play 管理中心内打开示例应用,然后依次前往 Monetize with Play > Products > One-time products
  2. 一次性商品 页面中,点击您的商品 (upcoming_movie_1) 对应的向右箭头。系统随即会打开修改一次性商品 页面。
  3. 点击您之前创建的 buy-movie 购买选项对应的向右箭头。系统随即会打开修改购买选项 页面。
  4. 点击修改供应状态和可预订状态 ,然后选择设为供应并允许用户预订
  5. 所有地区 下拉列表中,选择可供应的国家/地区 。系统应该只会显示您之前配置的 United States
  6. 选择相应国家/地区,然后点击设置为仅可预订
  7. 点击保存

请注意,您尚未为购买选项添加预订优惠。下一步是添加预订优惠。

添加预订优惠

  1. Google Play 管理中心内打开示例应用,然后依次前往 Monetize with Play > Products > One-time products
  2. 一次性商品 页面中,针对您的商品 (upcoming_movie_1) 依次点击添加优惠 > 预订 。系统随即会打开添加预订 页面。
  3. 输入预订详情:
    • 预订 ID :输入 preorder-offer-1
    • (可选)添加折扣:您可以选择百分比绝对折扣。出于此 Codelab 的目的,请选择
    • (可选)标签 :添加相关标签。
    • 开始日期和时间 :设置至少 3 天后的日期。
    • 结束日期和时间 :设置比开始日期晚至少 24 小时的日期。
    • 预订后的供应情况 :选择商品是在预订期结束后立即供应,还是在稍后的特定日期/时间供应。
    • (可选)低价保证 :如果您希望用户支付的预订价格和发布价格中的最低价格,请选择此选项。这可以成为吸引早期购买者的强大动力。
  4. 点击保存
  5. 打开您的一次性商品 (upcoming_movie_1) 的修改一次性商品 页面。
  6. 点击“购买”类型的购买选项 (buy-movie) 对应的激活
  7. 点击“购买”类型的购买选项下的预订优惠 (preorder-offer-1) 对应的激活 。系统随即会激活预订优惠,并使其在您之前在预订详情中配置的日期生效。

预订优惠创建视频

以下视频展示了之前介绍的预订优惠创建步骤。

4. 与 PBL 集成

如需将您的应用与 Play 结算库 (PBL) 集成,请执行以下步骤:

  1. 将 Play 结算库依赖项添加到示例应用。
    dependencies {
    val billing_version = "8.1.0"
    
    implementation("com.android.billingclient:billing-ktx:$billing_version")
    }
    
  2. 初始化 BillingClient。BillingClient 是驻留在您的应用中并与 Play 结算库通信的客户端 SDK。以下代码段展示了如何初始化结算客户端。
    private BillingClient createBillingClient() {
    return BillingClient.newBuilder(activity)
        .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
        // For one-time products, add a listener to process and acknowledge the purchases. This will notify
        // Google the purchase was processed.
        // For client-only apps, use billingClient.acknowledgePurchase().
        // 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
        // In this sample snippet purchases aren't processed. You must
        // implement your business logic to process and acknowledge the purchases.
        .setListener((billingResult, purchases) -> {})
        .enableAutoServiceReconnection()
        .build();
     }
    
  3. 连接到 Google Play。以下代码段展示了如何连接到 Google Play。
    /**
    * Starts the billing connection with Google Play. This method should be called exactly once
    * before any other methods in this class.
    *
    * @param productList The list of products to query for after the connection is established.
    */
    public void startBillingConnection(List<Product> productList) {
        billingClient.startConnection(
            new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                Log.d(TAG, "Billing Client Connection Successful");
                queryProductDetails(productList);
                } else {
                Log.e(TAG, "Billing Client Connection Failed: " + billingResult.getDebugMessage());
                listener.onBillingSetupFailed(billingResult); // Propagate the error to the listener to show a message to the user.
                }
            }
    
            @Override
            public void onBillingServiceDisconnected() {
                Log.e(TAG, "Billing Client Connection Lost");
                listener.onBillingError("Billing Connection Lost");
            }
            });
    }
    
  4. 提取一次性商品详情。将应用与 PBL 集成后,您必须将一次性商品详情提取到应用中。以下代码段展示了如何在应用中提取一次性商品详情。
    private void queryProductDetails(List<Product> productList) {
        QueryProductDetailsParams queryProductDetailsParams =
            QueryProductDetailsParams.newBuilder().setProductList(productList).build();
    
        billingClient.queryProductDetailsAsync(
            queryProductDetailsParams,
            new ProductDetailsResponseListener() {
            @Override
            public void onProductDetailsResponse(
                BillingResult billingResult, QueryProductDetailsResult productDetailsResponse) {
                if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                List<ProductDetails> productDetailsList =
                    productDetailsResponse.getProductDetailsList();
                    listener.onProductDetailsResponse(productDetailsList);
                } else {
                Log.e(TAG, "QueryProductDetailsAsync Failed: " + billingResult.getDebugMessage());
                listener.onBillingError("Query Products Failed: " + billingResult.getResponseCode());
                }
            }
            });
    }
    
    ProductDetails 中提取一次性商品(在此示例中为 upcoming_movie_1)后,您会收到类似于以下内容的响应:
    {
        "productId": "upcoming_movie_1",
        "type": "inapp",
        "title": "Purrfect Mayhem: The Final Playback (Movies All Day | Play Samples)",
        "name": "Purrfect Mayhem: The Final Playback",
        "description": "Yolo and Thorne must reach the original broadcasting site to initiate the \"Final Playback\" and save the timeline. Follow them through their race against the Clockinators.",
        "skuDetailsToken": "<---skuDetailsToken--->",
        "oneTimePurchaseOfferDetails": {},
        "oneTimePurchaseOfferDetailsList": [
            {
                "priceAmountMicros": 8500000,
                "priceCurrencyCode": "USD",
                "formattedPrice": "$8.50",
                "offerIdToken": "<---offerIdToken--->",
                "offerId": "preorder",
                "purchaseOptionId": "buy-option",
                "offerTags": [],
                "validTimeWindow": {
                    "startTimeMillis": 1756771200000,
                    "endTimeMillis": 1785542400000
                },
                "preorderDetails": {
                    "preorderReleaseTimeMillis": 1785542400000,
                    "preorderPresaleEndTimeMillis": 1785542400000
                }
            }
        ]
    }
    
    请注意,预订优惠详情位于 oneTimePurchaseOfferDetailsList 中。此列表包含 1 个购买选项(buy-option),您已在 Play 管理中心内为该选项配置了预订优惠。您可以通过每个购买选项的 offerIdToken 唯一标识该选项。
  5. 提取优惠令牌以及预订优惠详情。您需要产品令牌才能在第 6 步中启动结算流程。
    @Override
    public void onProductDetailsResponse(List<ProductDetails> productDetailsList) {
    
    if (productDetailsList != null && !productDetailsList.isEmpty()) {
    
    // Process productDetailsList returned by QueryProductDetailsResult
    for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) {
      for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails :
          productDetails.getOneTimePurchaseOfferDetailsList()) {
        // Checks if the offer is a preorder offer.
        if (oneTimePurchaseOfferDetails.getPreorderDetails() != null) {
          // Process the returned PreorderDetails
          OneTimePurchaseOfferDetails.PreorderDetails preorderDetails =
              oneTimePurchaseOfferDetails.getPreorderDetails();
          // Get preorder release time in millis.
          long preorderReleaseTimeMillis = preorderDetails.getPreorderReleaseTimeMillis();
          // Get preorder presale end time in millis.
          long preorderPresaleEndTimeMillis = preorderDetails.getPreorderPresaleEndTimeMillis();
          // Get offer ID
            String offerId = oneTimePurchaseOfferDetails.getOfferId();
          // Get the associated purchase option ID
          if (oneTimePurchaseOfferDetails.getPurchaseOptionId() != null) {
            String purchaseOptionId = oneTimePurchaseOfferDetails.getPurchaseOptionId();
          }
        }
      }
      }
      } else {
            Log.e(TAG, "No product details found for " + productId);
        }
    }
    
  6. 启动结算流程。
    /**
     * Launches the billing flow for the product with the given offer token.
    *
    * @param activity The activity instance from which the billing flow will be launched.
    * @param productDetails The product details of the product to purchase.
    * @param offerToken The offer token of the product to purchase.
    * @return The result of the billing flow.
    */
    public void launchPurchase(Activity activity, ProductDetails productDetails, String offerToken) {
        ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
            ImmutableList.of(
                BillingFlowParams.ProductDetailsParams.newBuilder()
                    .setProductDetails(productDetails)
                    .setOfferToken(offerToken)
                    .build());
        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .build();
        billingClient.launchBillingFlow(activity, billingFlowParams);
    }
    

5. 测试购买选项

在您的正式版应用中提供一次性商品之前,您可以使用许可测试人员和 Play Billing Lab 测试 PBL 集成。

如需了解如何使用 Play Billing Lab 测试购买选项,请参阅利用区域化产品定价开拓新市场 Codelab。

6. 后续步骤

参考文档

7. 恭喜!

恭喜!您已成功浏览 Google Play 管理中心,为一次性商品创建了预订优惠。现在,您对 Google Play 灵活的一次性购买商品清单有了更深入的了解。

调查问卷

我们非常重视您对此 Codelab 的反馈。请考虑抽出几分钟时间填写我们的调查问卷。