实现主动式应用内购买恢复
Implement proactive in-app purchase restore
2022年6月6日
一句话判断
如果你的付费应用还在让用户手动点”恢复购买”,这场 Session 会让你意识到这是多么落后的体验——StoreKit 2 已经可以做到完全无感恢复。
这场 Session 讲了什么
App Store 商务技术团队的 David Wendland 详细讲解了”主动式应用内购买恢复”的最佳实践。核心理念是:应用启动时,利用设备上已有的 StoreKit 数据,自动判断用户是新客户、现有客户还是流失客户,无需用户执行任何操作。
Session 用一个叫 Ocean Journal 的示例应用贯穿始终。在传统体验中,用户面对”购买”、“登录”和”恢复购买”三个按钮,往往不知道该选哪个。在优化后的体验中,已有订阅的用户在新设备上打开应用时,应用会自动识别其订阅状态并直接解锁所有功能。
内容覆盖了 StoreKit 2(iOS 15+)和原始 StoreKit 两种实现路径,以及三种核心客户状态的定义和应用场景。
值得深挖的点
三种核心客户状态。 第一种是”新客户”——没有任何当前或历史交易记录,适合展示默认的商品推荐页和 introductory offer。第二种是”已购买/活跃订阅”——有有效交易,应用有义务立即提供服务。第三种是”非活跃客户”——曾经购买但已过期或被撤销,适合展示 win-back offer。
计费重试与宽限期。 当自动续订失败后,Apple 会尝试恢复订阅长达 60 天。如果你在 App Store Connect 中开启了计费宽限期,订阅者在重试期间仍可使用服务。这段时间内应该展示简单的提示让用户解决付款问题,而不是粗暴地中断服务。
Original Transaction ID 的关键作用。 每笔购买和每个活跃订阅都有一个唯一且持久的原始交易 ID。你应该将这个 ID 与你系统中的用户账户关联,这样配合 App Store Server Notifications,服务端就能始终掌握最新的交易状态。
StoreKit 2 的实现更简洁。 通过 Transaction.currentEntitlements 可以直接获取当前有效的交易,Transaction.all 可以获取完整的历史记录。配合 Product.SubscriptionInfo.Status 可以精确判断订阅状态。
代码片段
import StoreKit
// StoreKit 2: 主动检查当前权益
func checkPurchaseStatus() async {
// 检查当前有效的权益
for await result in Transaction.currentEntitlements {
guard case .verified(let transaction) = result else { continue }
// 用户有有效权益,直接解锁功能
unlockFeature(for: transaction.productID)
transaction.finish()
}
}
// 检查订阅状态,区分活跃/非活跃客户
func checkSubscriptionStatus(productID: String) async {
let product = try await Product.products(for: [productID]).first
guard let product = product else { return }
let statuses = try await product.subscription?.status ?? []
for status in statuses {
switch status.state {
case .subscribed:
// 活跃订阅者,解锁全部功能
break
case .revoked, .expired:
// 非活跃订阅者,展示 win-back offer
presentWinBackOffer()
default:
break
}
}
}
// 获取完整交易历史用于判断新/老客户
func checkCustomerHistory() async {
var hasHistory = false
for await result in Transaction.currentEntitlements {
if case .verified = result {
hasHistory = true
break
}
}
if !hasHistory {
// 新客户,展示 introductory offer
presentNewCustomerExperience()
}
}
最佳实践
- 应用启动时立即检查 StoreKit 交易状态,不要等用户点击”恢复购买”
- 用 originalTransactionId 在服务端关联用户账户,配合 Server Notifications 保持状态同步
- 对于计费重试中的订阅者,展示温和的提示而非中断服务
- 对非活跃订阅者展示促销优惠或 offer code 来召回
- 如果支持多个产品或订阅组,需要分别判断每个产品的客户状态,考虑混合状态的处理
- 考虑跨平台活动(如 Web 端购买)对客户状态的影响
- iOS 15+ 使用 StoreKit 2,更早版本用原始 StoreKit + verifyReceipt 端点实现相同逻辑
还有什么值得关注
- App Store Server Notifications V2 提供了更丰富的订阅状态变更事件
- Family Sharing 场景下,交易可能因为共享权限被撤销而产生 revocation date
- 对于支持多年期的非续期订阅,过期判断逻辑与自动续订不同,需要特别处理
- 配套观看关于减少非自愿订阅流失的 Session,了解计费重试和宽限期的完整机制