从版本 4 或 5 迁移到 Google Play Billing Library 6

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

有关 6.0.0 版中的所有更改,请参阅版本说明

概览

Google Play Billing Library 6 以版本 5 中引入的新订阅功能为基础,并增加了一些改进。这些功能允许你以更多方式销售订阅,从而减少运营成本,无需创建和管理不断增加的 SKU 数量。

如需详细了解 Play Billing Library 5 中引入的新功能,请参阅 Play 管理中心近期订阅变动

向后兼容的 Play Billing Library 升级

作为 Play Billing Library 5 和新订阅平台于 2022 年 5 月发布的一部分,所有现有订阅产品已自动转换为这种新范例。这意味着你无需进行任何订阅产品配置更改即可拥有与新版 Play Billing Library 兼容的产品目录。如需详细了解订阅 SKU 如何转换为向后兼容的订阅,请参阅 Play 管理中心帮助文章中的“使用旧版订阅”部分。

旧版应用仍可正常运行

如果你拥有向后兼容的订阅目录,你的应用所有现有版本都应仍能正常运行,以支持这些产品。一次性产品购买在旧版本中也应继续正常运行。

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

升级到 Play Billing Library 5 或 6

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

作为迁移的第一步,你可以仅 更新库版本,保持你的产品目录和后端不变,并在应用仍使用已废弃方法的情况下测试应用。如果你不使用 queryPurchaseslaunchPriceChangeFlowsetVrPurchaseFlow,它应该仍能正常运行。之后,你可以迭代以完全采用 2022 年 5 月发布的新订阅功能

如果你之前已通过 Google Play Billing Library 5 迁移采用了这些功能,则可以直接跳到标有 更新 Google Play Billing Library更改用户的订阅购买交易 的部分。如果你从较早的版本开始或尚未完全采用新功能,则可以阅读以下完整迁移步骤,了解如何采用这些功能。

完整迁移步骤

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

使用 Play 管理中心或 Play Developer API,你现在可以配置具有多个基本方案的单个订阅,每个基本方案又具有多个优惠。订阅优惠具有灵活的定价模式和资格选项。你可以使用各种自动续订和预付费方案,在整个订阅生命周期中创建优惠。

我们建议你在迁移应用之前,先按照新订阅平台中的实体结构为 Play Billing Library 6 集成创建新产品。你可以将旧目录中表示相同授权权益的重复产品合并到单个订阅下,并使用基本方案和优惠配置来表示你希望提供的所有选项。有关此建议的更多信息,请参阅 Play 管理中心帮助文章中的“使用旧版订阅”部分。

我们建议你在 2022 年 5 月发布后不要修改已转换的订阅产品;你应该保持它们原样,以与使用已废弃方法(例如 querySkuDetailsAsync())的应用版本一起销售,而无需引入可能影响这些旧版本构建的更改。

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

  • 在前端,使用 querySkuDetailsAsync() 获取订阅产品详细信息的应用版本只能销售向后兼容的基本方案和优惠,并且只能有一种向后兼容的基本方案和优惠组合,因此如果你向已转换的订阅添加新的方案或优惠,这些新增的基本方案或优惠将无法在这些旧版本的应用上销售。

  • 在后端,如果你在 Play 管理中心界面中编辑已转换的订阅,将无法使用 inappproducts 端点对其进行管理,即使你之前调用该端点用于此目的。你还应迁移到新的订阅购买状态端点 (purchases.subscriptionsv2.get) 来管理这些订阅的购买交易,因为旧的购买状态端点 (purchases.subscriptions.get) 仅返回处理向后兼容的基本方案和优惠购买所需的数据。如需了解详细信息,请阅读 管理订阅购买状态部分

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

如果你使用 Google Play Developer API 自动管理订阅产品目录,则需要使用新的订阅产品定义端点来创建和管理订阅、基本方案和优惠。阅读2022 年 5 月订阅功能指南,了解有关此版本产品目录 API 更改的更多信息。

要迁移用于 Google Play 结算订阅的自动化产品目录管理模块,请将 inappproducts API 替换为新的 Subscription Publishing API 来管理和发布你的订阅目录。有三个新端点:

这些新端点具备利用目录中所有新功能的必要功能:基本方案和优惠标记、区域定位、预付费方案等等。

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

使用已废弃方法(例如 querySkuDetailsAsync())的应用版本将无法销售任何不向后兼容的基本方案或优惠。你可以在此处阅读有关向后兼容优惠的信息。

更新 Google Play Billing Library

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

dependencies {
    def billingVersion = "6.0.0"

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

你的项目应该立即构建成功,即使你没有修改任何方法调用——Play Billing Library 6 是向后兼容的。SKU 的概念被认为是已废弃的,但为了使应用移植过程更简单、更循序渐进,它仍然存在。

初始化 Billing Client 并建立与 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 Billing Library 不支持新对象(订阅、基本方案、优惠等),新系统会将每个订阅 SKU 转换为单个向后兼容的基本方案和优惠。可用的一次性购买产品也会移植到 ProductDetails 对象。可以使用 getOneTimePurchaseOfferDetails() 方法访问一次性购买产品的优惠详细信息。

极少数情况下,某些设备可能无法支持 ProductDetailsqueryProductDetailsAsync(),这通常是由于 Google Play 服务版本过旧所致。为确保对此情景的良好支持,请在调用 queryProductDetailsAsync 之前,针对 PRODUCT_DETAILS 功能调用 isFeatureSupported()。如果响应为 OK,则表示设备支持该功能,你可以继续调用 queryProductDetailsAsync()。如果响应为 FEATURE_NOT_SUPPORTED,则可以转而使用 querySkuDetailsAsync() 请求可用的向后兼容产品列表。要详细了解如何使用向后兼容功能,请参阅2022 年 5 月订阅功能指南

启动优惠购买流程

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

  • 对于 BillingFlowParams,使用 ProductDetailsParams 代替 SkuDetails
  • 优惠详细信息(例如优惠 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 Billing Library 6 处理购买交易与旧版本类似。

要拉取用户拥有的所有有效购买交易并查询新购买交易,请执行以下操作:

  • 调用 queryPurchasesAsync() 时,传递包含 BillingClient.ProductType 值的 QueryPurchasesParams 对象,而不是传递 BillingClient.SkuType 值。

以下示例展示了进行这些更改前后你的应用可能的样子:

之前

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 月发布之前定义的已转换订阅产品,你当前的订阅购买状态管理组件应能正常运行,并且足以管理向后兼容优惠的购买交易,但它不支持任何新功能。

你需要为你的订阅购买状态管理模块实现新的 Subscription Purchases API,该 API 用于检查购买状态并在后端管理 Play 结算订阅授权。该 API 的旧版本不返回在新平台中管理购买交易所需的所有详细信息。有关与旧版本相比的更改的详细信息,请参阅2022 年 5 月新订阅功能指南。

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

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

更改用户的订阅购买交易

在 Play Billing Library 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 集成指南购买生命周期管理指南