从 4 或 5 版迁移到 Google Play 结算库 6

本主题介绍如何从 Google Play 结算库 4 或 5 迁移到 Google Play 结算库 6,以及如何使用新的订阅功能。

有关 6.0.0 版本中的所有更改的完整列表,请参阅发行说明

概述

Google Play 结算库 6 基于 5 版中引入的新订阅功能,并增加了一些改进。这些功能允许您通过多种方式销售订阅,通过消除创建和管理数量不断增加的 SKU 的需要来降低运营成本。

有关 Play 结算库 5 中引入的新功能的更多信息,请参阅Play 管理中心中订阅的最新更改

向后兼容的 Play 结算库升级

作为 2022 年 5 月发布的 Play 结算库 5 和新的订阅平台的一部分,所有现有的订阅产品都已自动转换为这种新模式。这意味着您无需进行任何订阅产品配置更改即可拥有与新版本的 Play 结算库兼容的目录。有关如何将订阅 SKU 转换为向后兼容订阅的更多信息,请参阅Play 管理中心帮助文章中的“使用旧订阅”部分。

您应用的旧版本仍然有效

如果您拥有向后兼容的订阅目录,则您应用的所有现有版本都应继续按预期方式为这些产品工作。一次性产品购买也应在旧版本中继续正常工作。

使用已弃用方法(例如,querySkuDetailsAsync())的应用版本将无法销售任何与向后不兼容的基本计划或优惠。您可以在相关的Play 管理中心帮助文章中了解有关向后兼容优惠的信息。

升级到 Play 结算库 5 或 6

Play 结算库 5 和 6 包含已弃用的方法querySkuDetailsAsyncBillingFlowParams.Builder.setSkuDetails,它将SkuDetails作为结算流程参数。这意味着您可以通过规划不同的迁移阶段逐步迁移到 Play 结算库 6。

作为迁移的第一步,您可以只更新库版本,保留您的目录和后端不变,并在应用仍然使用已弃用方法时测试您的应用。如果您未使用queryPurchaseslaunchPriceChangeFlowsetVrPurchaseFlow,它应该仍然可以正常工作。之后,您可以迭代以完全采用2022 年 5 月发布的新订阅功能

如果您之前已通过 Google Play 结算库 5 迁移采用了这些功能,您可以直接转到标记为更新 Google Play 结算库更改用户的订阅购买的部分。如果您是从早期版本开始或尚未完全采用新功能,您可以阅读以下完整的迁移步骤,了解如何采用它们。

完整的迁移步骤

在您的后端产品目录中创建新的订阅

现在,您可以使用 Play 开发者控制台或 Play 开发者 API 配置单个订阅,每个订阅包含多个基本计划,每个基本计划包含多个优惠。订阅优惠具有灵活的定价模式和资格选项。您可以使用各种自动续订和预付费计划在订阅生命周期中创建优惠。

我们建议在迁移应用之前,按照新订阅平台中的实体结构为您的 Play 结算库 6 集成创建新产品。您可以将旧目录中代表相同权利益的重复产品整合到单个订阅下,并使用基本计划和优惠配置来表示您想要提供的全部选项。有关此建议的更多信息,请参阅Play 管理中心帮助文章中的“使用旧订阅”部分。

我们建议您在 2022 年 5 月发布后不要修改已转换的订阅产品;您应该保留它们,以便使用已弃用方法(例如,querySkuDetailsAsync())的应用版本进行销售,而不会引入可能影响这些旧版本的更改。

为了避免意外更改导致您现有集成出现问题,转换过程中将您2022年5月之前的目录中订阅产品设置为只读状态。可以更改这些订阅,但更改会影响您的前端和后端集成。

  • 在前端,使用querySkuDetailsAsync() 获取订阅产品详情的应用版本只能销售向后兼容的基础套餐和优惠,并且只能存在一个向后兼容的基础套餐和优惠组合。因此,如果您向已转换的订阅添加新的套餐或优惠,则这些新的附加基础套餐或优惠将无法在您应用的这些旧版本上销售。

  • 在后端,如果您在 Play Console UI 中编辑已转换的订阅,则无法使用inappproducts 端点管理它们(如果您之前为此目的调用过该端点)。您还应迁移到新的订阅购买状态端点(purchases.subscriptionsv2.get)来管理这些订阅的购买,因为旧的购买状态端点(purchases.subscriptions.get)仅返回处理向后兼容的基础套餐和优惠购买所需的数据。阅读管理订阅购买状态部分以了解更多信息。

使用新的 API 管理您的后端订阅目录

如果您使用 Google Play 开发者 API 自动管理订阅产品目录,则需要使用新的订阅产品定义端点来创建和管理订阅、基础套餐和优惠。阅读2022 年 5 月订阅功能指南,了解此版本的产品目录 API 变更的更多信息。

要迁移 Google Play 结算订阅的自动产品目录管理模块,请将inappproducts API 替换为新的订阅发布 API 以管理和发布您的订阅目录。共有三个新的端点:

这些新的端点具有利用目录中所有新功能所需的所有功能:基础套餐和优惠标签、区域定位、预付费套餐等等。

您仍应使用inappproducts API 管理一次性购买产品的应用内产品目录。

使用已弃用方法(例如,querySkuDetailsAsync())的应用版本将无法销售任何不向后兼容的基础套餐或优惠。您可以在此处了解有关向后兼容优惠的信息。

更新 Google Play 结算库

创建新的订阅产品目录后,您可以将应用迁移到 Google 结算库 5。将现有 Play 结算库依赖项替换为更新的版本,添加到您的应用的build.gradle 文件中。

dependencies {
    def billingVersion = "6.0.0"

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

即使您没有修改对方法的任何调用,您的项目也应该立即构建——Play 结算库 6 向后兼容。SKU 的概念被认为已弃用,但仍然存在,以使应用移植过程更简单、更渐进。

初始化结算客户端并建立与 Google Play 的连接

从 Android 应用启动购买的第一步保持不变:

显示可购买的产品

要获取用户有资格购买的所有优惠:

  • SkuDetailsParams 替换为QueryProductDetailsParams
  • BillingClient.querySkuDetailsAsync() 调用切换为使用BillingClient.queryProductDetailsAsync()

请注意,查询结果现在是ProductDetails,而不是SkuDetails。每个ProductDetails 项目都包含有关产品的信息(ID、标题、类型等)。对于订阅产品,ProductDetails 包含一个List<ProductDetails.SubscriptionOfferDetails>,即订阅优惠详情列表。对于一次性购买产品,ProductDetails 包含一个ProductDetails.OneTimePurchaseOfferDetails。这些可用于确定向用户显示哪些优惠。

以下示例显示了在进行这些更改之前和之后您的应用可能是什么样子:

之前

Kotlin

val skuList = ArrayList<String>()

skuList.add("up_basic_sub")

val params = SkuDetailsParams.newBuilder()

params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS).build()

billingClient.querySkuDetailsAsync(params) {
    billingResult,
    skuDetailsList ->
    // Process the result
}

Java

List<String> skuList = new ArrayList<>();

skuList.add("up_basic_sub");

SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();

params.setSkusList(skuList).setType(SkuType.SUBS).build();

billingClient.querySkuDetailsAsync(params,
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                List<SkuDetails> skuDetailsList) {
            // Process the result.
        }
    }
);

之后

Kotlin

val productList =
    listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("up_basic_sub")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )

val params = QueryProductDetailsParams.newBuilder().setProductList(productList).build()

billingClient.queryProductDetailsAsync(params) {
    billingResult,
    productDetailsList ->
    // Process the result
}

Java

ImmutableList<Product> productList = ImmutableList.of(Product.newBuilder()
                                            .setProductId("up_basic_sub")
                                            .setProductType(ProductType.SUBS)
                                            .build());

QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
    .setProductList(productList)
    .build();

billingClient.queryProductDetailsAsync(
        params,
        new ProductDetailsResponseListener() {
                public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
                    // Process the result
                }
        }
);

queryProductDetailsAsync 的回调返回一个List<ProductDetails>。每个ProductDetails 项目都包含有关产品的信息(ID、标题、类型等)。主要区别在于,订阅产品现在还包含一个List<ProductDetails.SubscriptionOfferDetails>,其中包含用户可以使用的所有优惠。

由于旧版本的 Play 结算库不支持新对象(订阅、基础套餐、优惠等),因此新系统会将每个订阅 SKU 转换为单个向后兼容的基础套餐和优惠。可用的的一次性购买产品也会移植到ProductDetails 对象中。一次性购买产品的优惠详情可以使用getOneTimePurchaseOfferDetails() 方法访问。

很少情况下,某些设备无法支持ProductDetailsqueryProductDetailsAsync(),通常是由于Google Play 服务版本过旧。为了确保对此场景的适当支持,请在调用queryProductDetailsAsync 之前,针对PRODUCT_DETAILS 功能调用isFeatureSupported()。如果响应为OK,则设备支持此功能,您可以继续调用queryProductDetailsAsync()。如果响应为FEATURE_NOT_SUPPORTED,则可以使用querySkuDetailsAsync() 请求可用的向后兼容产品列表。要了解有关如何使用向后兼容功能的更多信息,请参阅2022 年 5 月订阅功能指南

启动优惠购买流程

启动优惠的购买流程与启动 SKU 的流程非常相似。要使用版本 6 启动购买请求,请执行以下操作:

  • 不要使用SkuDetails 用于BillingFlowParams,而使用ProductDetailsParams
  • 优惠详情(例如优惠 ID、基础套餐 ID 等)可以使用SubscriptionOfferDetails 对象获取。

要使用用户选择的优惠购买产品,请获取所选优惠的offerToken 并将其传递到ProductDetailsParams 对象中。

创建BillingFlowParams 对象后,使用BillingClient 启动结算流程的方式保持不变。

以下示例显示了在进行这些更改之前和之后您的应用可能是什么样子:

之前

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
val billingFlowParams = BillingFlowParams.newBuilder()
                            .setSkuDetails(skuDetails)
                            .build()

val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build();

BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

之后

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 the offer token corresponding to the selected
        // offer call productDetails.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
        .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 the offer token corresponding to the selected
            // offer call productDetails.getSubscriptionOfferDetails().get(selectedOfferIndex).getOfferToken()
            .setOfferToken(selectedOfferToken)
            .build()
    );

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

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

处理购买

使用 Google Play 结算库 6 处理购买的方式与以前的版本相似。

要提取用户拥有的所有活动购买并查询新的购买,请执行以下操作:

  • 不要将BillingClient.SkuType 值传递给queryPurchasesAsync(),而是传递包含BillingClient.ProductType 值的QueryPurchasesParams 对象。

以下示例显示了在进行这些更改之前和之后您的应用可能是什么样子:

之前

Kotlin

billingClient.queryPurchasesAsync(BillingClient.SkuType.SUBS) {
    billingResult,
    purchaseList -> {
        // Process the result
    }
}

Java

billingClient.queryPurchasesAsync(
    BillingClient.SkuType.SUBS,
    new PurchasesResponseListener() {
        public void onQueryPurchasesResponse(
                BillingResult billingResult,
                List<Purchase> purchases) {
            // process the result
        }
    }
);

之后

Kotlin

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder()
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
) { billingResult, purchaseList ->
    // Process the result
}

Java

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder().setProductType(ProductType.SUBS).build(),
    new PurchasesResponseListener() {
        public void onQueryPurchasesResponse(
                BillingResult billingResult,
                List<Purchase> purchases) {
            // Process the result
        }
    }
);

管理应用外购买待处理交易的步骤没有改变。

使用新的后端 API 管理订阅购买状态

您应该迁移后端中的订阅购买状态管理组件,以准备好处理之前步骤中创建的新产品的购买。您当前的订阅购买状态管理组件应该像往常一样适用于您在 2022 年 5 月发布之前定义的已转换订阅产品,并且它足以管理向后兼容优惠的购买,但它不支持任何新功能。

您需要为您的订阅购买状态管理模块实现新的订阅购买 API,该模块检查购买状态并在您的后端管理 Play 结算订阅权利。旧版本的 API(旧版本)不会返回在新平台上管理购买所需的所有详细信息。有关与先前版本相比的更改详情,请参阅2022 年 5 月新的订阅功能指南

通常情况下,每次收到SubscriptionNotification实时开发者通知时,您都需要调用订阅购买API来获取订阅状态的最新信息。您需要将对purchases.subscriptions.get的调用替换为订阅购买API的新版本purchases.subscriptionsv2.get。有一个名为SubscriptionPurchaseV2的新资源,它提供了足够的信息来在新模型中管理订阅的购买权限。

这个新的端点返回您所有订阅产品和所有购买的狀態,无论销售它们的应用版本以及产品定义的时间(2022年5月发布之前或之后)如何,因此迁移后,您只需要此版本的订阅购买状态管理模块。

更改用户的订阅购买

在 Play Billing 库 5 及更早版本中,ProrationMode 用于应用对用户订阅购买的更改,例如升级或降级。此方法已弃用,并在 6 版中替换为 ReplacementMode

处理订阅价格更改

先前已弃用的 launchPriceConfirmationFlow API 已在 Play Billing 库 6 中移除。有关替代方法,请参阅价格更改指南

处理 Play Billing 库错误

在 Play Billing 库 6 中,添加了一个新的 NETWORK_ERROR 代码来指示用户设备与 Google Play 系统之间的网络连接问题。 SERVICE_TIMEOUTSERVICE_UNAVAILABLE 代码也进行了更改。有关更多信息,请参阅处理 BillingResult 响应代码

处理挂起的交易

从 6.0.0 版开始,Play Billing 库不会为挂起的购买创建订单 ID。对于这些购买,订单 ID 会在购买转移到PURCHASED状态后填充。确保您的集成仅在交易完全完成之后才期望订单 ID。您仍然可以使用购买令牌作为您的记录。有关处理挂起购买的更多信息,请参阅 Play Billing 库的集成指南购买生命周期管理指南