都2032年了,你还没使用StoreKit2吗?

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)
    }

这里可以将用户的uidProduct进行绑定,通过支付事务透传,完成支付流程后,从返回的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~~

相关推荐
耶啵奶膘2 小时前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
视频砖家2 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
lyj1689973 小时前
vue-i18n+vscode+vue 多语言使用
前端·vue.js·vscode
小白变怪兽4 小时前
一、react18+项目初始化(vite)
前端·react.js
ai小鬼头4 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
墨菲安全5 小时前
NPM组件 betsson 等窃取主机敏感信息
前端·npm·node.js·软件供应链安全·主机信息窃取·npm组件投毒
GISer_Jing5 小时前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript
天涯学馆5 小时前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
我在北京coding6 小时前
TypeError: Cannot read properties of undefined (reading ‘queryComponents‘)
前端·javascript·vue.js
前端开发与ui设计的老司机7 小时前
UI前端与数字孪生结合实践探索:智慧物流的货物追踪与配送优化
前端·ui