关于 rc-subscription-states
面向 Android 开发者的 RevenueCat 订阅状态处理技能,介绍如何判断用户是否有权访问功能并更新界面。内容涵盖权益字段、缓存或实时获取的 CustomerInfo,以及更新监听器。
主要功能
- 01依据权益激活状态控制访问
- 02读取续订、到期和账单状态
- 03使用缓存或实时获取的 CustomerInfo
- 04通过客户信息监听器更新界面
- 05处理已识别用户与匿名用户
适用场景
- 01根据订阅访问权限显示高级内容
- 02展示账单问题或到期提示
- 03服务器端授权后刷新访问状态
- 04购买或恢复后同步访问界面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ revenuecat/ ai-toolkit --skill rc-subscription-states安装后在 Claude Code 中输入/rc-subscription-states确认已加载。
详细介绍
Subscription States
在 Android 上使用 CustomerInfo 和 EntitlementInfo 判断用户是否有权限,并驱动能够感知状态的 UI。
Phase 1: Discover
编写代码前,确认你实际要检查的内容。
- 哪个权益标识符控制此功能?(例如
pro_access) - 你只需要一个简单的权限布尔值,还是也需要说明用户拥有或没有权限的原因(付款问题、已取消但仍处于付费期、已暂停)?
- 你需要缓存值(速度快,但可能过时),还是需要最新获取的值(以服务器为准)?
- 你是在启动时刷新一次 UI,还是要响应实时变化(购买、恢复、后台刷新)?
如果你只需要检查是否有权限,只需使用 isActive。其他内容都是可选的上下文信息。
Phase 2: Plan
Google 的七种状态与 RevenueCat 的布尔值
使用 Google Play Developer API 自行实现追踪器,意味着要映射七种订阅状态,并决定哪些状态授予权限。
| Google 状态 | 是否授予权限? |
|---|---|
| ACTIVE | 是 |
| IN_GRACE_PERIOD | 是 |
CANCELED(expirationDate 之前) | 是 |
| ON_HOLD | 否 |
| PAUSED | 否 |
| EXPIRED | 否 |
| PENDING | 否 |
RevenueCat 会在后端根据 Google SubscriptionPurchaseV2 资源计算此结果,并将结果作为一个字段提供:EntitlementInfo.isActive。你读取一个布尔值即可,无需自行实现状态机。
读取什么,以及何时读取
| 需求 | 字段 |
|---|---|
| 用户现在是否有权限? | entitlement.isActive |
| 订阅是否会在周期结束时续订? | entitlement.willRenew |
| 付费权限何时结束? | entitlement.expirationDate |
| 当前是试用期、入门优惠期、预付费期还是正常周期? | entitlement.periodType |
| 是否存在付款问题? | entitlement.billingIssueDetectedAt |
| 用户是否已取消,但仍有剩余时间? | entitlement.unsubscribeDetectedAt |
| 哪个商店授予了此权益? | entitlement.store |
判断规则
- 仅在
isActive == true时开放功能权限,不要依赖其他条件。 - 使用
billingIssueDetectedAt != null显示修复付款问题的提示。 - 使用
unsubscribeDetectedAt != null和expirationDate,在权限仍有效时显示续订提醒。 - 在没有付款问题且没有明确取消时间戳的情况下,使用
!willRenew显示不续订通知。
Phase 3: Execute
读取 CustomerInfo 并检查权限
val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()
val hasAccess = customerInfo.entitlements["pro_access"]?.isActive == true
awaitCustomerInfo() 会立即返回磁盘缓存,随后在后台从网络刷新。即使离线,权益检查也能保持快速。
必须时强制获取最新数据
在服务器端授予权限后使用(例如,支持人员发放了促销权益)。
val fresh = Purchases.sharedInstance.awaitCustomerInfo(
fetchPolicy = CacheFetchPolicy.FETCH_CURRENT
)
驱动能够感知状态的 UI
使用 isActive 控制权限;其他字段用于说明上下文。
fun updateUI(entitlement: EntitlementInfo?) {
if (entitlement == null || !entitlement.isActive) {
showSubscribeScreen(); return
}
showPremiumContent()
when {
entitlement.billingIssueDetectedAt != null -> showBillingIssueWarning()
entitlement.unsubscribeDetectedAt != null ->
entitlement.expirationDate?.let { showExpiryNotice(it) }
!entitlement.willRenew -> showNonRenewingNotice()
}
}
根据状态信号显示消息
| 有效权益上的信号 | 要显示的消息 |
|---|---|
billingIssueDetectedAt != null | 付款问题,请更新付款方式 |
unsubscribeDetectedAt != null | 权限将于 expirationDate 结束 |
willRenew == false(没有其他信号) | 此周期结束后不会续订 |
periodType == TRIAL 或 INTRO | 正在使用试用期或入门优惠价格 |
为多设备场景识别用户
val result = Purchases.sharedInstance.awaitLogIn("your_user_id")
val customerInfo = result.customerInfo
val createdNewUser = result.created
awaitLogIn() 会将匿名购买记录合并到已识别的用户账户中。logOut() 会开始一个全新的匿名会话。
Phase 4: Verify
监听 CustomerInfo 更新
注册监听器,让 UI 能够响应购买、恢复和后台刷新,而无需手动轮询。
Purchases.sharedInstance.updatedCustomerInfoListener =
UpdatedCustomerInfoListener { info ->
val active = info.entitlements["pro_access"]?.isActive == true
updateAccessGate(active)
}
如果 SDK 启动时命中缓存且内容未发生变化,监听器不会触发。除了设置监听器外,启动时还应始终调用 awaitCustomerInfo()。
测试矩阵
逐一检查每种情况,并确认 UI 响应正确。
| 情况 | 预期 isActive | 预期 UI |
|---|---|---|
| 新购买 | true | 高级内容 |
| 宽限期(账单问题,但仍有权限) | true | 高级内容 + 账单警告 |
已取消,但尚未到 expirationDate | true | 高级内容 + 到期通知 |
| 暂停计费 | false | 订阅页面 |
| 已暂停 | false | 订阅页面 |
| 已过期 | false | 订阅页面 |
| 待处理(尚未付款) | false | 订阅页面 |
检查要点
- 购买后监听器触发时,访问权限门控会正确切换。
- 后端授予权限(促销、退款、客服操作)后,
FETCH_CURRENT会更新CustomerInfo。 - 调用
logOut()后,CustomerInfo会反映匿名用户状态,isActive也会相应重置。 - 离线启动时仍会返回缓存的
CustomerInfo,并在不发起网络请求的情况下控制访问权限。