Rc Subscription States

RevenueCat/ai-toolkit/revenuecat-play-billing/skills/rc-subscription-states

作者 RevenueCatccfc038185ec457b2ac9077f3bbf1ba947a44dbbApache-2.0; see LICENSE收錄於 2026年10月9日更新於 2026年10月9日

Use this skill when gating access based on RevenueCat subscription state on Android. Covers reading CustomerInfo, checking entitlement.isActive as the single source of truth for access, and drilling into willRenew, periodType, and expirationDate when deeper logic is needed.

AI 產生的概覽

指導 Android 開發者使用 RevenueCat CustomerInfo 與 entitlement.isActive 控制功能存取權限。

功能
說明如何在 Android 上讀取 CustomerInfo 與 EntitlementInfo,並以 entitlement.isActive 作為存取權限的唯一判斷依據。它將 Google Play 的訂閱狀態對應到 RevenueCat 的布林值,列出續訂、到期、帳務問題與週期類型所需讀取的欄位,並提供 Kotlin 程式碼範例,涵蓋存取檢查、強制重新整理、狀態感知介面、使用者識別與 CustomerInfo 監聽器。也包含測試矩陣與健全性檢查,用來驗證存取控制。
適用情境
適用於在使用 RevenueCat SDK 的 Android 應用程式中實作或審查訂閱存取控制。適合需要依據權益狀態判斷存取權限、解釋授予或拒絕存取的原因,或回應購買、恢復與背景重新整理事件的情境。
執行需求
需要一個使用 RevenueCat Purchases SDK 的 Android 專案,並設定好權益識別碼;不包含指令碼,只有說明與程式碼範例。

Subscription States

Decide whether a user has access, and drive state aware UI, using CustomerInfo and EntitlementInfo on Android.

Phase 1: Discover

Confirm what you are actually checking before you write code.

  • Which entitlement identifier gates the feature? (for example pro_access)
  • Do you need a plain access boolean, or do you also need to explain why the user has or lacks access (billing issue, canceled but still paid, paused)?
  • Do you need the cached value (fast, possibly stale) or a freshly fetched value (server authoritative)?
  • Are you refreshing UI once on launch, or reacting to live changes (purchase, restore, background refresh)?

If you only need access on/off, you only need isActive. Everything else is optional context.

Phase 2: Plan

Google's seven states versus RevenueCat's boolean

Rolling your own tracker with the Google Play Developer API means mapping seven subscription states and deciding which grant access.

Google stateGrants access?
ACTIVEyes
IN_GRACE_PERIODyes
CANCELED (before expirationDate)yes
ON_HOLDno
PAUSEDno
EXPIREDno
PENDINGno

RevenueCat computes this on the backend from the Google SubscriptionPurchaseV2 resource and exposes the result as one field: EntitlementInfo.isActive. You read a boolean instead of implementing the state machine.

What to read, and when

NeedField
Does the user have access right now?entitlement.isActive
Will the subscription renew at period end?entitlement.willRenew
When does paid access end?entitlement.expirationDate
Is this a trial, intro, prepaid, or normal period?entitlement.periodType
Is there a payment problem?entitlement.billingIssueDetectedAt
Has the user canceled but still has time left?entitlement.unsubscribeDetectedAt
Which store issued the entitlement?entitlement.store

Decision rules

  • Gate features on isActive == true. Nothing else.
  • Use billingIssueDetectedAt != null to show a fix payment prompt.
  • Use unsubscribeDetectedAt != null with expirationDate to show a renewal reminder while access is still valid.
  • Use !willRenew (when no billing issue and no explicit cancel timestamp) to show a non renewing notice.

Phase 3: Execute

Read CustomerInfo and check access

kotlin
val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()val hasAccess = customerInfo.entitlements["pro_access"]?.isActive == true

awaitCustomerInfo() returns the disk cache immediately, then refreshes from the network in the background. Entitlement checks stay fast, even offline.

Force a fresh fetch when you must

Use this after a server side grant (for example, a support agent issued a promo).

kotlin
val fresh = Purchases.sharedInstance.awaitCustomerInfo(    fetchPolicy = CacheFetchPolicy.FETCH_CURRENT)

Drive state aware UI

isActive gates access; the other fields explain context.

kotlin
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()    }}

Messaging guide by signal

Signal on an active entitlementMessage to show
billingIssueDetectedAt != nullPayment problem, update method
unsubscribeDetectedAt != nullAccess ends on expirationDate
willRenew == false (no other signal)Will not renew this period
periodType == TRIAL or INTROTrial or intro pricing in effect

Identify the user for multi device

kotlin
val result = Purchases.sharedInstance.awaitLogIn("your_user_id")val customerInfo = result.customerInfoval createdNewUser = result.created

awaitLogIn() merges anonymous purchases with the identified user. logOut() starts a fresh anonymous session.

Phase 4: Verify

Listen for CustomerInfo updates

Register a listener so UI reacts to purchases, restores, and background refreshes without manual polling.

kotlin
Purchases.sharedInstance.updatedCustomerInfoListener =    UpdatedCustomerInfoListener { info ->        val active = info.entitlements["pro_access"]?.isActive == true        updateAccessGate(active)    }

The listener does not fire when the SDK starts with a cache hit and nothing changed. Always call awaitCustomerInfo() on launch in addition to setting the listener.

Test matrix

Walk through each case and confirm the UI responds correctly.

CaseExpected isActiveExpected UI
Fresh purchasetruePremium content
Grace period (billing issue, still granted)truePremium + billing warning
Canceled, still before expirationDatetruePremium + expiry notice
On holdfalseSubscribe screen
PausedfalseSubscribe screen
ExpiredfalseSubscribe screen
Pending (no payment yet)falseSubscribe screen

Sanity checks

  • Access gate flips correctly when the listener fires after a purchase.
  • FETCH_CURRENT updates CustomerInfo after a backend grant (promo, refund, support action).
  • After logOut(), CustomerInfo reflects an anonymous user and isActive resets accordingly.
  • Offline launch still returns cached CustomerInfo and gates access without a network call.

References

來源與署名

來源:RevenueCat/ai-toolkit位於revenuecat-play-billing/skills/rc-subscription-states提交ccfc038

授權條款: Apache-2.0; see LICENSE

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架