// // UserPayViewManager.swift // yinmeng-ios // // Created by yinmeng on 2024/3/1. // import UIKit import StoreKit @available(iOS 15.0, *) typealias Transaction = StoreKit.Transaction @available(iOS 15.0, *) typealias RenewalInfo = StoreKit.Product.SubscriptionInfo.RenewalInfo @available(iOS 15.0, *) typealias RenewalState = StoreKit.Product.SubscriptionInfo.RenewalState enum StoreError: Error { // 错误回调枚举 case FailedVerification case NoProduct } @objc public enum StoreConditionResult: Int64 { // 支付状态 case Start // 开始 case Pay // 进行苹果支付 case VerifiedServer // 服务器校验 case UserCancelled // 用户取消 case Pending // 等待(家庭用户才有的状态) case Unowned case NoProduct //没有商品 case FailedVerification //验证失败 } @available(iOS 15.0, *) public class UserPayViewManager: NSObject { public typealias _KConditionBlock = (_ state :StoreConditionResult,_ param:Dictionary?) ->() @objc public var _ConditionBlock: _KConditionBlock! // 状态回调 var _updateListenerTask: Task? = nil // 支付事件监听 var _transactionMap :[String:Transaction] // 用于完成Id的缓存map var _name: String = "" // 单例的写法 @objc public static let shared = { let instance = UserPayViewManager() return instance }() private override init() { // 单例需要保证private的私有性质 _transactionMap = [:] // 初始化 super.init() Task { _updateListenerTask = _listenForTransactions() } } // 购买某个产品 @objc public func _demandCommodityThing(productId:String, uuid: String) async throws { if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.Start,nil) } do { let list:[String] = [productId] let storeProducts = try await Product.products(for: Set.init(list)) if storeProducts.count > 0 { try await _purchase(storeProducts[0],uuid) }else { print("iap: no found product") if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.NoProduct,nil) } throw StoreError.NoProduct // 没有该产品 } } catch { print("Failed product request from the App Store server: \(error)") if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.NoProduct,nil) } throw StoreError.NoProduct // 没有该产品 } } // 购买 private func _purchase(_ product: Product, _ uuid: String) async throws -> Transaction? { if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.Pay,nil) } guard let curUUID = UUID.init(uuidString: uuid) else{ if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.FailedVerification,nil) } return nil } let getUUID = Product.PurchaseOption.appAccountToken(curUUID) let result = try await product.purchase(options: [getUUID]) switch result { case .success(let verification): // 用户购买完成 let transaction = try await _verifiedAndAccomplish(verification) return transaction case .userCancelled: // 用户取消 if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.UserCancelled,nil) } return nil case .pending: // 此次购买被挂起 if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.Pending,nil) } return nil default: if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.Unowned,nil) } return nil } } // 校验 func _checkVerified(_ result: VerificationResult) throws -> T { //Check whether the JWS passes StoreKit verification. switch result { case .unverified: //StoreKit parses the JWS, but it fails verification. if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.FailedVerification,nil) } throw StoreError.FailedVerification case .verified(let safe): //The result is verified. Return the unwrapped value. print("iap: verified success") return safe } } // 校验&完成后传给服务器 func _verifiedAndAccomplish(_ verification:VerificationResult) async throws -> Transaction?{ //Check whether the transaction is verified. If it isn't, //this function rethrows the verification error. let transaction = try _checkVerified(verification) // 这里将订单提交给服务器进行验证 ~~~ let transactionId = try verification.payloadValue.id // 添加进入待完成map let key = String(transactionId) _transactionMap[key] = transaction await _uploadServer(for: transactionId) // 这里不触发完成,等服务器验证再触发完成逻辑 await transaction.finish() print("iap: finish") return transaction } /*All transactions:全部的购买交易订单 Latest transactions:最新的购买交易订单。(分为订阅品项和除订阅品项外的所有类型二种) Current entitlements:当前用户有购买的权限。(全部的订阅品项、和非消耗品项) */ func _getAllBusiness(transactionId:String) async { let transactionIntId = UInt64(transactionId) for await result in Transaction.all { do { let tran = try _checkVerified(result) let resultId = try result.payloadValue.id if transactionIntId == resultId { await tran.finish() break } } catch let error { print("error:----\(error)") } } //Transaction.latest(for: "pid") } // 事件完成处理 @objc public func _verifyBusinessAccomplish(transaction:String) async{ if(_transactionMap[transaction] != nil){ await _transactionMap[transaction]!.finish() _transactionMap.removeValue(forKey: transaction) print("verifyBusinessFinish end") }else { await _getAllBusiness(transactionId: transaction) } } @MainActor func _uploadServer(for transactionId:UInt64) async { let dic :Dictionary = ["transactionId":transactionId] if(_ConditionBlock != nil ){ _ConditionBlock(StoreConditionResult.VerifiedServer,dic) } } // 退订 @objc public func refunRequest(view: UIView,transactionId:UInt64) async{ do { if let scene = await view.window?.windowScene{ try await Transaction.beginRefundRequest(for:transactionId , in: scene) } }catch{ print("iap error") } } // 支付监听事件 func _listenForTransactions() -> Task { return Task.detached { //Iterate through any transactions that don't come from a direct call to `purchase()`. // 修改update 为 unfinished? for await result in Transaction.updates { //会导致二次校验? do { print("iap: updates") print("result:\(result)") try await self._verifiedAndAccomplish(result) } catch { //StoreKit has a transaction that fails verification. Don't deliver content to the user. print("Transaction failed verification") } } } } // 销毁调用 deinit { _updateListenerTask?.cancel() } }