rc-purchase-flow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePurchase Flow
购买流程
A RevenueCat purchase on Android is two lines of code. The SDK handles the billing params, the Play sheet, the purchase token round trip, server side verification, and acknowledgment. You decide which package to pass in and what to do with the returned entitlements.
在Android上通过RevenueCat实现内购只需两行代码。SDK会处理账单参数、Play支付界面、购买令牌往返、服务器端验证以及确认操作。你只需决定传入哪个套餐,以及如何处理返回的权限。
Phase 1: Understand what awaitPurchase() does
阶段1:理解awaitPurchase()的作用
One call to runs six steps under the hood. You do not write any of this.
Purchases.sharedInstance.awaitPurchase(params)| Step | What happens |
|---|---|
| 1 | Builds |
| 2 | Calls |
| 3 | Suspends until the |
| 4 | If |
| 5 | Backend verifies via |
| 6 | SDK acknowledges (subs or non-consumables) or consumes (consumables) within the 3 day Google window |
The call returns . Retriable failures are retried automatically.
PurchaseResult(storeTransaction, customerInfo)See the Purchase Flow chapter on revenuecat.com for the six-step diagram and the full picture.
调用一次会在后台执行六个步骤,你无需编写任何相关代码。
Purchases.sharedInstance.awaitPurchase(params)| 步骤 | 操作内容 |
|---|---|
| 1 | 从你的 |
| 2 | 针对你传入的Activity调用 |
| 3 | 暂停执行,直到 |
| 4 | 如果结果为 |
| 5 | 后端通过 |
| 6 | SDK会在Google规定的3天窗口期内确认(订阅或非消耗品)或消耗(消耗品)购买 |
该调用会返回。可重试的失败会自动重试。
PurchaseResult(storeTransaction, customerInfo)查看revenuecat.com上的购买流程章节获取六步流程图和完整说明。
Phase 2: Prepare the package
阶段2:准备套餐
You need a from offerings before you can purchase. Fetch offerings, let your UI pick one, and hold the activity.
Packagekotlin
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.monthly ?: returnIf you need a specific offer instead of the default, resolve a and pass that to instead of the package.
SubscriptionOptionPurchaseParams.Builderkotlin
val option = pkg.product.subscriptionOptions
?.firstOrNull { it.tags.contains("promo_50_off") }
?: pkg.product.defaultOption
?: returnFor EU personalized pricing, chain on the builder so Play shows the customized price notice.
.isPersonalizedPrice(true)在进行购买前,你需要从offerings中获取一个。获取offerings,让UI选择一个,并持有当前Activity。
Packagekotlin
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.monthly ?: return如果你需要特定优惠而非默认选项,解析一个并将其传入,而不是传入套餐。
SubscriptionOptionPurchaseParams.Builderkotlin
val option = pkg.product.subscriptionOptions
?.firstOrNull { it.tags.contains("promo_50_off") }
?: pkg.product.defaultOption
?: return对于欧盟个性化定价,在builder上链式调用,这样Play会显示自定义价格通知。
.isPersonalizedPrice(true)Phase 3: Execute the purchase
阶段3:执行购买
Build the params, await the purchase, read to gate access, and handle the two expected error branches.
customerInfokotlin
try {
val result = Purchases.sharedInstance.awaitPurchase(
PurchaseParams.Builder(activity, pkg).build()
)
val customerInfo = result.customerInfo
if (customerInfo.entitlements["pro"]?.isActive == true) {
navigateToApp()
}
} catch (e: PurchasesTransactionException) {
when {
e.userCancelled -> { /* backed out, do nothing */ }
e.error.code == PurchasesErrorCode.ProductAlreadyPurchasedError ->
showMessage("You already have this subscription")
else -> showError(e.error.message)
}
}Rules for this block:
- is
PurchaseResult. Access fields as@Pokoandresult.customerInfo. Do not destructure withresult.storeTransaction.val (transaction, customerInfo) = result - is the gate. Do not check
customerInfo.entitlements["<id>"]?.isActive == trueto decide access.storeTransaction - is the only exception type thrown by
PurchasesTransactionException. Catch it, branch onawaitPurchasefirst, then onuserCancelled.e.error.code - User cancellation is not an error to surface. Swallow it.
If you prefer callbacks over coroutines, is the equivalent entry point.
purchaseWith(params, onError, onSuccess)构建参数,等待购买完成,读取以控制访问权限,并处理两种预期的错误分支。
customerInfokotlin
try {
val result = Purchases.sharedInstance.awaitPurchase(
PurchaseParams.Builder(activity, pkg).build()
)
val customerInfo = result.customerInfo
if (customerInfo.entitlements["pro"]?.isActive == true) {
navigateToApp()
}
} catch (e: PurchasesTransactionException) {
when {
e.userCancelled -> { /* 用户退出,不做处理 */ }
e.error.code == PurchasesErrorCode.ProductAlreadyPurchasedError ->
showMessage("你已订阅该服务")
else -> showError(e.error.message)
}
}此代码块的规则:
- 是
PurchaseResult类型。通过@Poko和result.customerInfo访问字段,不要使用result.storeTransaction进行解构。val (transaction, customerInfo) = result - 是访问控制的判断依据。不要通过
customerInfo.entitlements["<id>"]?.isActive == true来决定访问权限。storeTransaction - 只会抛出
awaitPurchase类型的异常。捕获该异常后,先判断PurchasesTransactionException,再根据userCancelled分支处理。e.error.code - 用户取消操作不属于需要提示的错误,直接忽略即可。
如果你更喜欢回调而非协程,是等效的入口方法。
purchaseWith(params, onError, onSuccess)Phase 4: Inspect StoreTransaction only if you need it
阶段4:仅在需要时查看StoreTransaction
result.storeTransactioncustomerInfo| Field | Type | Notes |
|---|---|---|
| | Null for restored purchases |
| | Raw Play purchase token |
| | Product IDs in the transaction |
| | Epoch millis |
| | |
result.storeTransactioncustomerInfo| 字段 | 类型 | 说明 |
|---|---|---|
| | 恢复的购买记录为Null |
| | 原始Play购买令牌 |
| | 交易中的产品ID列表 |
| | 时间戳(毫秒级) |
| | |
Phase 5: Restore on reinstall or device switch
阶段5:重新安装或切换设备时恢复购买
Restore runs , posts everything found to RevenueCat, and returns the fresh . Gate access the same way you do after a purchase.
queryPurchasesAsync()CustomerInfokotlin
try {
val customerInfo = Purchases.sharedInstance.awaitRestore()
if (customerInfo.entitlements["pro"]?.isActive == true) {
navigateToApp()
} else {
showMessage("No active purchases found")
}
} catch (e: PurchasesException) {
showError(e.error.message)
}Note the exception type. throws , not . There is no flag to check because no billing sheet is shown.
awaitRestore()PurchasesExceptionPurchasesTransactionExceptionuserCancelled恢复购买会执行,将所有找到的购买记录发送至RevenueCat,并返回最新的。访问控制方式与购买完成后相同。
queryPurchasesAsync()CustomerInfokotlin
try {
val customerInfo = Purchases.sharedInstance.awaitRestore()
if (customerInfo.entitlements["pro"]?.isActive == true) {
navigateToApp()
} else {
showMessage("未找到活跃的购买记录")
}
} catch (e: PurchasesException) {
showError(e.error.message)
}注意异常类型。抛出的是,而非。由于不会显示支付界面,因此没有标志可供判断。
awaitRestore()PurchasesExceptionPurchasesTransactionExceptionuserCancelledException types at a glance
异常类型一览
| Call | Thrown exception | Has |
|---|---|---|
| | Yes |
| | No |
| 调用方法 | 抛出的异常 | 是否包含 |
|---|---|---|
| | 是 |
| | 否 |