本文档详细介绍了如何将一次性商品 (OTP) 与 Play 结算库集成。本文还进一步介绍了如何集成与一次性商品相关的各种购买选项和优惠。
您可以为一次性商品配置多种购买选项和优惠。例如,您可以为同一一次性商品配置购买购买选项和预订优惠。
前提条件
如需为一次性商品配置多个优惠,您必须使用 queryProductDetailsAsync()
API。不支持已弃用的 querySkuDetailsAsync()
API。如需了解如何使用 queryProductDetailsAsync()
以及将 ProductDetailsParams
作为输入的 launchBillingFlow()
版本,请参阅迁移步骤。
查询商品详情
如果您为一次性商品配置了多项优惠或购买选项,则 queryProductDetailsAsync()
方法返回的 ProductDetails
对象可以包含多项可用的购买和(或)租借购买选项(每项一次性商品)。如需获取每个 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);
offerToken
可作为 OneTimePurchaseOfferDetails
的一部分找到。向用户展示优惠时,请务必使用可从 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.subscriptionOfferDetails() // 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<ProductDetailsParams> productDetailsParamsList = ImmutableList.of( 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.subscriptionOfferDetails() // 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);
offerToken
可在OneTimePurchaseOfferDetails
中找到。 向用户显示优惠时,请务必使用可从oneTimePurchaseOfferDetails.getOfferToken()
方法中获取的正确优惠令牌来配置结算流程参数。
预订优惠
借助预订功能,您可以设置一次性商品,以便在商品发布之前供买家购买。当用户预订您的商品时,即表示同意在商品发布时付款,除非用户在发布日期之前取消预订。在发布日期,系统会向买家收取费用,并通过电子邮件通知买家商品已发布。
本文档介绍了将预订购买优惠与 Play 结算库 (PBL) 集成的步骤。
将预订优惠与 PBL 集成
本部分介绍了如何将预订优惠与 Play Billing 库 (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
对象,请使用BillingFlowParams.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);
offerToken
可在OneTimePurchaseOfferDetails
中找到。 向用户显示优惠时,请务必使用可从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(); // … } } } } });
折扣优惠数量有限
您可以在折扣优惠一级指定数量上限,该上限仅在优惠一级适用。下面举例说明:
- 超级屏保针对屏保产品提供 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 会考虑已消耗的购买交易或待处理的购买交易。已取消、退款或退单的购买交易不会计入用户的自有数量。例如:
超级屏保设置的折扣优惠的最大允许数量为 1,因此用户最多可以购买一个折扣屏保。
用户购买了其中一款折扣屏保。如果用户随后尝试购买第二个折扣屏保,系统会出错,并且
PurchasesUpdatedListener
会收到 ITEM_UNAVAILABLE 响应代码。用户要求退还最初购买的折扣屏保,并成功收到退款。用户尝试购买其中一个折扣屏保,购买成功。
国家/地区资格条件
您可以选择向用户提供购买选项优惠或折扣优惠的国家或地区。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(); // … } } } });
优惠标记的继承
您可以为商品、购买选项或折扣优惠设置优惠标记。 折扣优惠会沿用其购买选项优惠的优惠标记。 同样,如果在商品级别指定了商品优惠标记,那么购买选项优惠和折扣优惠都会继承商品优惠标记。
例如,“超级屏保”针对屏保产品提供了两项优惠:购买选项屏保和折扣屏保。
- 超级屏保具有商品优惠标记
SSProductTag
。 - 购买选项屏保带有优惠标记
SSPurchaseOptionTag
。 - 折扣屏保带有优惠标记
SSDiscountOfferTag
。
在此示例中,购买选项优惠的 oneTimePurchaseOfferDetails.getOfferTagsList()
方法返回 SSProductTag
和 SSPurchaseOptionTag
。对于折扣优惠,该方法会返回 SSProductTag
、SSPurchaseOptionTag
和 SSDiscountOfferTag
。