本文档详细介绍了如何将一次性商品 (OTP) 与 Play 结算库集成。此外,本文档还介绍了如何集成与一次性商品相关的各种购买选项和优惠。
您可以为一次性商品配置多个购买选项和优惠。例如,您可以为同一款一次性商品配置购买选项“购买”和预订优惠。
前提条件
如需为一次性商品配置多项优惠,您必须使用
queryProductDetailsAsync() API。系统不支持已废弃的
querySkuDetailsAsync() API。如需了解如何使用
queryProductDetailsAsync()以及将launchBillingFlow()
作为输入的ProductDetailsParams版本,请参阅迁移步骤。
查询商品详情
如果您为一次性
商品配置了多项优惠或购买选项,则 ProductDetails 对象返回的
queryProductDetailsAsync() 方法可以为每款一次性商品提供多个可用的购买选项“购买”
和/或“租借”。如需获取每个 ProductDetails 对象的所有
符合条件的优惠列表,请使用
getOneTimePurchaseOfferDetailsList() 方法。此列表只会返回用户符合条件的优惠和购买选项。您的代码
在 onProductDetailsResponse() 方法中应处理返回的
优惠。
启动结算流程
如需从应用发起购买请求,请从应用的主线程调用 launchBillingFlow()
方法。此方法接受对
BillingFlowParams对象的引用,该对象包含通过调用queryProductDetailsAsync()获取的相关ProductDetails
对象。如需创建
BillingFlowParams对象,请使用BillingFlowParams.Builder
类。请注意,您必须在创建 BillingFlowParams 对象时设置与用户选择的优惠对应的优惠令牌
。
以下示例展示了如何为具有多项优惠的一次性商品启动购买流程:
Java
// An activity reference from which the billing flow will launch. Activity activity = ...; ImmutableList<ProductDetailsParams> productDetailsParamsList = ImmutableList.of( ProductDetailsParams.newBuilder() // retrieve a value for productDetails by calling queryProductDetailsAsync() .setProductDetails(productDetails) // to get an offer token, call // ProductDetails.getOneTimePurchaseOfferDetailsList() for a list of offers // that are available to the user .setOfferToken(selectedOfferToken) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
您可以在 OneTimePurchaseOfferDetails 中找到 offerToken。向用户展示优惠时,请务必使用正确的优惠令牌配置结算流程参数,您可以从
oneTimePurchaseOfferDetails.getOfferToken() 方法获取该令牌。
购买选项和优惠
借助购买选项,您可以定义向用户授予使用权的途径、商品价格以及供应商品的地区。单款商品可设有多个购买选项,这些选项可以代表您销售商品的地点和方式。
Google Play 支持以下一次性商品购买选项:
- 购买选项“购买”
- 购买选项“租借”
优惠是指您可以为一次性商品创建的定价方案。 例如,您可以为一次性商品创建折扣优惠。
Google Play 支持以下一次性商品购买优惠:
- 预订优惠(仅支持购买选项“购买”)
- 折扣优惠(支持购买选项“购买”和“租借”)
购买选项“购买”
购买选项“购买”表示一次性商品的标准购买交易。它有一个可选的 legacyCompatible 字段,用于指明此购买选项是否可在不支持新模型的旧版 Play 结算库(版本 7 或更低版本)流程中使用。为了实现向后兼容,至少应将一个购买选项“购买”标记为与旧版兼容。
将购买选项“购买”和“租借”与 PBL 集成的步骤相同。如需了解如何将购买选项“购买”与 PBL 集成, 请参阅将购买选项“租借”与 PBL 集成。
购买选项“租借”
借助购买选项“租借”,用户可以在指定的时间段内使用一次性商品。您可以指定租期及其到期时间。本文档介绍了将购买选项“租借”与 Play 结算库 (PBL) 集成的步骤。
将购买选项“租借”与 PBL 集成
本部分介绍了如何将购买选项“租借”与 Play 结算库 (PBL) 集成。本部分假定您熟悉初始 PBL 集成步骤,例如将 PBL 依赖项添加到应用、 初始化 BillingClient 以及连接到 Google Play。本部分重点介绍了特定于购买选项“租借”的 PBL 集成方面。
如需配置可供租借的商品,您需要使用 Play Developer API 的新 monetization.onetimeproducts
服务或 Play 开发者控制台界面。如需使用该服务,您可以直接调用 REST API,或
使用 Java 客户端库。
为租借选项启动购买流程
如需为租借优惠启动购买流程,请执行以下步骤:
使用
ProductDetails.oneTimePurchaseOfferDetails.getRentalDetails()方法提取购买选项“租借”的元数据。以下示例展示了如何获取购买选项“租借”的元数据:
Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult) { // check billingResult // … // process productDetailsList returned by QueryProductDetailsResult for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) { for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails : productDetails.getOneTimePurchaseOfferDetailsList()) { // Checks if the offer is a rent purchase option. if (oneTimePurchaseOfferDetails.getRentalDetails() != null) { // process the returned RentalDetails OneTimePurchaseOfferDetails.RentalDetails rentalDetails = oneTimePurchaseOfferDetails.getRentalDetails(); // Get rental period in ISO 8601 format. String rentalPeriod = rentalDetails.getRentalPeriod(); // Get rental expiration period in ISO 8601 format, if present. if (rentalDetails.getRentalExpirationPeriod() != null) { String rentalExpirationPeriod = rentalDetails.getRentalExpirationPeriod(); } // Get offer token String offerToken = oneTimePurchaseOfferDetails.getOfferToken(); // Get the associated purchase option ID if (oneTimePurchaseOfferDetails.getPurchaseOptionId() != null) { String purchaseOptionId = oneTimePurchaseOfferDetails.getPurchaseOptionId(); } } } } } });
启动结算流程。
如需从应用发起购买请求,请从应用的主线程调用
launchBillingFlow()方法。此方法 接受对BillingFlowParams对象的引用,该对象包含 通过调用queryProductDetailsAsync()获取的相关ProductDetails对象。如需创建BillingFlowParams对象,请使用BillingFlowParams.Builder类。请注意,您必须在创建BillingFlowParams对象时设置 与用户选择的优惠对应的优惠令牌。如果用户符合 购买选项“租借”的条件,他们将在queryProductDetailsAsync()中收到包含 RentalDetails 和 offerId 的优惠。以下示例展示了如何启动结算流程:
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) // Get the offer token: // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList() // for a list of offers that are available to the user. // b. For subscriptions, call ProductDetails.getSubscriptionOfferDetails() // for a list of offers that are available to the user. .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<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = ImmutableList.of( BillingFlowParams.ProductDetailsParams.newBuilder() // retrieve a value for "productDetails" by calling queryProductDetailsAsync() .setProductDetails(productDetails) // Get the offer token: // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList() // for a list of offers that are available to the user. // b. For subscriptions, call ProductDetails.getSubscriptionOfferDetails() // for a list of offers that are available to the user. .setOfferToken(selectedOfferToken) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
您可以在
OneTimePurchaseOfferDetails中找到offerToken。 向用户展示优惠时,请务必使用正确的优惠令牌配置结算流程参数,您可以从oneTimePurchaseOfferDetails.getOfferToken()方法获取该令牌。
预订优惠
借助预订功能,您可以设置一次性商品以便用户在商品发布前抢先购买。用户以预订方式购买即表示同意在商品发布时付款,除非用户在发布日期之前取消相应预订。在发布日期当天,系统会向买家收取费用,并且 Google Play 会向买家发送邮件通知,让他们知道商品发布了。
本文档介绍了将预订购买优惠与 Play 结算库 (PBL) 集成的步骤。
将预订优惠与 PBL 集成
本部分介绍了如何将预订优惠与 Play 结算库 (PBL) 集成。本部分假定您熟悉初始 PBL 集成步骤,例如将 PBL 依赖项添加到应用、 初始化 BillingClient 以及连接到 Google Play。本部分重点介绍了特定于预订优惠的 PBL 集成方面。
为预订优惠启动购买流程
如需为预订优惠启动购买流程,请执行以下步骤:
使用
ProductDetails.oneTimePurchaseOfferDetails.getPreorderDetails()方法提取预订优惠的元数据。以下示例展示了如何获取预订优惠的元数据:Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult) { // check billingResult // … // 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(); } } } } } });
启动结算流程。
如需从应用发起购买请求,请从应用的主线程调用
launchBillingFlow()方法。此方法接受对BillingFlowParams对象的引用,该对象包含通过调用 queryProductDetailsAsync()获取的相关ProductDetails对象。如需创建BillingFlowParams对象,请使用 theBillingFlowParams.Builder class。请注意,您必须在创建BillingFlowParams对象时设置与用户选择的优惠对应的优惠 令牌。如果用户符合预订优惠的条件,他们将在queryProductDetailsAsync()方法中收到包含 PreorderDetails 和 offerId 的优惠。以下示例展示了如何启动结算流程:
Java
// An activity reference from which the billing flow will launch. Activity activity = ...; ImmutableList productDetailsParamsList = ImmutableList.of( ProductDetailsParams.newBuilder() // retrieve a value for productDetails by calling queryProductDetailsAsync() .setProductDetails(productDetails) // to get an offer token, call // ProductDetails.getOneTimePurchaseOfferDetailsList() for a list of offers // that are available to the user .setOfferToken(selectedOfferToken) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
您可以在
OneTimePurchaseOfferDetails中找到offerToken。 向用户展示优惠时,请务必使用正确的优惠令牌配置结算流程参数,您可以从oneTimePurchaseOfferDetails.getOfferToken()方法获取该令牌。
折扣优惠
本部分介绍了如何为一次性商品配置折扣优惠。
您可以在一次性商品折扣优惠中配置以下四个不同的参数:
折扣优惠价格:指定有关折扣百分比或相对于原价的绝对折扣价格的详细信息。
符合条件的国家/地区:指定一次性商品优惠在某个国家/地区的可用性。
限购数量(可选):让您确定用户可以兑换同一优惠的次数。如果用户超出限购数量,则不符合优惠条件。
限时(可选):指定优惠的有效期。超出时间段后,优惠将无法购买。
检索折扣优惠价格信息
对于折扣优惠,您可以检索折扣百分比或提供的绝对折扣。
示例 1:检索折扣优惠的折扣百分比
以下示例展示了如何获取折扣优惠的原始全价及其折扣百分比。请注意,系统只会为折扣优惠返回折扣百分比信息。
Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult){ // check billingResult // … // process productDetailsList returned by QueryProductDetailsResult for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) { for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails : productDetails.getOneTimePurchaseOfferDetailsList()) { long discountedOfferPriceMicros = oneTimePurchaseOfferDetails.getPriceAmountMicros(); // process the returned fullPriceMicros and percentageDiscount. if (oneTimePurchaseOfferDetails.getFullPriceMicros() != null) { long fullPriceMicros = oneTimePurchaseOfferDetails.getFullPriceMicros(); } if (oneTimePurchaseOfferDetails.getDiscountDisplayInfo() != null) { long percentageDiscount = oneTimePurchaseOfferDetails .getDiscountDisplayInfo() .getPercentageDiscount(); } // … } } } });
示例 2:检索折扣优惠的绝对折扣
以下示例展示了如何获取折扣优惠的原始全价及其绝对折扣(以微为单位)。请注意,系统只会为折扣优惠返回绝对折扣(以微为单位)信息。对于折扣优惠,必须指定绝对折扣或折扣百分比。
Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult) { // check billingResult // … // process productDetailsList returned by QueryProductDetailsResult for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) { for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails : productDetails.getOneTimePurchaseOfferDetailsList()) { long discountedOfferPriceMicros = oneTimePurchaseOfferDetails.getPriceAmountMicros(); // process the returned fullPriceMicros and absolute DiscountAmountMicros. if (oneTimePurchaseOfferDetails.getFullPriceMicros() != null) { long fullPriceMicros = oneTimePurchaseOfferDetails.getFullPriceMicros(); } if (oneTimePurchaseOfferDetails.getDiscountDisplayInfo() != null) { long discountAmountMicros = oneTimePurchaseOfferDetails .getDiscountDisplayInfo() .getDiscountAmount() .getDiscountAmountMicros(); } // … } } } });
获取优惠的有效时间窗口
您可以使用 OneTimePurchaseOfferDetails.getValidTimeWindow()
方法获取优惠的有效时间窗口。此对象包含时间窗口的开始时间和结束时间(以毫秒为单位)。
以下示例展示了如何获取优惠的有效时间窗口:
Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult) { // check billingResult // … // process productDetailsList returned by QueryProductDetailsResult for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) { for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails : productDetails.getOneTimePurchaseOfferDetailsList()) { if (oneTimePurchaseOfferDetails.getValidTimeWindow() != null) { // process the returned startTimeMillis and endTimeMillis. ValidTimeWindow validTimeWindow = oneTimePurchaseOfferDetails.getValidTimeWindow(); long startTimeMillis = validTimeWindow.getStartTimeMillis(); long endTimeMillis = validTimeWindow.getEndTimeMillis(); // … } } } } });
折扣优惠级别的数量限制
您可以在折扣优惠级别指定数量上限,该上限仅在优惠级别应用。以下示例对此进行了说明:
- Super screensavers 为屏保商品提供了 2 项优惠:购买选项“屏保”和折扣屏保。
- 购买选项“屏保”没有设置数量限制。
- 折扣屏保的优惠级别数量上限设置为 3。
- 屏保商品没有商品级别数量上限,因此用户可以购买无限数量的此商品。
- 用户拥有 1 个折扣屏保,并计划再购买 1 个折扣屏保。
- 检索可用优惠时,购买选项“屏保”的 LimitedQuantityInfo 为 null,折扣屏保的剩余数量值为 2。
以下示例展示了如何获取折扣优惠级别的数量限制:
Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult) { // check billingResult // … // process productDetailsList returned by QueryProductDetailsResult for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) { for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails : productDetails.getOneTimePurchaseOfferDetailsList()) { if (oneTimePurchaseOfferDetails.getLimitedQuantityInfo() != null) { // process the returned maximumQuantity and remainingQuantity. LimitedQuantityInfo limitedQuantityInfo = oneTimePurchaseOfferDetails.getLimitedQuantityInfo(); int maximumQuantity = limitedQuantityInfo.getMaximumQuantity(); int remainingQuantity = limitedQuantityInfo.getRemainingQuantity(); // … } } } } });
当用户用完优惠的兑换数量上限时,getOneTimePurchaseOfferDetailsList() 方法不会返回该优惠。
计算兑换限制
以下示例展示了如何获取有关特定折扣优惠的数量限制信息。您可以获取当前用户的允许数量上限和剩余数量。请注意,数量限制功能适用于消耗型和非消耗型一次性商品优惠。 此功能仅在优惠级别受支持。
Google Play 会通过从您设置的允许数量上限中减去用户拥有的数量来计算剩余数量。在计算用户拥有的数量时,Google Play 会考虑已消耗的购买交易或待处理的购买交易。已取消、已退款或已退单的购买交易不计入用户拥有的数量。例如:
Super screensavers 设置的折扣优惠的允许数量上限为 1,因此用户最多可以购买 1 个折扣屏保。
用户购买了 1 个折扣屏保。如果用户随后尝试购买第二个折扣屏保,则会出错,并且
PurchasesUpdatedListener将收到 ITEM_UNAVAILABLE 响应代码。用户要求退还最初购买的折扣屏保,并成功收到退款。用户尝试购买 1 个折扣屏保,购买交易将成功。
国家/地区资格
您可以选择购买选项优惠或折扣优惠将面向用户的国家/地区。Google Play 将根据 Play
国家/地区评估用户资格。如果您为优惠配置了地区可用性,则只有当用户位于目标国家/地区时,该优惠才会作为 getOneTimePurchaseOfferDetailsList()
的一部分返回;否则,当您调用 queryProductDetailsAsync() 时,该优惠不会包含在返回的优惠列表中。
优惠标记
以下示例展示了如何检索与优惠关联的优惠标记。
Java
billingClient.queryProductDetailsAsync( queryProductDetailsParams, new ProductDetailsResponseListener() { public void onProductDetailsResponse( BillingResult billingResult, QueryProductDetailsResult productDetailsResult) { // check billingResult // … // process productDetailsList returned by QueryProductDetailsResult for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) { for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails : productDetails.getOneTimePurchaseOfferDetailsList()) { // process the returned offer tags. ImmutableList<String> offerTags = oneTimePurchaseOfferDetails.getOfferTagsList(); // … } } } });
优惠标记的继承
您可以为商品、购买选项或折扣优惠设置优惠标记。 折扣优惠会从其购买选项优惠继承优惠标记。 同样,如果在商品级别指定了优惠标记,购买选项优惠和折扣优惠都会继承商品优惠标记。
例如,Super screensavers 为屏保商品提供了两项优惠:购买选项“屏保”和折扣屏保。
- Super screensaver 具有商品优惠标记
SSProductTag。 - 购买选项“屏保”具有优惠标记
SSPurchaseOptionTag。 - 折扣屏保具有优惠标记
SSDiscountOfferTag。
在此示例中,购买选项优惠的 oneTimePurchaseOfferDetails.getOfferTagsList() 方法返回
SSProductTag 和 SSPurchaseOptionTag。对于折扣优惠,该方法返回 SSProductTag、SSPurchaseOptionTag
和 SSDiscountOfferTag。