PAYATHON 2026

如何在 Google Play Store 中更改订阅频率?

架构老王

结论

在应用内实现订阅升级或降级,并不会让 Google Play 的“管理订阅”页面自动出现“更改方案/更改频率”选项。Billing API 和 Play Store 的订阅管理界面是两个独立入口,Play Store 不会分析应用代码,也不会据此判断哪些订阅可以互相切换。

按照目前的 Google Play 订阅模型,如果月付和年付对应同一项会员服务,通常应将它们配置为同一个订阅产品下的两个自动续订基础方案(base plans)。基础方案发布后,还要满足地区、价格和用户资格等条件,Google Play 才可能在“管理订阅”页面为符合条件的用户显示切换入口。

是否显示该入口,最终由 Google Play 决定,开发者无法强制开启。因此,应用内仍需实现方案切换,不能完全依赖 Play Store 的管理页面。

推荐的订阅配置

如果月付和年付只是同一项会员服务的不同计费周期,建议采用以下结构:

Subscription product: premium_membership
├── Base plan: monthly
│   └── Billing period: P1M
└── Base plan: yearly
    └── Billing period: P1Y

需要在 Play Console 中确认:

  1. 月付和年付属于同一个订阅产品,而非两个相互独立的订阅产品。
  2. 两个基础方案都已激活。
  3. 两个方案在目标国家或地区均可购买,并且配置了有效价格。
  4. 用户当前使用的应用版本可以识别这些基础方案。
  5. 服务端可以处理基础方案变更后的购买状态。

如果项目仍使用旧式的独立订阅 SKU,迁移时要格外谨慎。对于仍有存量订阅用户的旧产品,不要直接删除或停用。应先确认 Google Play Console 当前支持的迁移和兼容方式。

在应用内切换订阅

在应用内切换方案时,需要查询目标基础方案的 ProductDetails,取得相应的 offerToken,再将用户当前订阅的 purchaseToken 作为旧订阅令牌传入 SubscriptionUpdateParams。

下面是简化后的 Kotlin 示例:

fun launchPlanChange(
    activity: Activity,
    billingClient: BillingClient,
    targetProductDetails: ProductDetails,
    targetOfferToken: String,
    currentPurchaseToken: String
) {
    val productDetailsParams =
        BillingFlowParams.ProductDetailsParams.newBuilder()
            .setProductDetails(targetProductDetails)
            .setOfferToken(targetOfferToken)
            .build()

    val updateParams =
        BillingFlowParams.SubscriptionUpdateParams.newBuilder()
            .setOldPurchaseToken(currentPurchaseToken)
            .setSubscriptionReplacementMode(
                BillingFlowParams.SubscriptionUpdateParams.ReplacementMode
                    .CHARGE_PRORATED_PRICE
            )
            .build()

    val billingFlowParams =
        BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(listOf(productDetailsParams))
            .setSubscriptionUpdateParams(updateParams)
            .build()

    val result = billingClient.launchBillingFlow(
        activity,
        billingFlowParams
    )

    if (result.responseCode != BillingClient.BillingResponseCode.OK) {
        // 记录错误,并根据 responseCode 向用户显示合适的提示
    }
}

示例中的 CHARGE_PRORATED_PRICE 只是其中一种替换方式,不能不加判断地用于所有场景。月付转年付、年付转月付,以及立即生效或下个周期生效,可能需要使用不同的 ReplacementMode。具体模式应根据产品规则选择,同时要在购买确认页面清楚显示扣款金额和生效时间。

选择月付或年付时,也不能依赖基础方案在数组中的位置。应通过 basePlanId、标签或后端下发的配置来识别目标方案。

Play Store 中是否会显示切换入口

订阅结构和用户状态符合要求时,Google Play 可能在以下位置显示方案切换入口:

Google Play
→ 个人资料
→ 付款和订阅
→ 订阅
→ 选择对应订阅
→ 管理

以下情况可能导致该入口不显示:

  • 月付和年付被创建为两个互不关联的订阅产品;
  • 目标基础方案尚未激活,或未在用户所在地区销售;
  • 当前订阅处于暂停、宽限期、账号保留、取消待到期等特殊状态;
  • 目标方案或优惠不适用于该用户;
  • 用户的订阅不是通过 Google Play 建立的;
  • Google Play 客户端、账号类型或商店界面暂不支持该入口;
  • Play Console 中的订阅配置尚未完成发布,或仍在传播中。

因此,不能只根据“管理订阅”页面是否出现按钮来判断应用功能是否正确。

可以提供 Play Store 管理入口

应用可以提供一个按钮,将用户引导至 Google Play 的订阅管理页面:

val uri = Uri.parse(
    "https://play.google.com/store/account/subscriptions" +
        "?sku=premium_membership" +
        "&package=com.example.app"
)

val intent = Intent(Intent.ACTION_VIEW, uri)
activity.startActivity(intent)

这里的 sku 通常填写订阅产品 ID,而不是自行拼接的基础方案 ID。该链接只能打开管理页面,不能保证页面中一定会显示方案切换功能,也不能替代应用内的 launchBillingFlow()。

服务端处理不能遗漏

无论用户是在应用内切换方案,还是通过 Google Play 管理页面操作,服务端都必须以 Google Play 返回的购买状态为准:

  • 使用 Google Play Developer API 校验购买;
  • 处理 Real-time Developer Notifications;
  • 保存当前订阅产品、基础方案和到期时间;
  • 正确处理新的购买令牌及可能存在的关联令牌;
  • 对需要确认的购买及时执行 acknowledgement;
  • 以幂等方式处理续订、取消、暂停、恢复和方案切换。

不要只通过客户端回调更新会员权益。用户从 Play Store 操作时,应用可能并未运行,客户端也不一定能立即收到回调。

实施建议

可以按以下方式实施:

  1. 将月付和年付配置为同一个订阅产品下的两个基础方案。
  2. 在应用内使用 Google Play Billing Library 实现清晰的月付和年付切换流程。
  3. 在设置页面提供跳转至 Google Play 订阅管理页的入口。
  4. 将 Play Store 自行显示的“更改方案”作为补充渠道,而非唯一渠道。
  5. 使用测试账号分别验证月转年、年转月、取消后切换及特殊付款状态。

Google Play Console 和 Billing Library 的界面及规则会不断调整。如果控制台中没有单独的“允许更改频率”开关,通常不代表遗漏了开发步骤。需要重点检查的是订阅产品与基础方案的建模是否正确。至于切换入口是否显示,则由 Google Play 根据用户资格和商店能力决定。

备注:内容仅供参考。