rc-payment-recovery
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePayment Recovery
订阅续费恢复
Failed renewals on Google Play move a subscription through two states: grace period (user keeps access while Google retries the card) and account hold (access revoked until the user fixes the payment method). With RevenueCat, both states land in automatically, and Google's in app message shows by default.
CustomerInfoGoogle Play平台上的订阅续费失败后,订阅会进入两个状态:宽限期(用户在Google重试支付期间仍可访问服务)和账户冻结(用户修复支付方式前将被取消访问权限)。通过RevenueCat,这两种状态都会自动同步到中,且Google的应用内消息默认会自动显示。
CustomerInfoPhase 1: Understand
第一阶段:理解
Three things happen when a renewal fails:
| State | Access | How RevenueCat surfaces it | User sees |
|---|---|---|---|
| Grace period | Retained | | Google in app snackbar by default |
| Account hold | Revoked | | Google in app snackbar by default |
| Recovered | Retained | | Nothing |
Two signals matter in the SDK:
- is non null from the moment Google reports a billing problem until the user resolves it.
EntitlementInfo.billingIssueDetectedAt - tells you whether they still have access.
EntitlementInfo.isActive
On the backend, a webhook fires once per transition. You do not decode RTDNs.
BILLING_ISSUE续费失败时会发生以下三件事:
| 状态 | 访问权限 | RevenueCat的呈现方式 | 用户看到的内容 |
|---|---|---|---|
| 宽限期 | 保留 | | 默认显示Google应用内 Snackbar |
| 账户冻结 | 取消 | | 默认显示Google应用内 Snackbar |
| 已恢复 | 保留 | | 无内容 |
SDK中有两个关键信号:
- 从Google报告账单问题开始,到用户解决问题前,该值始终非空。
EntitlementInfo.billingIssueDetectedAt - 用于判断用户是否仍拥有访问权限。
EntitlementInfo.isActive
在后端,每次状态转换时都会触发 webhook。无需解析RTDNs。
BILLING_ISSUEPhase 2: Plan
第二阶段:规划
Before you write app code, decide what you actually need. Most apps need none.
Ask:
- Do you want the default Google in app message? If yes, do nothing. The SDK calls on BillingClient connect.
showInAppMessagesIfNeeded - Do you want your own banner or dialog? If yes, read from
billingIssueDetectedAtand branch onCustomerInfo.isActive - Do you want to gate the message to specific screens? If yes, disable the automatic call and invoke yourself.
showInAppMessagesIfNeeded(activity) - Do you need a server side flag (for example, to send a recovery email)? If yes, handle the webhook. No app code required.
BILLING_ISSUE
If you only want the default behavior, stop here.
在编写应用代码前,先确定实际需求。大多数应用无需额外开发。
思考以下问题:
- 是否需要默认的Google应用内消息?如果是,无需任何操作。SDK会在BillingClient连接时自动调用。
showInAppMessagesIfNeeded - 是否需要自定义横幅或弹窗?如果是,从中读取
CustomerInfo并根据billingIssueDetectedAt进行分支处理。isActive - 是否需要将消息限定在特定页面显示?如果是,禁用自动调用,自行在指定的Activity中调用。
showInAppMessagesIfNeeded(activity) - 是否需要服务端标识(例如发送恢复邮件)?如果是,处理webhook即可,无需编写应用代码。
BILLING_ISSUE
如果仅需要默认行为,到此为止即可。
Phase 3: Execute
第三阶段:实施
Default (recommended)
默认方式(推荐)
Leave automatic in app messages on. This is the default:
kotlin
PurchasesConfiguration.Builder(context, apiKey)
.showInAppMessagesAutomatically(true)
.build()保持应用内消息自动开启。这是默认配置:
kotlin
PurchasesConfiguration.Builder(context, apiKey)
.showInAppMessagesAutomatically(true)
.build()Manual trigger
手动触发
Disable the automatic call and show the message from your chosen activity:
kotlin
PurchasesConfiguration.Builder(context, apiKey)
.showInAppMessagesAutomatically(false)
.build()
Purchases.sharedInstance.showInAppMessagesIfNeeded(activity)禁用自动调用,在指定的Activity中手动显示消息:
kotlin
PurchasesConfiguration.Builder(context, apiKey)
.showInAppMessagesAutomatically(false)
.build()
Purchases.sharedInstance.showInAppMessagesIfNeeded(activity)Your own UI during grace period
宽限期内自定义UI
Read the entitlement and branch on both flags:
kotlin
val entitlement = customerInfo.entitlements["pro_access"]
when {
entitlement == null || !entitlement.isActive ->
showSubscribeScreen()
entitlement.billingIssueDetectedAt != null && entitlement.isActive ->
showGracePeriodWarning()
entitlement.billingIssueDetectedAt != null && !entitlement.isActive ->
showAccountHoldScreen()
else ->
showPremiumContent()
}读取订阅权限并根据两个标志进行分支处理:
kotlin
val entitlement = customerInfo.entitlements["pro_access"]
when {
entitlement == null || !entitlement.isActive ->
showSubscribeScreen()
entitlement.billingIssueDetectedAt != null && entitlement.isActive ->
showGracePeriodWarning()
entitlement.billingIssueDetectedAt != null && !entitlement.isActive ->
showAccountHoldScreen()
else ->
showPremiumContent()
}Send the user to fix payment
引导用户修复支付方式
CustomerInfo.managementURLkotlin
customerInfo.managementURL?.let { url ->
startActivity(Intent(Intent.ACTION_VIEW, url))
}CustomerInfo.managementURLkotlin
customerInfo.managementURL?.let { url ->
startActivity(Intent(Intent.ACTION_VIEW, url))
}Phase 4: Verify
第四阶段:验证
Test each transition:
- Use a Google Play test card that declines renewals to push a subscription into grace period.
- Confirm becomes non null and
entitlement.billingIssueDetectedAtstaysisActive.true - Wait for account hold and confirm flips to
isActivewhilefalseremains non null.billingIssueDetectedAt - Update the payment method and confirm returns to
billingIssueDetectedAt.null - On backend, confirm a webhook fires on the first transition.
BILLING_ISSUE
测试每一种状态转换:
- 使用Google Play的测试拒付卡片,将订阅推入宽限期。
- 确认变为非空,且
entitlement.billingIssueDetectedAt保持为isActive。true - 等待进入账户冻结状态,确认变为
isActive,同时false仍为非空。billingIssueDetectedAt - 更新支付方式,确认恢复为
billingIssueDetectedAt。null - 在后端,确认首次状态转换时触发了webhook。
BILLING_ISSUE