关于订阅

本主题介绍如何处理订阅生命周期事件,例如续订和到期。它还介绍了其他订阅功能,例如提供促销活动和允许您的用户管理他们自己的订阅。

如果您尚未为您的应用配置订阅产品,请参阅 创建和配置您的产品

订阅概述

订阅表示用户在指定时间段内可以访问的一组权益。例如,订阅可能使用户有权访问音乐流媒体服务。

您可以在同一个应用中拥有多个订阅,无论是代表不同的权益集,还是单个权益集的不同级别(例如“银色”和“金色”级别)。

通过基本计划优惠,您可以为同一个订阅产品创建多个配置。例如,您可以为从未订阅过您的应用的用户创建入门优惠。同样,您可以为已经订阅的用户创建升级优惠。

有关订阅产品、基本计划和优惠的详细信息,请参阅 Play 管理中心帮助中心 中的文档。

预付费计划集成

预付费计划不会在到期后自动续订。要无中断地延长其订阅资格,用户必须为同一订阅充值预付费计划。

对于充值,请像进行原始购买一样启动计费流程。您无需指明购买是充值。

预付费计划充值始终使用CHARGE_FULL_PRICE替换模式,您无需显式设置此模式。用户会立即被收取全额账单周期费用,并且其资格会延长充值中指定的期限。

充值后,Purchase结果对象中的以下字段将更新为反映最新的充值购买

  • 订单 ID
  • 购买时间
  • 签名
  • 购买令牌
  • 已确认

以下Purchase字段始终包含与原始购买中相同的数据

  • 包名
  • 购买状态
  • 产品
  • 自动续订

预付费购买确认

与自动续订订阅类似,您必须在购买后确认预付费计划。必须确认初始购买和任何充值。有关更多信息,请参阅 处理购买

由于预付费计划期限可能较短,因此尽快确认购买非常重要。

期限为一周或更长的预付费计划必须在三天内确认。

期限短于一周的预付费计划必须在计划期限的一半内确认。例如,开发者有 1.5 天的时间来确认三天的预付费计划。

分期付款订阅集成

分期付款订阅是一种订阅类型,用户分期付款支付订阅费用,而不是一次性支付全部订阅费用。

分期付款订阅的其他注意事项

  • 国家/地区可用性:分期付款订阅功能仅在巴西、法国、意大利和西班牙可用(请查看控制台了解最新可用性)。
  • 价格设置:在控制台中设置分期付款订阅价格时,价格代表每月付款金额。此金额与设定的承诺期限相结合,会在购买页面生成订阅总金额。
  • 承诺期限:初始订阅承诺的总时长,在此期间需要每月付款。例如,如果基础计划的承诺期限为 15 个月,则用户在此期间将支付 15 个月的费用。
  • 续订:在分期付款订阅的上下文中,“续订”表示承诺期限(初始承诺期限或后续承诺期限)的结束。在初始注册后,第一次续订发生在整个初始承诺期限结束后。后续续订发生在每个后续承诺期限结束后。“自动每月续订”或“自动以相同时长续订”是分期付款订阅的续订类型。对于“自动每月续订”,没有后续承诺,计划的行为类似于每月订阅,其中每个月订阅收费都构成一次续订。
  • 计费周期:在分期付款订阅的上下文中,这指的是基础计划中指定的定期付款间隔。
  • 计划变更与价格变更行为:对于价格变更和取消,承诺是固定的。这意味着如果用户想要取消或开发者想要更改价格,更改将在承诺期限结束时生效。对于计划变更,承诺并非固定。这意味着计划变更不必等到承诺期限结束,它会根据设定的替换模式立即或在下一个付款日期生效。
  • 相同订阅计划变更:不允许从分期付款基础计划更改为同一订阅产品的非分期付款基础计划。
  • 实时开发者通知 (RTDN):当承诺期限内仍有付款时,用户主动取消后,会立即发送 SUBSCRIPTION_CANCELLATION_SCHEDULED RTDN。取消正在进行中,只有在承诺期限结束时才会生效。然后,如果用户未恢复,则在承诺期限结束时发送 SUBSCRIPTION_CANCELEDSUBSCRIPTION_EXPIRED RTDN。

  • 支付/收入实现:开发者支付将在用户每月付款时进行,与所有其他订阅的条款相同。用户注册分期付款订阅时,开发者不会预先获得付款。

  • 错过的付款收取:如果用户未能支付任何分期付款订阅费用,Google 和开发者都不会尝试向用户收取任何此类错过的或未付的费用,但 Google 可能会根据其正常的付款重试实践,在任何适用的宽限期或账户暂停期间定期重试付款。Google 对任何剩余未支付的分期付款不承担责任。

  • Play 结算库可用性installmentDetails 字段仅适用于 PBL 7 或更高版本。对于 PBL 5 及更高版本,使用 queryProductDetails() 返回分期付款订阅,但订阅不包含计划的已承诺付款次数等详细的分期付款信息。

使用深度链接允许用户管理订阅

您的应用应在设置或偏好设置屏幕上包含一个链接,允许用户管理他们的订阅,您可以将其融入到您的应用的自然外观和风格中。

您可以从您的应用到 Google Play 订阅中心的深度链接用于非过期订阅,您可以使用 subscriptionState 字段确定 订阅资源。基于此,您可以通过多种方式深度链接到 Play 商店订阅中心。

使用以下网址将用户引导至显示其所有订阅的页面,如图 1 和图 2 所示

https://play.google.com/store/account/subscriptions
The Play Store subscriptions screen shows status for all of a user's Google Play-billed subscriptions.
图 1. Play 商店订阅屏幕显示用户所有 Google Play 计费订阅的状态。


Tap on a subscription to see additional details.
图 2. 点击订阅以查看更多详细信息。

此深度链接可能有助于用户从 Play 商店订阅中心恢复已取消的订阅。

要直接链接到非过期订阅的管理页面,请指明与已购买订阅关联的包名和 productId。要以编程方式确定现有订阅的 productId,请查询您应用的后端或调用 BillingClient.queryPurchasesAsync() 以获取与特定用户关联的订阅列表。每个订阅都包含作为订阅状态信息一部分的相应 productId。每个与订阅购买相关的 SubscriptionPurchaseLineItem 对象都包含用户在该项目中购买的订阅关联的 productId 值。

使用以下网址将用户引导至特定订阅管理屏幕,将“your-sub-product-id”和“your-app-package”分别替换为 productId 和应用包名

https://play.google.com/store/account/subscriptions?sku=your-sub-product-id&package=your-app-package

然后,用户可以管理他们的付款方式并访问包括取消、重新订阅和暂停在内的功能。

允许用户升级、降级或更改他们的订阅

您可以为现有订阅者提供各种选项来更改他们的订阅计划,以更好地满足他们的需求

  • 如果您销售多个订阅层级,例如“基础”和“高级”订阅,您可以允许用户通过购买不同订阅的基础计划或优惠来切换层级。
  • 您可以允许用户更改其当前计费周期,例如从月度计划切换到年度计划。
  • 您还可以允许用户在自动续订和预付费计划之间切换。

您可以通过提供订阅优惠来鼓励任何这些更改,以便为符合条件的用户提供折扣。例如,您可以创建一个优惠,在从月度计划切换到年度计划时提供第一年 50% 的折扣,并将此优惠限制为订阅了月度计划且未购买此优惠的用户。有关优惠资格标准的更多信息,请访问 帮助中心

图 3 显示了一个带有三个不同计划的示例应用

This app has three subscription tiers..
图 3. 此应用具有三个订阅层级。

您的应用可以显示类似于图 3 的屏幕,让用户可以选择更改他们的订阅。在所有情况下,都应向用户清楚地说明他们当前的订阅计划以及他们可以更改它的选项。

当用户决定升级、降级或更改他们的订阅时,您将指定一个替换模式,该模式决定如何应用当前已支付计费周期的按比例分配的值,以及何时发生任何权利变更。

替换模式

下表列出了可用的替换模式和示例用法,以及被视为已支付的付款次数。

替换模式

描述

示例用法

记录为已支付的已承诺付款(对于分期付款订阅替换)

WITH_TIME_PRORATION

订阅会立即升级或降级。任何剩余时间都会根据价格差异进行调整,并通过提前下一个计费日期来记入新订阅。这是默认行为。

升级到更昂贵的层级,无需任何立即的额外付款。

0

CHARGE_PRORATED_PRICE

订阅会立即升级,计费周期保持不变。然后向用户收取剩余期间的价格差异。

注意:此选项仅适用于订阅升级,其中每单位时间的价格会增加。

升级到更昂贵的层级,无需更改计费日期。

1

CHARGE_FULL_PRICE

订阅会立即升级或降级,并且系统会立即向用户收取新权利的全部价格。先前订阅的剩余价值将用于相同的权利,或者在切换到不同的权利时按时间比例分配。

注意:如果新订阅有免费试用或入门优惠,则在升级或降级时,用户将被收取 0 美元或入门优惠的价格(以适用者为准)。

从较短的计费周期升级到较长的计费周期。

1(注意:如果新订阅有免费试用,则为 0。)

WITHOUT_PRORATION

订阅会立即升级或降级,并在订阅续订时收取新价格。计费周期保持不变。

升级到更高的订阅层级,同时保留任何剩余的免费期限。

0

DEFERRED

订阅仅在订阅续订时升级或降级,但新购买会立即发出,未来开始日期为新权利,因此开发者可以允许用户根据需要进行其他更改。例如,他们可以恢复到原始计划或启动新的延期计划更改。注意:对于分期付款订阅,计划更改发生在下一个付款日期开始时。

降级到较便宜的层级。

1

要了解有关升级或降级优惠的不同上销和赢回应用的更多信息,请阅读优惠和促销指南。

设置购买的替换模式

您可以根据您的偏好和业务逻辑,对不同类型的订阅转换使用不同的替换模式。本节说明如何为订阅更改设置替换模式以及适用的限制。

在同一订阅内重新订阅或切换套餐

您可以在 Google Play Console 中指定默认替换模式。此设置允许您选择在当前订阅者购买同一订阅的不同基础套餐或优惠,或取消后重新订阅时,何时向其收费。可用选项为 *立即收费*,等效于 CHARGE_FULL_PRICE,以及 *在下个计费日期收费*,等效于 WITHOUT_PRORATION。这些是在同一订阅内切换基础套餐时唯一相关的替换模式。

例如,如果您正在为用户取消后但在订阅结束前实施同一套餐的回馈优惠,您可以将新的购买处理为常规购买,无需在 SubscriptionUpdateParams 中指示任何值。系统将使用您在订阅中配置的默认替换模式,并自动处理从旧购买到新购买的套餐转换。

跨订阅切换套餐,或覆盖默认替换模式

如果用户正在更改订阅 *产品—购买* 不同的订阅,或者您出于任何原因想要覆盖默认替换模式,则您需要在运行时作为购买流程参数的一部分指定分摊比率。

为了正确地将 SubscriptionUpdateParams 作为运行时购买流程配置的一部分提供,请注意以下限制

  • 在将预付费套餐升级、降级或启动同一订阅切换 **到** 预付费套餐 **从** 预付费套餐、自动续订套餐或分期付款套餐时,唯一允许的替换模式是 CHARGE_FULL_PRICE。如果您指定任何其他替换模式,则购买将失败,并且会向用户显示错误。
  • 在同一订阅内将自动续订套餐 **切换到** 自动续订套餐 **从** 预付费套餐或自动续订套餐时,有效的分摊模式是 CHARGE_FULL_PRICEWITHOUT_PRORATION。如果您指定任何其他分摊模式,则购买将失败,并且会向用户显示错误。
  • 不允许在同一订阅产品中将分期付款基础套餐切换到非分期付款基础套餐。

替换示例和行为

要了解每种分摊模式的工作原理,请考虑以下场景

Samwise 订阅了 Country Gardener 应用的在线内容。他每月订阅内容的 **第一层** 版本,该版本仅包含文本。此订阅每月花费他 **2 美元**,并在每月第一天续订。

4 月 15 日,Samwise 选择升级到 **第二层** 订阅的年度版本,其中包括视频更新,每年花费 **36 美元**。

升级订阅时,开发者会选择分摊模式。以下列表描述每种分摊模式如何影响 Samwise 的订阅

WITH_TIME_PRORATION

Samwise 的 **第一层** 订阅立即结束。因为他支付了一个月的费用(4 月 1 日至 30 日),但在订阅期间的中途进行了升级,所以半个月的订阅费用(1 美元)将计入他的新订阅。但是,由于新的订阅每年花费 36 美元,因此 1 美元的信用余额只能支付 10 天(4 月 16 日至 25 日);因此,在 4 月 26 日,他将被收取 36 美元的新订阅费用,以及以后每年的 4 月 26 日的另外 36 美元。

购买成功后,您应该立即调用应用的 PurchasesUpdatedListener,并且您能够在 queryPurchasesAsync() 调用中检索新的购买。您的后端会立即收到 SUBSCRIPTION_PURCHASED 实时开发者通知。

CHARGE_PRORATED_PRICE

可以使用此模式,因为 **第二层** 订阅的每单位时间价格(36 美元/年 = 3 美元/月)大于 **第一层** 订阅的每单位时间价格(2 美元/月)。Samwise 的 **第一层** 订阅立即结束。因为他支付了一个月的费用,但只使用了其中的一半,所以半个月的订阅费用(1 美元)将计入他的新订阅。但是,由于新的订阅每年花费 36 美元,剩余 15 天的费用为 1.50 美元;因此,他将被收取 0.50 美元的新订阅费用差额。5 月 1 日,Samwise 将被收取 36 美元的新订阅层费用,以及以后每年的 5 月 1 日的另外 36 美元。

购买成功后,您应该立即调用应用的 PurchasesUpdatedListener,并且您能够在 queryPurchasesAsync() 调用中检索新的购买。您的后端会立即收到 SUBSCRIPTION_PURCHASED 实时开发者通知。

WITHOUT_PRORATION

Samwise 的 **第一层** 订阅立即升级到 **第二层**,无需额外收费,5 月 1 日,他将被收取 36 美元的新订阅层费用,以及以后每年的 5 月 1 日的另外 36 美元。

购买成功后,您应该立即调用应用的 PurchasesUpdatedListener,并且您能够在 queryPurchasesAsync() 调用中检索新的购买。您的后端会立即收到 SUBSCRIPTION_PURCHASED 实时开发者通知。

DEFERRED

Samwise 的 **第一层** 订阅持续到 4 月 30 日到期。5 月 1 日,**第二层** 订阅生效,Samwise 将被收取 36 美元的新订阅层费用。

购买成功后,您应该立即调用应用的 PurchasesUpdatedListener,并且您能够在 queryPurchasesAsync() 调用中检索新的购买。您的后端会立即收到 SUBSCRIPTION_PURCHASED 实时开发者通知。您应该 处理购买,方法与处理任何其他新购买相同。尤其要确保您已确认新购买。请注意,新订阅的 startTime 在替换生效时填充,这发生在旧订阅到期时。此时,您会收到新订阅套餐的 SUBSCRIPTION_RENEWED RTDN。阅读 处理延迟替换 中有关 ReplacementMode.DEFERRED 行为的更多信息。

CHARGE_FULL_PRICE

Samwise 的 **第一层** 订阅立即结束。他的 **第二层** 订阅从今天开始,他将被收取 36 美元。因为他支付了一个月的费用,但只使用了其中的一半,所以半个月的订阅费用(1 美元)将计入他的新订阅。由于新的订阅每年花费 36 美元,他将获得 1/36 年的订阅期延长(约 10 天)。因此,Samwise 的下次收费将是今天起一年零十天后,费用为 36 美元。之后,他每年将被收取 36 美元。

选择分摊模式时,请务必查看我们的 替换建议

在应用内触发订阅更改

您的应用可以使用与 启动购买流程 相同的步骤为用户提供升级或降级服务。但是,在升级或降级时,您需要提供当前订阅、未来(升级或降级)订阅以及要使用的替换模式的详细信息,如下例所示

Kotlin

val offerToken = productDetails
        .getSubscriptionOfferDetails(selectedOfferIndex)
        .getOfferToken()

val billingParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(
       listOf(
           BillingFlowParams.ProductDetailsParams.newBuilder()
               .setProductDetails(productDetails)
               .setOfferToken(offerToken)
               .build()
       )
       ).setSubscriptionUpdateParams(
           BillingFlowParams.SubscriptionUpdateParams.newBuilder()
               .setOldPurchaseToken("old_purchase_token")
               .setSubscriptionReplacementMode(
                 BillingFlowParams.ReplacementMode.CHARGE_FULL_PRICE
               )
               .build()
       ).build()

billingClient.launchBillingFlow(
    activity,
    billingParams
   )
// ...

Java

String offerToken = productDetails
    .getSubscriptionOfferDetails(selectedOfferIndex)
    .getOfferToken();

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        ImmuableList.of(
            ProductDetailsParams.newBuilder()
                // fetched via queryProductDetailsAsync
                .setProductDetails(productDetails)
                // offerToken can be found in
                // ProductDetails=>SubscriptionOfferDetails
                .setOfferToken(offerToken)
                .build()))
    .setSubscriptionUpdateParams(
        SubscriptionUpdateParams.newBuilder()
            // purchaseToken can be found in Purchase#getPurchaseToken
            .setOldPurchaseToken("old_purchase_token")
            .setSubscriptionReplacementMode(ReplacementMode.CHARGE_FULL_PRICE)
            .build())
    .build();

BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
// ...

替换建议

下表显示了不同的分摊方案以及我们对每种方案的建议

场景 推荐的替换模式 结果
升级到更昂贵的层级 CHARGE_PRORATED_PRICE 用户立即获得访问权限,同时保持相同的计费周期。
降级到更便宜的层级 DEFERRED 用户已经为更昂贵的层级付费,因此他们可以一直访问到下一个计费日期。
在免费试用期间升级,保留试用版 WITHOUT_PRORATION 用户保留免费试用访问权限,但在试用剩余时间内升级到更高层级。
在免费试用期间升级 - 结束免费试用访问权限 CHARGE_PRORATED_PRICE 用户立即获得新层级的访问权限,但不再享受免费试用。

处理订阅更改购买

就所有条款和目的而言,套餐更改都是新的购买,并且在计费流程成功完成之后,应像处理新的购买一样对其进行处理和确认。除了适当处理新购买外,您还必须停用被替换的购买。

应用内行为与任何新购买相同。您的应用会在您的 PurchasesUpdatedListener 中收到新购买的结果,并且新购买在 queryPurchasesAsync 中可用。

当购买替换现有购买时,Google Play 开发者 API 会在 订阅资源 中返回 linkedPurchaseToken。请务必使 linkedPurchaseToken 中提供的令牌无效,以确保旧令牌不会用于访问您的服务。有关处理升级和降级购买的信息,请参阅 升级、降级和重新注册

收到新的购买令牌后,请遵循与 验证新的购买令牌 相同的验证流程。请务必使用 Google Play 结算库中的 BillingClient.acknowledgePurchase() 或 Google Play 开发者 API 中的 Purchases.subscriptions:acknowledge 确认这些购买。

处理延迟替换

延迟替换模式允许您让用户在开始使用新套餐之前用完旧套餐中剩余的权利。

当您对新购买使用 ReplacementMode.DEFERRED 时,queryPurchasesAsync() 会在购买流程后返回一个新的购买令牌,该令牌与旧产品相关联,直到下一个续订日期进行延迟替换,之后将返回新产品。

过去,您可以使用已弃用的 ProrationMode.DEFERRED 来实现这种用户体验,但 ProrationMode.DEFERRED 在 Play 结算库 6 中已弃用。请参见下表,了解行为差异之处

时间

ProrationMode.DEFERRED(已弃用)

ReplacementMode.DEFERRED

购买流程成功后立即(应用)

PurchasesUpdatedListener 在购买后调用,状态为升级或降级是否成功。

对旧套餐的权利持续到下一个续订日期。为了确保应用提供正确的权利,queryPurchasesAsync() 返回一个 Purchase 对象,其中包含 **原始** 购买令牌和 **原始** 权利,直到替换发生。

不会显示新的购买令牌,因此此时无法对其进行处理。

PurchasesUpdatedListener 在购买后调用,状态为升级或降级是否成功。

queryPurchasesAsync() 会立即返回带有**新**购买令牌的购买信息以及与其关联的**原始权益**。

新的购买令牌已显示,因此应该立即处理,同时考虑何时进行替换。

购买流程成功后(后端)

购买流程结束后**不会**发送 SUBSCRIPTION_PURCHASED RTDN。后端尚未意识到新的购买。

带有旧 product_id 的 SUBSCRIPTION_PURCHASED RTDN 会在新购买令牌的购买流程结束后立即发送。

使用新的购买令牌调用purchases.subscriptionsv2.get 方法会返回一个包含**两项**的购买信息,其中'startTime'指示购买时间。

  • 一项代表**旧**权益,并且具有未来的'expiryTime'。旧权益不会续订,并且具有一个DeferredItemReplacement,其中包含**新**权益的产品。这表示在旧权益到期后将对其进行替换。
  • 一项代表**新购买的**权益。它的'expiryTime'字段未设置值。

针对**旧**购买令牌发送 SUBSCRIPTION_EXPIRED。使用**旧**购买令牌调用purchases.subscriptionsv2.get 方法时,它显示为已过期(旧计划的权益已转移到新的购买中,剩余时间有效)。

替换 - 购买流程后的首次续订(应用)

queryPurchasesAsync() 返回一个新的 Purchase 对象,其中包含**新**购买令牌和权益。

新的购买令牌现在已显示,因此应该处理

queryPurchasesAsync() 会立即返回带有**新**购买令牌的购买信息以及与其关联的**新权益**。

购买流程成功后,应该已经处理了新的购买,因此应用除了确保授予正确的权益外,无需采取任何特殊操作。

替换 - 购买流程后的首次续订(后端)

发送第一个 SUBSCRIPTION_RENEWED RTDN 后,现在可以处理并确认新的购买。

订阅资源中的linkedPurchaseToken 可用于确定应更新哪些用户(如果适用)的订阅后端,以获得新的权益。

发送新购买令牌的 SUBSCRIPTION_PURCHASED RTDN 后,已处理并确认新的购买,并将其记录为'startTime'。

使用 ReplacementMode.DEFERRED 时,第一次续订遵循任何其他续订的标准行为,在此事件发生时,您无需处理替换的特殊逻辑。

使用新的购买令牌调用purchases.subscriptionsv2.get 方法会返回一个包含**两项**的购买信息。

  • 一项代表**旧**权益,其`expiryTime`在过去,并且DeferredItemReplacement未设置值。
  • 一项代表**新**权益,其`expiryTime`在未来,并且auto_renewing_enabled标志已启用。

现在应该使用 ReplacementMode.DEFERRED 代替已弃用的 ProrationMode.DEFERRED,因为它在权益更改方面表现出相同的行为,但提供了一种更符合其他新购买行为的购买管理方式。

客户管理

使用实时开发者通知,您可以实时检测用户何时决定取消订阅。当用户取消订阅但在订阅到期之前,您可以向他们发送推送通知或应用内消息,以请求他们重新订阅。

用户取消订阅后,您可以在您的应用中或通过 Play 商店尝试将其赢回。下表描述了各种订阅场景以及相关的赢回操作和应用需求。

订阅到期前 订阅到期后
应用内 Play 商店 应用内 Play 商店
赢回功能 应用内订阅 恢复 应用内订阅 重新订阅
用户完成结账流程
用户订阅与相同的 SKU 关联 用户可以注册相同或不同的 SKU 用户可以注册相同或不同的 SKU
创建新的购买令牌
默认启用 是,所有开发者都需要支持

没有 Billing 库 2.0+ 的应用:否

有 Billing 库 2.0+ 的应用:是。开发者可以在控制台中选择退出。

向用户收费时

如果使用相同的 SKU:当前计费周期结束。

如果使用不同的 SKU:取决于分摊模式。

当前计费周期结束 立即 立即
需要实现 在您的应用中提供重新注册 UI

检测订阅状态的变化

深度链接到 Play 商店

在您的应用中提供重新注册 UI 处理应用外购买

订阅到期前 - 应用内

对于已取消但尚未过期的订阅,您可以允许订阅者通过应用与新订阅者相同的应用内产品购买流程来在您的应用中恢复其订阅。确保您的 UI 反映用户拥有现有订阅。例如,您可能需要显示用户的当前到期日期和循环价格以及一个**重新激活**按钮。

大多数情况下,您希望为用户提供他们已订阅的相同价格和 SKU,如下所示

  • 使用相同的 SKU 启动新的订阅购买。
  • 新订阅将替换旧订阅,并在相同的到期日期续订。旧订阅会立即标记为已过期。
  • 例如,Achilles 订阅了 Example Music App,订阅将于 8 月 1 日到期。7 月 10 日,他以每月相同的价格重新订阅了一个月的订阅。新订阅将按比例分配剩余信用额,立即生效,并在 8 月 1 日续订。

如果您想提供不同的价格,例如新的免费试用或赢回折扣,您可以改为向用户提供不同的 SKU

  • 使用替换模式WITHOUT_PRORATION启动升级或降级,使用不同的 SKU。
  • 新订阅将替换旧订阅,并在相同的到期日期续订。用户将在原始到期日期支付新 SKU 的价格,包括任何介绍性价格。如果旧订阅是使用模糊的帐户 ID 创建的,则应将相同的 ID 传递给升级和降级的BillingFlowParams
  • 例如,Achilles 订阅了 Example Music App,订阅将于 8 月 1 日到期。7 月 10 日,他重新订阅了具有优惠价格的年度订阅。新订阅立即生效,用户将在 8 月 1 日支付优惠价格。
  • 如果您决定在赢回 SKU 中包含免费试用或优惠价格,请确保用户有资格,方法是取消选中 Google Play Console 中的**每个应用允许一次免费试用**框,这会限制用户每个应用只能获得一次免费试用。

收到购买令牌后,处理购买,就像处理新的订阅一样。此外,Google Play 开发者 API 会在订阅资源中返回linkedPurchaseToken。请务必使linkedPurchaseToken中提供的令牌无效,以确保旧令牌不会用于访问您的服务。

订阅到期前 - Play 商店

虽然订阅已取消但仍处于活动状态,但用户可以通过点击**重新订阅**(以前为**恢复**)来在 Google Play 订阅中心恢复订阅。这将保留相同的订阅和购买令牌。

subscriptions section in the google play store app showing a
            cancelled subscription with a resubscribe button
图 8. Google Play 商店应用中显示已取消订阅且带有**重新订阅**按钮的帐户>订阅部分。

有关恢复订阅的更多信息,请参阅恢复

订阅到期后 - 应用内

您可以允许已过期订阅者通过应用与新订阅者相同的应用内产品购买流程来在您的应用中重新订阅。请注意以下几点:

  • 为了向用户提供折扣,您可能希望为您的订阅提供具有特殊价格的产品 ID,也称为赢回 SKU。您可以在您的应用中提供优惠,也可以在应用外(例如在电子邮件中)通知用户优惠。
  • 要启动赢回订阅,请使用 Google Play 结算库在您的 Android 应用中启动购买流程。这与新的订阅相同,但您可以确定对用户可用的 SKU。
  • 如果您决定在赢回 SKU 中包含免费试用或优惠价格,请确保用户有资格,方法是取消选中 Google Play Console 中的**每个应用允许一次免费试用**框,这会限制用户每个应用只能获得一次免费试用。
  • 如果用户重新订阅相同的 SKU,他们将不再有资格获得免费试用或优惠价格。确保您的 UI 反映这一点。

收到购买令牌后,处理购买,就像处理新的订阅一样。您不会在订阅资源中收到linkedPurchaseToken

订阅到期后 - Play 商店

如果启用,用户可以在到期后一年内通过点击 Google Play 订阅中心中的**重新订阅**来重新订阅相同的 SKU。这将生成新的订阅和购买令牌。

subscriptions section in the google play store app showing a
            cancelled and expired subscription with resubscribe and remove
            buttons
图 9. Google Play 商店应用中显示已取消且已过期的订阅以及**重新订阅**和**移除**按钮的帐户>订阅部分。

重新订阅被视为应用外购买,因此请务必遵循处理应用外购买的最佳实践

推广您的订阅

您可以创建促销代码,以向选定的用户提供现有订阅的延长免费试用期。要了解更多信息,请参阅促销代码

对于免费试用,Google Play 会在开始免费试用之前验证用户是否有有效的付款方式。某些用户可能会将其验证视为其付款方式上的预授权或收费。此预授权或收费是临时的,稍后会被撤销或退款。

试用期结束后,用户的付款方式将被收取全额订阅费用。

如果用户在免费试用期间的任何时间取消订阅,则订阅将保持活动状态直到试用结束,并且在免费试用期结束时不会向他们收费。

取消、退款或撤销

您可以使用Google Play 开发者 API取消退款撤销订阅。此功能也适用于Google Play Console

  • 取消:用户可以在 Google Play 上取消订阅。您也可以在您的应用或网站上为用户提供取消选项。您的应用应按照取消订阅中所述处理这些取消。
  • 退款:退款后,用户可以继续使用订阅。例如,如果由于技术错误导致用户无法访问您的产品,但该错误已解决,则可以使用退款。请注意,要退款超过最近一次付款,或者如果您想进行部分退款,则必须使用 Google Play Console。
  • 撤销:撤销后,用户将立即失去对订阅的访问权限。例如,如果由于技术错误导致用户无法访问您的产品,并且用户不想继续使用该产品,则可以使用此功能。您的应用应按照撤销订阅中所述处理这些取消。

下表说明了取消、退款和撤销之间的区别。

停止续订 退款 撤销访问权限
取消
退款
撤销

为订阅者延迟计费

您可以使用 Google Play 开发者 API 中的Purchases.subscriptions:defer提前自动续订订阅者的下一个计费日期。在延迟期间,用户已订阅您的内容并拥有完全访问权限,但不会被收取费用。订阅续订日期将更新以反映新日期。

对于预付费计划,您可以使用延迟计费 API 来延迟到期时间。

延迟计费允许您执行以下操作:

  • 向用户提供免费访问权限作为特别优惠,例如购买电影后免费赠送一周。
  • 作为善意姿态向客户提供免费访问权限。

每次 API 调用最多可以延迟一天,最长可以延迟一年。要进一步延迟计费,您可以在新的计费日期到达之前再次调用 API。

例如,Darcy 订阅了 Fishing Quarterly 应用的在线内容月度订阅。她通常在每个月的第一天支付 1.25 英镑。三月份,她参与了该应用发布者的在线调查。发布者通过将下一次付款延迟到 5 月 15 日(比她之前计划的 4 月 1 日的计费日期晚六周)来奖励她六周的免费使用权。Darcy 不用支付四月份和五月初的费用,并且仍然可以访问内容。5 月 15 日,她将支付正常的 1.25 英镑月度订阅费。她的下一个续订日期现在是 6 月 15 日。

延迟时,您可能需要通过电子邮件或在应用内通知用户其计费日期已更改。

处理付款失败

如果订阅续订存在付款问题,Google 将定期尝试在取消之前一段时间内续订订阅。此恢复期可以包含宽限期,然后是帐户暂停期。在此期间,Google 会向用户发送电子邮件和通知,提示他们更新其付款方式。

付款失败后,如果配置了宽限期,订阅将进入宽限期。在宽限期内,您应确保用户仍然可以访问订阅权限。

任何宽限期结束后,订阅将进入帐户暂停期。在帐户暂停期间,您应确保用户无法访问订阅权限。

您可以在 Google Play Console 中指定每个自动续订基本计划的宽限期和帐户暂停期的长度。指定小于默认值的长度可能会减少从付款失败中恢复的订阅数量。

为了最大限度地提高付款失败期间订阅恢复的可能性,您可以告知用户付款问题并要求他们解决。

您可以自己执行此操作,如宽限期帐户暂停部分所述,也可以实施应用内消息 API,Google 会在您的应用中向用户显示消息。

应用内消息

如果您已使用InAppMessageCategoryId.TRANSACTIONAL启用应用内消息,Google Play 将在宽限期和帐户暂停期间每天向用户显示一次消息,并为他们提供机会在不离开应用的情况下解决付款问题。

Snackbar notifying the user to fix their payment
图 20. 通知用户修复付款的 Snackbar。

我们建议您在用户打开应用时随时调用此 API,以确定是否应显示消息。

如果用户成功恢复了订阅,您将收到SUBSCRIPTION_STATUS_UPDATED响应代码以及购买令牌。然后,您应该使用此购买令牌调用 Google Play 开发者 API 并刷新应用中的订阅状态。

集成应用内消息

要向用户显示应用内消息,请使用BillingClient.showInAppMessages()

这是一个触发应用内消息流程的示例

Kotlin

val inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build()

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        object : InAppMessageResponseListener() {
            override fun onInAppMessageResponse(inAppMessageResult: InAppMessageResult) {
                if (inAppMessageResult.responseCode == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        })

Java

InAppMessageParams inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build();

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        new InAppMessageResponseListener() {
            @Override
            public void onInAppMessageResponse(InAppMessageResult inAppMessageResult) {
                if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        });

处理订阅挂起的交易

挂起的交易可能发生在初始购买、充值、升级或降级中。订阅购买从SUBSCRIPTION_STATE_PENDING状态开始,然后转换为SUBSCRIPTION_STATE_ACTIVE状态。如果交易过期或被用户取消,则会转到SUBSCRIPTION_STATE_PENDING_PURCHASE_EXPIRED状态。您必须并且应该仅在交易完成后才更新用户的权利。

带有挂起交易的初始购买的订阅状态更改很简单。当用户启动挂起交易时,您的应用会收到带有PENDING状态的Purchase。交易完成后,您的应用会再次收到Purchase,其状态更新为PURCHASED。带有类型SUBSCRIPTION_PURCHASEDSubscriptionNotification消息将发送到您的 RTDN 客户端。按照正常流程验证购买,允许用户访问内容并确认购买。如果交易过期或被取消,则带有类型SUBSCRIPTION_PENDING_PURCHASE_CANCELEDSubscriptionNotification消息将发送到您的 RTDN 客户端。在这种情况下,用户不应获得对内容的访问权限。

带有挂起交易的充值、升级或降级涉及旧订阅和新订阅的状态更改。当用户启动挂起的充值、升级或降级交易时,您的应用会收到带有PendingPurchaseUpdate对象的旧订阅的Purchase。此时,用户仍然拥有旧订阅,并且尚未获得新订阅。在PendingPurchaseUpdate对象上调用getProducts()getPurchaseToken()将返回新订阅的产品 ID 和购买令牌。交易完成后,您的应用会收到带有为新订阅设置的顶级购买令牌且状态设置为PURCHASEDPurchase。带有类型SUBSCRIPTION_PURCHASEDSubscriptionNotification消息将发送到您的 RTDN 客户端。只有此时,您才应将旧购买令牌替换为新购买令牌并更新用户对内容的访问权限。如果交易过期或被取消,则带有类型SUBSCRIPTION_PENDING_PURCHASE_CANCELEDSubscriptionNotification消息将发送到您的 RTDN 客户端。在这种情况下,用户仍应可以访问旧订阅的内容。