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 PIStoreError: 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 PIIAPRegulate: NSObject { public typealias KConditionBlock = (_ state :StoreConditionResult,_ param:Dictionary?) ->() @objc public var ConditionBlock: KConditionBlock! // 状态回调 var updateListenerTask: Task? = nil // 支付事件监听 var transactionMap :[String:Transaction] = [:]// 用于完成Id的缓存map @objc public static let shared = PIIAPRegulate() private override init() { // 单例需要保证private的私有性质 super.init() self.updateListenerTask = listenForTransactions() } func triggerConditionBlock(_ state: StoreConditionResult, _ param: [String: Any]? = nil) { if let ConditionBlock = ConditionBlock { ConditionBlock(state, param) } } // 退订 @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") } } // 购买某个产品 @objc public func demandCommodityThing(productId:String, uuid: String) async throws { triggerConditionBlock(.start) do { let list = [productId] let storeProducts = try await Product.products(for: Set(list)) guard let product = storeProducts.first else { // triggerConditionBlock(.noProduct) throw PIStoreError.noProduct } _ = try await purchase(product, uuid) } catch { triggerConditionBlock(.noProduct) throw PIStoreError.noProduct } } // 购买 private func purchase(_ product: Product, _ uuid: String) async throws -> Transaction? { triggerConditionBlock(.pay) guard let curUUID = UUID.init(uuidString: uuid) else{ triggerConditionBlock(.failedVerification) return nil } let result = try await product.purchase(options: [.appAccountToken(curUUID)]) switch result { case .success(let verification): // 用户购买完成 return try await verifiedAndAccomplish(verification) case .userCancelled: // 用户取消 triggerConditionBlock(.userCancelled) case .pending: // 此次购买被挂起 triggerConditionBlock(.pending) default: triggerConditionBlock(.unowned) } 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. triggerConditionBlock(.failedVerification) throw PIStoreError.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: key) // 这里不触发完成,等服务器验证再触发完成逻辑 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)") } } } // 事件完成处理 // @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) // } // } @objc public func verifyBusinessAccomplish(transactionID: String, completionHandler: @escaping (Bool, Error?) -> Void) { if let transaction = transactionMap[transactionID] { Task { await transaction.finish() transactionMap.removeValue(forKey: transactionID) completionHandler(true, nil) // 成功完成交易 } } else { Task { await getAllBusiness(transactionId: transactionID) completionHandler(false, nil) // 没有找到交易,但已尝试处理未完成交易 } } } @MainActor func uploadServer(for transactionId:String) async { let dic :Dictionary = ["transactionId":transactionId] triggerConditionBlock(.verifiedServer, dic) } // 支付监听事件 func listenForTransactions() -> Task { return Task.detached { for await result in Transaction.updates { do { let resultId = try result.payloadValue.id if !self.transactionMap.keys.contains(String(resultId)) { _ = try await self.verifiedAndAccomplish(result) } } catch { // 在这里处理错误 print("Error: \(error)") } } } } //获取推广内购商品 func Promotion() async -> [SKProduct]?{ let promotion = SKProductStorePromotionController() let prodicts = try? await promotion.promotionOrder() return prodicts } // 销毁调用 deinit { updateListenerTask?.cancel() } }