SuperAPI AI 工具集

rc-plan-changes

编程与开发

查看项目

更新于 10-15 18:09

技能介绍

用途

当用户已拥有有效的 Google Play 订阅,需要将其迁移到不同的 SKU(升级、降级、跨级或试用转换)时,使用此技能。RevenueCat 通过单一的 PurchaseParams 构建器暴露 Google 的替换模式,并在服务端解析 linkedPurchaseToken 链,因此你无需编写令牌链处理代码。

前置条件

在调用计划变更前,确认以下条件:

  • 用户恰好拥有一个你打算替换的有效 Google Play 订阅。
  • 你拥有来自 Purchases.sharedInstance.awaitCustomerInfo() 的新鲜 CustomerInfo,或来自最近监听器回调的缓存值。
  • 你已从 offerings.current 解析出目标 Package(参见 fetch-offerings 技能)。
  • 通过 RevenueCat SDK 在类路径上包含 Google Play Billing Library 7+。

如果用户没有有效订阅,跳过此技能。对于全新购买,请改用 make-purchase。

选择替换模式

GoogleReplacementMode 与 Google 的计费模式一一对应。根据用户意图选择:

场景 模式 计费效果
标准升级(月付转年付) WITH_TIME_PRORATION 立即切换,剩余时间抵扣
升级,保留现有计费日期 CHARGE_PRORATED_PRICE 立即切换,现在按比例收费
切换到预付费计划或从预付费计划切换 CHARGE_FULL_PRICE 立即切换,现在全额收费
在有效免费试用期间升级 CHARGE_PRORATED_PRICE 立即切换,现在按比例收费
降级(年付转月付) DEFERRED 切换在下次续订时生效

注意:

  • 不要对试用升级默认使用 WITHOUT_PRORATION。WITHOUT_PRORATION 会立即应用新计划,但在下次续订前不收费,这会让用户获得未付费的高级访问权限。应使用 CHARGE_PRORATED_PRICE 立即收取升级费用。
  • 如果不设置模式,PurchaseParams 默认使用 WITHOUT_PRORATION。每次都要显式设置模式。
  • DEFERRED 仅对降级有效。Google 拒绝延迟升级。

执行

从 CustomerInfo 派生 currentProductId。硬编码 SKU 在用户跨计划迁移时会失效。

val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()

// activeSubscriptions 条目为 "productId:basePlanId",去掉基础计划后缀
val currentProductId = customerInfo.activeSubscriptions
    .firstOrNull()
    ?.substringBefore(":")
    ?: return  // 没有有效订阅,改为路由到 make-purchase

val newPackage = offerings.current
    ?.availablePackages
    ?.firstOrNull { it.identifier == "premium_annual_package" }
    ?: return

val params = PurchaseParams.Builder(activity, newPackage)
    .googleProductChangeInfo(
        GoogleProductChangeInfo(
            oldProductId = currentProductId,
            replacementMode = GoogleReplacementMode.WITH_TIME_PRORATION,
        )
    )
    .build()

try {
    val result = Purchases.sharedInstance.awaitPurchase(params)
    // result.customerInfo 反映新订阅
} catch (e: PurchasesTransactionException) {
    if (!e.userCancelled) showError(e.error.message)
}

关于 oldProductId 的说明:

  • 只传递订阅产品 ID。如果传递 "basic_monthly:monthly_plan",SDK 会为你去掉 :monthly_plan,但自行切分意图更清晰。
  • CustomerInfo.activeSubscriptions 使用 productId:basePlanId 形式。substringBefore(":") 可得到正确值。

验证

挂起调用返回后,读取更新后的 CustomerInfo:

  • customerInfo.activeSubscriptions 现在包含新的 productId:basePlanId。
  • customerInfo.entitlements["pro"]?.isActive 在切换期间保持 true;不要根据 SKU 字符串来限制 UI。
  • 对于 DEFERRED 模式,activeSubscriptions 在下次续订前仍报告旧产品。RevenueCat 在服务端跟踪待处理的切换,并在 Google 发送续订 RTDN 后翻转权益。

你无需编写 linkedPurchaseToken 遍历代码。RevenueCat 解析链,将旧令牌标记为已替换,并将两个令牌归属到同一 App User ID。客户端代码读取权益并信任它们。

常见错误

错误 修复
将 oldProductId 硬编码为常量 从 customerInfo.activeSubscriptions.firstOrNull()?.substringBefore(":") 派生
将 "productId:basePlanId" 作为 oldProductId 传递 用 substringBefore(":") 切掉基础计划
对试用升级使用 WITHOUT_PRORATION 使用 CHARGE_PRORATED_PRICE 以便用户现在被收费
对升级使用 DEFERRED DEFERRED 仅用于降级;Google 拒绝延迟升级
编写后端代码跟踪 linkedPurchaseToken RevenueCat 在服务端执行此操作,删除该代码
读取 SKU 来决定 UI 状态 改为读取 entitlements[...]?.isActive

限制

  • 仅适用于 Android 平台上的 Google Play 订阅。
  • 需要 Google Play Billing Library 7+。
  • DEFERRED 模式仅对降级有效。
  • 其他平台或购买类型暂未提供。