WWDC21: StoreKit 2 简介 Meet StoreKit 2
新的 StoreKit 2 包含 Products / Purchases / Transaction Info / Transaction history / Subscription status
,基于 Swift async/await 设计,只需一行代码即可完成支付,还可以指定 appAccountToken
标识交易,打通自建账号系统;此外借由 automatic validation,可以方便的在 iOS 端完成支付验证,当然自定义验证依然支持;通过监听 transaction update,可以更好的处理一些需要额外验证操作的交易。
使用 StoreKit configuration 文件测试
请求、支付、响应
init() {
// ...
// 监听交易更新
taskHandle = listenForTransactions()
async {
// 初始化时即查询
await requestProducts()
}
}
@MainActor
func requestProducts() async {
do {
let storeProducts = try await Product.request(with: Set(productIdToEmoji.keys))
var newCars: [Product] = []
var newSubscriptions: [Product] = []
var newFuel: [Product] = []
for product in storeProducts {
switch product.type {
case .consumable:
newFuel.append(product)
case .nonConsumable:
newCars.append(product)
case .autoRenewable:
newSubscriptions.append(product)
default:
print("Unknown product")
}
}
// Sort products by price.
cars = sortByPrice(newCars)
subscriptions = sortByPrice(newSubscriptions)
fuel = sortByPrice(newFuel)
} catch {
print("Failed product request: \(error)")
}
}
func purchase(_ product: Product) async throws -> Transaction? {
// 可以通过指定 `appAccountToken` 实现与自己的账号系统同步
let result = try await product.purchase(options: [.appAccoutnToken(currentAccountToken)])
switch result {
// StoreKit 2 会完成 transaction 的验证
case .success(let verification):
let transaction = try checkVerified(verification)
// 更新已支付的内容状态以及 UI
await updatePurchasedIdentifiers(transaction)
// 告知 StoreKit 完成订单
await transaction.finish()
return transaction
// 有时用户会做一些额外的验证操作,例如儿童需要父母通过,就会返回 `.pending` 状态,
// 这些步骤完成后,支付操作完成,因此还需要监听 transaction updates,见 `listenForTransactions()`
case .userCancelled, .pending:
return nil
default:
}
}
func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
switch result {
case .unverified:
// 如果订单验证失败,抛出一个错误,可以通过 UI 告知用户
throw StoreError.failedVerification
case .verified(let safe):
return safe
}
}
func isPurchased(_ productIdentifier: String) async throws -> Bool {
guard let result = await Transaction.latest(for: productIdentifier) else {
return false
}
let transaction = try checkVerified(result)
return transaction.revocationDate == nil && !transaction.isUpgraded
}
/// 监听交易更新,在 app 启动时开启监听
func listenForTransactions() -> Task.Handle<Void, Error> {
return detach {
for await result in Transaction.listener {
do {
// 和支付响应一样,检查支付验证结果
let transaction = try self.checkVerified(result)
// 更新已支付的内容状态以及 UI
await updatePurchasedIdentifiers(transaction)
await transaction.finish()
} catch {
print("Transaction failed verification")
}
}
}
}
为了测试交易监听,需要在 Xcode 测试环境中开启
Ask To Buy
来模拟生成带有.pending
状态的交易。点击 configuration 文件,在 Editor 菜单开启Ask To Buy
,重新运行 App,再次点击支付,会弹出一个提示界面,点击Ask
,回到 Xcode,打开 Xcode transaction manager,可以看到待处理交易,选中,点击右上角的Approve
按钮即可。
交易历史
当 App 安装后,全部交易记录就准备好,以备随时获取。不同设备上的交易历史是实时自动同步的。可以使用上文中的示例,在 App 启动时注册监听,来响应更新。所以,基于新的 StoreKit 2,Restore
按钮不再是必选项了。
因为在 StoreKit 2 中所有的更新等都是自动完成的,但为了以防万一,如果用户觉得自己的交易没有被加载,应用可以使用 Sync API,允许用户主动触发同步。
订阅状态
Latest transaction
近期交易
Renewal state
枚举类型,可以方便的检查该订阅是否有效、过期等
@MainActor
func updateSubscriptionStatus() async {
do {
guard let product = store.subscriptions.first, let statuses = try await product.subscription?.status else { return }
// 因为状态包含同一项目等多种支付状态数据,例如个人、家庭共享等
var highestStatus: Product.SubscrriptionInfo.Status? = nil
var highestProduct: Product? = nil
for status in statuses {
switch status.state {
case .expired, .revoked:
continue
default:
let renewalInfo = try store.checkVerified(status.renewalInfo)
guard let newSubscription = store.subscriptions.first(where: { $0.id == renewalInfo.currentProductID }) else {
continue
}
guard let currentProduct = highestProduct else {
highestStatus = status
highestProduct = newSubscription
continue
}
let highestTier = store.tier(for: currentProduct.id)
let newTier = store.tier(for: renewalInfo.currentProductID)
if newTier > highestTier {
highestStatus = status
highestProduct = newSubscription
}
}
}
status = highestStatus
currentSubscription = highestProduct
} catch {
print("Could not update subscription status \(error)")
}
}
Renewal info
详细信息
via WWDC 2021:Meet StoreKit2 代码摘自 Implementing a Store In Your App Using the StoreKit API