mcpField
R
rc-plan-changes

rc-plan-changes

github.com/revenuecat开发76641 次安装1 天前更新
代码仓库

关于 rc-plan-changes

面向 Android 开发者的 RevenueCat Google Play 活跃订阅变更指南,介绍替换模式、从 CustomerInfo 获取当前产品,以及变更后的状态验证。

主要功能

  1. 01选择 Google 替换模式
  2. 02从 CustomerInfo 获取产品 ID
  3. 03说明升级与降级的生效时机
  4. 04使用 RevenueCat 处理令牌链
  5. 05验证变更后的权益状态

适用场景

  1. 01将月度计划升级为年度计划
  2. 02安排在续费时降级
  3. 03将免费试用转换为付费计划

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/revenuecat/ai-toolkit --skill rc-plan-changes

安装后在 Claude Code 中输入/rc-plan-changes确认已加载。

详细介绍

在 Android 上使用 RevenueCat 更改套餐

当用户已有有效的 Google Play 订阅,而你需要将其切换到其他 SKU(升级、降级、跨级切换或试用转化)时,请使用此技能。RevenueCat 通过单个 PurchaseParams 构建器提供 Google 的替换模式,并在服务器端解析 linkedPurchaseToken 链,因此你无需编写令牌链代码。

阶段 1:前提条件

调用套餐更改前,请确认以下事项:

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

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

阶段 2:规划(选择替换模式)

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 会拒绝延迟升级。

阶段 3:执行

从 CustomerInfo 推导 currentProductId。用户切换套餐后,硬编码的 SKU 会出错。

val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()

// activeSubscriptions entries are "productId:basePlanId", strip the base plan suffix
val currentProductId = customerInfo.activeSubscriptions
    .firstOrNull()
    ?.substringBefore(":")
    ?: return  // nothing active, route to make-purchase instead

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 reflects the new subscription
} catch (e: PurchasesTransactionException) {
    if (!e.userCancelled) showError(e.error.message)
}

关于 oldProductId 的说明:

  • 只传入订阅产品 ID。如果传入 "basic_monthly:monthly_plan",SDK 会为你去掉 :monthly_plan,但自行截取会让意图更清晰。
  • CustomerInfo.activeSubscriptions 使用 productId:basePlanId 的格式。substringBefore(":") 可获得正确的值。

阶段 4:验证

挂起调用返回后,读取更新后的 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,让用户立即付费
为升级使用 DEFERREDDEFERRED 仅用于降级;Google 会拒绝延期升级
编写后端代码来跟踪 linkedPurchaseTokenRevenueCat 会在服务端处理,删除这段代码
读取 SKU 来判断 UI 状态改为读取 entitlements[...]?.isActive

参考资料

评论

支持 Markdown