rc-error-handling
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseError Handling
错误处理
Phase 1: Understand
第一阶段:理解
With raw Google Play Billing you enumerate every , split them into retriable and non retriable groups, and build backoff retry logic. RevenueCat collapses this into a single type you deal with: .
BillingResponseCodePurchasesErrorkotlin
public class PurchasesError(
val code: PurchasesErrorCode,
val underlyingErrorMessage: String? = null,
) {
val message: String // technical description, for logs
}Key facts you rely on:
- is a cross platform enum with stable, readable codes.
PurchasesErrorCode - is a technical string. It belongs in logs, not in the UI.
error.message - throws
awaitPurchase(), which adds aPurchasesTransactionExceptionflag.userCancelled: Boolean - Every other call (
await*,awaitOfferings,awaitGetProducts,awaitCustomerInfo) throwsawaitRestore.PurchasesException - The SDK already retries transient billing and network failures internally. Any error that reaches you has exhausted the SDK retry budget. You do not add your own backoff loop. The only retry you implement is a user triggered "Try Again" button.
在原生Google Play Billing中,你需要枚举每个,将它们分为可重试和不可重试组,并实现退避重试逻辑。而RevenueCat将这些简化为一种你只需处理的类型:。
BillingResponseCodePurchasesErrorkotlin
public class PurchasesError(
val code: PurchasesErrorCode,
val underlyingErrorMessage: String? = null,
) {
val message: String // 技术描述,用于日志记录
}你需要了解的关键信息:
- 是一个跨平台枚举,具有稳定且易读的错误码。
PurchasesErrorCode - 是技术字符串,应仅用于日志,不要展示在UI中。
error.message - 会抛出
awaitPurchase(),该异常包含PurchasesTransactionException标志。userCancelled: Boolean - 其他所有调用(
await*、awaitOfferings、awaitGetProducts、awaitCustomerInfo)都会抛出awaitRestore。PurchasesException - SDK已在内部对临时账单和网络故障进行重试。任何到达你这里的错误都已耗尽SDK的重试次数,你无需自行添加退避循环,仅需实现用户触发的“重试”按钮即可。
Phase 2: Plan
第二阶段:规划
Before writing a block, decide three things:
catch- Which call are you wrapping? That picks the exception type.
await* - Which codes have specific handling? Everything else falls into a generic branch.
- What user facing string does each handled code map to?
Use this table to categorize values and pick the UX response.
PurchasesErrorCode| Code | Meaning | Handling |
|---|---|---|
| User backed out of the flow | Do nothing. |
| Product already active for the user | Refresh |
| Purchase entered pending state | Show a pending message. Wait for |
| Request failed due to connectivity | Prompt the user to retry. |
| Google Play issue | Prompt to retry or update Play Store. |
| Device or account cannot purchase | Show an explanatory message. |
| User not eligible for the offer | Show the base plan instead. |
Exception type decision:
| Call | Exception to catch | |
|---|---|---|
| | Yes |
| | No |
| | No |
| | No |
| | No |
编写块之前,先确定三件事:
catch- 你要包装哪个调用?这会决定异常类型。
await* - 哪些错误码需要特定处理?其他所有错误都归入通用分支。
- 每个需处理的错误码对应什么面向用户的提示字符串?
使用下表对值进行分类,并选择对应的UX响应方式。
PurchasesErrorCode| 错误码 | 含义 | 处理方式 |
|---|---|---|
| 用户退出了购买流程 | 不做任何操作。此时 |
| 用户已拥有该产品的使用权 | 刷新 |
| 购买进入待处理状态 | 显示待处理提示信息,等待 |
| 因网络连接问题导致请求失败 | 提示用户重试。 |
| Google Play出现问题 | 提示用户重试或更新Google Play商店。 |
| 设备或账户无法进行购买 | 显示解释性提示信息。 |
| 用户不符合该优惠的参与条件 | 展示基础方案。 |
异常类型选择:
| 调用方法 | 需要捕获的异常 | 是否支持 |
|---|---|---|
| | 是 |
| | 否 |
| | 否 |
| | 否 |
| | 否 |
Phase 3: Execute
第三阶段:实施
Purchase errors
购买错误处理
Check first and return silently. Then branch on .
userCancellederror.codekotlin
try {
val result = Purchases.sharedInstance.awaitPurchase(params)
handleSuccess(result.customerInfo)
} catch (e: PurchasesTransactionException) {
if (e.userCancelled) return
when (e.error.code) {
PurchasesErrorCode.PaymentPendingError -> showPendingMessage()
PurchasesErrorCode.ProductAlreadyPurchasedError -> {
val info = Purchases.sharedInstance.awaitCustomerInfo()
handleSuccess(info)
}
PurchasesErrorCode.NetworkError -> showRetryDialog()
else -> showGenericError(userFacingMessage(e.error))
}
}先检查,如果为true则静默返回。然后根据进行分支处理。
userCancellederror.codekotlin
try {
val result = Purchases.sharedInstance.awaitPurchase(params)
handleSuccess(result.customerInfo)
} catch (e: PurchasesTransactionException) {
if (e.userCancelled) return
when (e.error.code) {
PurchasesErrorCode.PaymentPendingError -> showPendingMessage()
PurchasesErrorCode.ProductAlreadyPurchasedError -> {
val info = Purchases.sharedInstance.awaitCustomerInfo()
handleSuccess(info)
}
PurchasesErrorCode.NetworkError -> showRetryDialog()
else -> showGenericError(userFacingMessage(e.error))
}
}Non purchase errors
非购买错误处理
Catch and branch on the code. Use offline or cached fallbacks where you have them.
PurchasesExceptionkotlin
try {
val offerings = Purchases.sharedInstance.awaitOfferings()
displayOfferings(offerings)
} catch (e: PurchasesException) {
when (e.error.code) {
PurchasesErrorCode.NetworkError -> showOfflineFallback()
else -> logError(e.error)
}
}捕获并根据错误码分支处理,如有离线或缓存回退方案则使用。
PurchasesExceptionkotlin
try {
val offerings = Purchases.sharedInstance.awaitOfferings()
displayOfferings(offerings)
} catch (e: PurchasesException) {
when (e.error.code) {
PurchasesErrorCode.NetworkError -> showOfflineFallback()
else -> logError(e.error)
}
}Map codes to user facing strings
错误码转面向用户的提示字符串
Keep a single mapping function. Never pass to the UI.
e.error.messagekotlin
fun userFacingMessage(error: PurchasesError): String = when (error.code) {
PurchasesErrorCode.PurchaseCancelledError -> ""
PurchasesErrorCode.NetworkError ->
"Please check your internet connection and try again."
PurchasesErrorCode.StoreProblemError ->
"There was a problem with Google Play. Please try again."
PurchasesErrorCode.ProductAlreadyPurchasedError ->
"You already have this subscription."
PurchasesErrorCode.PaymentPendingError ->
"Your payment is being processed. We'll notify you when it completes."
else -> "Something went wrong. Please try again."
}保留一个统一的映射函数,切勿将传递到UI中。
e.error.messagekotlin
fun userFacingMessage(error: PurchasesError): String = when (error.code) {
PurchasesErrorCode.PurchaseCancelledError -> ""
PurchasesErrorCode.NetworkError ->
"请检查你的网络连接后重试。"
PurchasesErrorCode.StoreProblemError ->
"Google Play出现问题,请重试。"
PurchasesErrorCode.ProductAlreadyPurchasedError ->
"你已订阅该服务。"
PurchasesErrorCode.PaymentPendingError ->
"你的支付正在处理中,完成后我们会通知你。"
else -> "出错了,请重试。"
}Checklist
检查清单
- You picked for
PurchasesTransactionExceptionandawaitPurchaseelsewhere.PurchasesException - You checked before any branching on
userCancelled.error.code - You handled ,
PaymentPendingError, andProductAlreadyPurchasedErrorwith their specific flows.NetworkError - You logged and showed a mapped string from
error.messageto the user.userFacingMessage - You did not add retry loops around SDK calls. Retries are user initiated only.
- 你为选择了
awaitPurchase,为其他调用选择了PurchasesTransactionException。PurchasesException - 在根据分支处理前,你先检查了
error.code。userCancelled - 你针对、
PaymentPendingError和ProductAlreadyPurchasedError实现了特定处理流程。NetworkError - 你记录了,并向用户展示了
error.message映射后的提示字符串。userFacingMessage - 你没有在SDK调用周围添加重试循环,仅允许用户主动触发重试。