Rc Error Handling

RevenueCat/ai-toolkit/revenuecat-play-billing/skills/rc-error-handling

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

Use this skill when handling errors from the RevenueCat Android SDK. Covers PurchasesError, the PurchasesErrorCode enum, the userCancelled flag on PurchasesTransactionException, and the recommended UI response per code.

AI 產生的概覽

指導處理 RevenueCat Android SDK 錯誤,將 PurchasesErrorCode 對應到使用者介面回應。

功能
此技能說明如何攔截與處理 RevenueCat Android SDK 的錯誤,涵蓋 PurchasesError、PurchasesErrorCode 列舉,以及 PurchasesTransactionException 上的 userCancelled 旗標。它提供決策表,用來依 await 呼叫選擇例外類型並分類錯誤碼,也包含購買與非購買錯誤分支的 Kotlin 程式碼範例。此外還說明如何將錯誤碼對應到使用者可見文字,並附有檢查清單。
適用情境
適用於在 Android 應用程式中撰寫或檢閱 RevenueCat 購買流程的錯誤處理時。適合需要判斷該攔截哪種例外、如何回應特定錯誤碼,或要顯示什麼提示給使用者的情境。
執行需求
不需要指令碼或特殊工具,僅為說明性內容。前提是使用 RevenueCat Android SDK 的 Kotlin Android 專案。

Error Handling

Phase 1: Understand

With raw Google Play Billing you enumerate every BillingResponseCode, split them into retriable and non retriable groups, and build backoff retry logic. RevenueCat collapses this into a single type you deal with: PurchasesError.

kotlin
public class PurchasesError(    val code: PurchasesErrorCode,    val underlyingErrorMessage: String? = null,) {    val message: String // technical description, for logs}

Key facts you rely on:

  • PurchasesErrorCode is a cross platform enum with stable, readable codes.
  • error.message is a technical string. It belongs in logs, not in the UI.
  • awaitPurchase() throws PurchasesTransactionException, which adds a userCancelled: Boolean flag.
  • Every other await* call (awaitOfferings, awaitGetProducts, awaitCustomerInfo, awaitRestore) throws 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.

Phase 2: Plan

Before writing a catch block, decide three things:

  1. Which await* call are you wrapping? That picks the exception type.
  2. Which codes have specific handling? Everything else falls into a generic branch.
  3. What user facing string does each handled code map to?

Use this table to categorize PurchasesErrorCode values and pick the UX response.

CodeMeaningHandling
PurchaseCancelledErrorUser backed out of the flowDo nothing. userCancelled is also true.
ProductAlreadyPurchasedErrorProduct already active for the userRefresh CustomerInfo and check entitlements.
PaymentPendingErrorPurchase entered pending stateShow a pending message. Wait for UpdatedCustomerInfoListener.
NetworkErrorRequest failed due to connectivityPrompt the user to retry.
StoreProblemErrorGoogle Play issuePrompt to retry or update Play Store.
PurchaseNotAllowedErrorDevice or account cannot purchaseShow an explanatory message.
IneligibleErrorUser not eligible for the offerShow the base plan instead.

Exception type decision:

CallException to catchuserCancelled available?
awaitPurchase()PurchasesTransactionExceptionYes
awaitRestore()PurchasesExceptionNo
awaitOfferings()PurchasesExceptionNo
awaitGetProducts()PurchasesExceptionNo
awaitCustomerInfo()PurchasesExceptionNo

Phase 3: Execute

Purchase errors

Check userCancelled first and return silently. Then branch on error.code.

kotlin
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 PurchasesException and branch on the code. Use offline or cached fallbacks where you have them.

kotlin
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 e.error.message to the UI.

kotlin
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."}

Checklist

  • You picked PurchasesTransactionException for awaitPurchase and PurchasesException elsewhere.
  • You checked userCancelled before any branching on error.code.
  • You handled PaymentPendingError, ProductAlreadyPurchasedError, and NetworkError with their specific flows.
  • You logged error.message and showed a mapped string from userFacingMessage to the user.
  • You did not add retry loops around SDK calls. Retries are user initiated only.

References

來源與署名

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

授權條款: Apache-2.0; see LICENSE

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

檢舉或申請下架