StoreKit2
对iOS内购进行了重构,相较于原版本StoreKit
,API改动较大,且更高效简洁。 StoreKit2
抛弃了Objective-C,仅支持Swift且最低支持版本为iOS15。
swift
/// Contains properties and methods to facilitate interactions between your app and the App Store.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
其中,使用了Swift语言的一些新的特性:
- @aync/@await: Swift5.5推出的多线程编程API。
- @MainActor: 防止应用在多线程中造成数据竞争,是保证多线程安全性的新类型。
- JWS:JSON Web Signature,是一套加密校验体系,在
StoreKit2
中通过此校验体系来校验订单。
使用StoreKit2
,可以更简单地实现内购流程,所有请求可以在回调中返回,而无需通过监听代理方法而造成发起支付和支付结果返回的代码割裂。内购流程也更加清晰,再配合最新的App Store Server API,可以为iOS内购流程带来很大程度上的优化。
获取商品(Product)
swift
@MainActor
/// 通过 productIds 请求 Product 列表
/// - Parameter productIds: product ids
/// - Returns: Product 列表
func requestProducts(productIds: [String]) async -> [Product]? {
products = try? await Product.products(for: Set.init(productIds))
return products
}
判断商品类型
swift
/// Array of consumable products
public var consumableProducts: [Product]? {
guard products != nil else {
return nil
}
return products?.filter({ product in
product.type == .consumable
})
}
/// Array of nonConsumbale products
public var nonConsumbaleProducts: [Product]? {
guard products != nil else {
return nil
}
return products?.filter({ product in
product.type == .nonConsumable
})
}
/// Array of subscriptio products
public var subscriptionProducts: [Product]? {
guard products != nil else {
return nil
}
return products?.filter({ product in
product.type == .autoRenewable
})
}
/// Array of nonSubscription products
public var nonSubscriptionProducts: [Product]? {
guard products != nil else {
return nil
}
return products?.filter({ product in
product.type == .nonRenewable
})
}
发起支付
swift
/// 发起支付
/// - Parameter product: Product对象
public func purchase(product: Product, uid: String) async throws -> (transaction: Transaction?, purchaseState: PurchaseState) {
guard purchaseState != .inProgress else {
throw PurchaseException.purchaseInProgressException
}
purchaseState = .inProgress
//App account token
//用于将用户 ID 绑定到交易(Transcation)中,即可建立苹果的交易订单数据与用户信息的映射关系,方便数据整合与追溯
let uuid = Product.PurchaseOption.appAccountToken(UUID.init(uuidString: uid)!)
//发起支付流程
guard let res = try? await product.purchase(options: [uuid]) else {
purchaseState = .failed
throw PurchaseException.transactionVerificationFailed
}
var validateTransaction: Transaction? = nil
switch res {
case .success(let verificationResult):
//购买状态:成功
let checkResult = checkTransactionVerificationResult(verificationResult)
if !checkResult.verified {
purchaseState = .failedVerification
throw PurchaseException.transactionVerificationFailed
}
validateTransaction = checkResult.transaction
await validateTransaction!.finish()
purchaseState = .complete
case .userCancelled:
//购买状态:用户取消
purchaseState = .cancelled
case .pending:
//购买状态:进行中
purchaseState = .pending
default:
//购买状态:未知
purchaseState = .unknown
}
return (transaction: validateTransaction, purchaseState: purchaseState)
}
这里可以将用户的uid
与Product
进行绑定,通过支付事务透传,完成支付流程后,从返回的Transaction
中可获取appAccountToken
参数,以便合并数据。
验证票据
StoreKit2
可在客户端本地验证票据,而无需依赖服务端。
swift
private func checkTransactionVerificationResult(_ result: VerificationResult<Transaction>) -> (transaction: Transaction, verified: Bool) {
//Check whether the JWS parses StoreKit verification.
switch result {
case .unverified(let transaction, _):
//StoreKit parses the JWS, but it fails verification.
return (transaction: transaction, verified: false)
case .verified(let transaction):
//The reult is verified. Return the unwrapped value.
return (transaction: transaction, verified: true)
}
}
监听订单
swift
private func listenForTransaction() -> Task<Void, Error> {
return Task.detached {
for await verificationResult in Transaction.updates {
let checkResult = self.checkTransactionVerificationResult(verificationResult)
if checkResult.verified {
let validatedTransaction = checkResult.transaction
await validatedTransaction.finish()
} else {
print("Transaction failed verification.")
}
}
}
}
调用
swift
let purchaseManager: InAppPurchaseManager = InAppPurchaseManager()
Task.init {
//获取 Product 列表
let products = await purchaseManager.requestProducts(productIds: ["com.purdoctid.1", "purdoctid.6", "purdoctid.10"])
print("获取所有商品: \(products!.count)")
//发起内购
do {
let product = purchaseManager.product(from: "purdoctid.10")!
let res = try await purchaseManager.purchase(product: product, uid: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")
//支付完成,发送凭据给服务端验证,请求发货。
print("transaction id: \(res.transaction!.originalID), \(res.transaction!.originalPurchaseDate)")
} catch {
print("Purchase failed")
}
}
支付结果的验证其实客户端可调用checkTransactionVerificationResult
自行认证,但该过程是由客户端借助StoreKit2
向Apple Server发起请求完成的,客户端不负责该过程的具体实现。 因此为了安全性和稳定性考虑,需要支付Server介入对支付结果进行验证,再由支付Server根据验证的结果决定是否发货,再通知客户端完成支付流程。
都2032年了,你还没用StoreKit2吗?如果还没有,其实你可以先试试App Store Server API~~