从 Google Play 结算库版本 4 迁移到版本 5 的迁移指南

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

概览

Google Play 结算库版本 5 引入了订阅基础方案和订阅优惠。这些功能可以丰富销售订阅的方式,并且有助于降低运营费用,因为无需创建和管理越来越多的 SKU。如需了解详情,请参阅 Play 管理中心内订阅方面的近期变更

作为 2022 年 5 月发布的 Play 结算库版本 5 和新订阅平台的一部分,所有现有的订阅产品都会自动转换为这种新范例。如需详细了解此转换,请参阅 Play 管理中心帮助文章中的“使用旧版订阅”部分。

现在,使用 Play 管理中心或 Play Developer API,您可以配置包含多个基础方案的单个订阅,每个基础方案可提供多项优惠。您可以为订阅优惠设置灵活的定价模式和资格条件。您可以使用各种各样的自动续订服务和预付费方案,在整个订阅生命周期中创建优惠。如需了解详情,请参阅集成指南

不过,如果您不打算立即采用这些新功能,仍可以将应用迁移到 Play 结算库版本 5,以后再规划后端组件迁移,前提是您的产品清单保留在向后兼容的配置中。

迁移步骤

创建后端产品清单

我们建议在迁移应用之前,按照新订阅平台中的实体结构为 Play 结算库版本 5 集成创建新产品。您可以在单个订阅下合并旧清单中提供相同使用权福利的重复产品,并使用基础方案和优惠配置来表示您希望提供的所有选项。如需详细了解此建议,请参阅 Play 管理中心帮助文章“使用旧版订阅”部分。

建议您不要修改在 2022 年 5 月版本之后转换的订阅产品,应该保持不变,即使用已废弃的方法(例如,querySkuDetailsAsync())随应用版本一起销售,避免引入可能影响这些旧 build 的变更。

转换流程会使 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 结算库

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

dependencies {
    def billingVersion = "5.0.0"

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

即使您没有修改任何方法调用,您的项目应该也会立即构建,因为我们已在 Play 结算库版本 5 中实现了向后兼容性。不过,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)

billingClient.querySkuDetailsAsync(params.build()) {
    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);

billingClient.querySkuDetailsAsync(params.build(),
    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)

billingClient.queryProductDetailsAsync(params.build()) {
    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 服务版本已过时。为了确保对此场景提供适当的支持,请先针对 PRODUCT_DETAILS 功能调用 isFeatureSupported(),然后再调用 queryProductDetailsAsync。如果响应为 OK,表示设备支持该功能,您可以继续调用 queryProductDetailsAsync()。如果响应为 FEATURE_NOT_SUPPORTED,您可以改用 querySkuDetailsAsync() 请求可用的向后兼容产品列表。如需详细了解如何使用 Play 结算库版本 5 的向后兼容功能,请参阅 2022 年 5 月的订阅功能指南

启动优惠购买流程

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

  • 不要将 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 = ...;
// Retrieve a value for "productDetails" by calling queryProductDetailsAsync()
// Get the offerToken of the selected offer
val offerToken = productDetails.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken

val productDetailsParamsList =
    listOf(
        BillingFlowParams.ProductDetailsParams.newBuilder()
            .setProductDetails(productDetails)
            .setOfferToken(offerToken)
            .build()
    )
val billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .build()

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

Java

// Retrieve a value for "productDetails" by calling queryProductDetailsAsync()
// Get the offerToken of the selected offer
String offerToken = productDetails
                     .getSubscriptionOfferDetails()
                     .get(selectedOfferIndex)
                     .getOfferToken();
// Set the parameters for the offer that will be presented
// in the billing flow creating separate productDetailsParamsList variable
ImmutableList<ProductDetailsParams> productDetailsParamsList =
        ImmutableList.of(
                 ProductDetailsParams.newBuilder()
                     .setProductDetails(productDetails)
                     .setOfferToken(offerToken)
                     .build()
        );

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

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

处理购买交易

使用 Google Play 结算库版本 5 处理购买交易的操作与以前的版本类似。

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

  • 请传递一个包含 BillingClient.ProductType 值的 QueryPurchasesParams 对象,而不要向 queryPurchasesAsync() 传递 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,该模块会在后端检查购买状态并管理 Play 结算服务订阅使用权。该 API 的旧版本不会返回在新平台中管理购买交易所需的所有详细信息。如需详细了解与先前版本相比的变化,请参阅 2022 年 5 月新订阅功能指南。

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

此新端点会返回所有订阅产品和所有购买交易的状态,无论销售该产品的应用版本是什么,也不管该产品是何时定义的(2022 年 5 月版本之前或之后),因此在迁移后,您只需使用此版本的订阅购买状态管理模块。