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

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

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

概述

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

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

向后兼容的 Play 结算库升级

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

您应用的旧版本仍然有效

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

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

升级到 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 Console 帮助文章中的“使用旧版订阅”部分。

我们建议您在 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 结算订阅的自动产品目录管理模块,请使用新的订阅发布 API 替换inappproducts 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
  • 可以使用 SubscriptionOfferDetails 对象获取优惠详细信息,例如优惠 ID、基本计划 ID 等。

要使用用户选择的优惠购买产品,请获取所选优惠的 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 结算库 5 及更早版本中,ProrationMode 用于将更改应用于用户的订阅购买,例如升级或降级。此方法已弃用,并在版本 6 中替换为ReplacementMode

处理订阅价格更改

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

处理 Play Billing Library 错误

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

处理挂起的交易

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