rc-one-time-products
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOne Time Products with RevenueCat
基于RevenueCat的Android一次性产品销售
Sell one time products on Android without writing BillingClient glue. The RevenueCat SDK fetches products, launches the purchase, verifies the receipt, and acknowledges or consumes the token for you.
无需编写BillingClient相关粘合代码,即可在Android平台销售一次性产品。RevenueCat SDK会为你完成产品获取、发起购买、验证收据以及确认或消耗订单令牌的操作。
Phase 1: Scope
阶段1:范围界定
Answer these before touching code.
| Question | Decision |
|---|---|
| Is the product consumable or non consumable? | Mark consumables in the RevenueCat dashboard so the SDK calls |
| Do you read access through entitlements or transactions? | Prefer entitlements so access logic stays decoupled from product IDs. |
| Do you need server notification of the purchase? | Use RevenueCat webhooks. The |
Do not call , , or yourself once RevenueCat is integrated. The SDK owns the instance.
BillingClient.queryProductDetailsAsyncacknowledgePurchaseconsumeAsyncBillingClient在编写代码前先回答以下问题。
| 问题 | 决策 |
|---|---|
| 产品是消耗型还是非消耗型? | 在RevenueCat控制台标记消耗型产品,SDK会自动为你调用 |
| 你通过权益(entitlements)还是交易记录来判断用户权限? | 优先使用权益,这样权限逻辑与产品ID解耦。 |
| 是否需要服务器接收购买通知? | 使用RevenueCat webhooks。 |
集成RevenueCat后,请勿自行调用、或。SDK会管理实例。
BillingClient.queryProductDetailsAsyncacknowledgePurchaseconsumeAsyncBillingClientPhase 2: Prepare
阶段2:准备工作
- Configure the product in Google Play Console as a one time product.
- In the RevenueCat dashboard, attach the product to an offering and package, and flag it as consumable if it should be consumed on purchase.
- Confirm runs once on app start with your Android API key and the current App User ID.
Purchases.configure
- 在Google Play控制台中将产品配置为一次性产品。
- 在RevenueCat控制台中,将产品关联到一个套餐组(offering)和套餐(package),如果产品需要在购买后被消耗,则标记为消耗型。
- 确认在应用启动时仅运行一次,传入你的Android API密钥和当前应用用户ID。
Purchases.configure
Phase 3: Execute
阶段3:执行流程
Fetch offerings, launch the purchase, then read the result off .
CustomerInfokotlin
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.availablePackages?.first() ?: returnLaunch the purchase with . It suspends until the RevenueCat backend verifies the token.
awaitPurchasekotlin
val result = Purchases.sharedInstance.awaitPurchase(
PurchaseParams.Builder(activity, pkg).build()
)
val info = result.customerInfoRead access through entitlements, or fall back to the non subscription transactions list.
kotlin
val hasAccess = info.entitlements["lifetime_access"]?.isActive == true
val owned = info.nonSubscriptionTransactions
.any { it.productIdentifier == "lifetime_product_id" }The SDK acknowledges non consumables and consumes consumables automatically once the backend verifies the purchase. You do not call or .
acknowledgePurchaseconsumeAsync获取产品套餐,发起购买,然后从中读取结果。
CustomerInfokotlin
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.availablePackages?.first() ?: return调用发起购买,该方法会挂起直到RevenueCat后端验证令牌完成。
awaitPurchasekotlin
val result = Purchases.sharedInstance.awaitPurchase(
PurchaseParams.Builder(activity, pkg).build()
)
val info = result.customerInfo通过权益判断用户权限,或者退而求其次查看非订阅交易列表。
kotlin
val hasAccess = info.entitlements["lifetime_access"]?.isActive == true
val owned = info.nonSubscriptionTransactions
.any { it.productIdentifier == "lifetime_product_id" }一旦后端验证完成购买,SDK会自动确认非消耗型产品并消耗消耗型产品。你无需调用或。
acknowledgePurchaseconsumeAsyncPhase 4: Handle Edge Cases
阶段4:处理边缘情况
Wrap to separate cancellation, pending payments, and real errors.
awaitPurchasekotlin
try { /* awaitPurchase */ } catch (e: PurchasesTransactionException) {
when {
e.error.code == PurchasesErrorCode.PaymentPendingError -> showPending()
e.userCancelled -> Unit
else -> showError(e.error.message)
}
}| Outcome | SDK signal | Action |
|---|---|---|
| Success | | Grant access from |
| User cancel | | Swallow silently. |
| Pending payment | | Show a pending message. The SDK updates the entitlement later through |
| Other error | | Surface |
对进行包装,区分取消、待处理支付和真实错误。
awaitPurchasekotlin
try { /* awaitPurchase */ } catch (e: PurchasesTransactionException) {
when {
e.error.code == PurchasesErrorCode.PaymentPendingError -> showPending()
e.userCancelled -> Unit
else -> showError(e.error.message)
}
}| 结果 | SDK信号 | 操作 |
|---|---|---|
| 成功 | | 通过 |
| 用户取消 | | 静默处理。 |
| 待处理支付 | | 显示待处理提示。后续SDK会通过 |
| 其他错误 | | 显示 |
Phase 5: Verify
阶段5:验证
- Trust from
result.customerInfo. Do not grant access from optimistic local state.awaitPurchase - Pending purchases do not grant entitlements. Register an so you react when the payment completes.
UpdatedCustomerInfoListener - For server side provisioning, subscribe to the RevenueCat webhook. Do not reimplement Google Play Developer API verification.
INITIAL_PURCHASE
- 信任返回的
awaitPurchase。请勿基于本地乐观状态授予权限。result.customerInfo - 待处理的购买不会授予权益。注册以便在支付完成时做出响应。
UpdatedCustomerInfoListener - 如需服务器端配置,请订阅RevenueCat的webhook。请勿重新实现Google Play开发者API验证逻辑。
INITIAL_PURCHASE