// // MP_IAPManager.swift // relax.offline.mp3.music // // Created by Mr.Zhou on 2024/7/29. // import UIKit import SwiftyStoreKit import StoreKit ///内部购买项目管理器 class MP_IAPManager: NSObject { ///单例 static let shared = MP_IAPManager() ///产品ID(根据开关获取) var productIdentifiers:[String] = ["1weekvip","1yearvip","lifetimevip"] ///产品交易信息组 private var transactions:[PaymentTransaction] = [] ///产品请求(根据ID获取产品信息) private var productsRequest:SKProductsRequest? ///可用产品组 private var availableProducts:Set = [] ///调用方案 private var showHUD:Bool = false ///是否是VIP var isVIP:Bool { if let purchasedProducts = UserDefaults.standard.array(forKey: "PurchasedProducts") as? [String], !purchasedProducts.isEmpty { //存在旧版购买数据,默认为VIP return true }else { //获取VIP过期时间 guard let lastDate = UserDefaults.standard.object(forKey: "PurchaseVIPDate") as? Date, lastDate.timeIntervalSince(Date()) > 0 else { return false } return true } } override init() { super.init() } deinit { } ///校正内购产品交易情况 func observeVIPStoreKit() { //清理原有的交易信息组 transactions.removeAll() //执行交易信息组校正处理 SwiftyStoreKit.completeTransactions { [weak self] purchases in guard let self = self else { return } var productId = "" //检查交易情况 print("检索交易") purchases.forEach { item in switch item.transaction.transactionState { case .purchased, .restored://交易完成/处理中 //检查交易是否需要强制终结 if item.needsFinishTransaction { SwiftyStoreKit.finishTransaction(item.transaction) } //更新产品ID productId = item.productId //添加交易信息组 self.transactions.append(item.transaction) case .failed://交易失败 //检查交易是否需要强制终结 if item.needsFinishTransaction { SwiftyStoreKit.finishTransaction(item.transaction) } default: break } } //交易检索完毕,查看是否有订阅的产品 if !productId.isEmpty { //存在交易产品,重新验证一下流程 print("当前用户有VIP记录,进行验证") self.showHUD = false self.purchaseVerificationProductVIP(with: productId) }else { //无交易内容 print("当前用户非VIP") } } if let purchasedProducts = UserDefaults.standard.array(forKey: "PurchasedProducts") as? [String], !purchasedProducts.isEmpty { restorePurchases(true) } } ///获取内购产品信息 func getVIPAllProducts() { let productIds:Set = Set(productIdentifiers) SwiftyStoreKit.retrieveProductsInfo(productIds) { [weak self] results in guard let self = self else { return } //检索产品信息 if !results.retrievedProducts.isEmpty { self.availableProducts = results.retrievedProducts } } } ///对指定产品进行购买 func purchaseProduct(with productId:String) { showHUD = true MP_HUD.loading() //判断当前的购买状态是否可以继续执行 if SwiftyStoreKit.canMakePayments { //可以继续购买项目 MP_AnalyticsManager.shared.VIP_continue_clickAction(productId) self.pruchaseProductVIP(with: productId) }else { //不可以购买项目 MP_HUD.error("Sorry. Subscription purchase failed", delay: 1.0, completion: nil) MP_AnalyticsManager.shared.VIP_buy_failureAction(productId, error: "The current project status is not supported") } } ///用户重新检索交易 func restorePurchases(_ isLaunch:Bool = false) { if isLaunch == true { showHUD = false }else { MP_HUD.loading() showHUD = true } self.transactions.removeAll() SwiftyStoreKit.restorePurchases(atomically: false) { [weak self] results in guard let self = self else { return } if !results.restoredPurchases.isEmpty, let productId = results.restoredPurchases.first?.productId { //restore成功 self.transactions.append(contentsOf: results.restoredPurchases.map({$0.transaction})) self.purchaseVerificationProductVIP(with: productId) return } if !results.restoreFailedPurchases.isEmpty { MP_HUD.error("Restore purchase failed", delay: 1.0, completion: nil) return } MP_HUD.error("No purchase can be restored", delay: 1.0, completion: nil) } } } //MARK: - 交易执行 extension MP_IAPManager { ///执行交易 private func pruchaseProductVIP(with productId:String) { //优先移除交易信息组 self.transactions.removeAll() SwiftyStoreKit.purchaseProduct(productId, quantity: 1, atomically: false) { [weak self] (result) in guard let self = self else { return } switch result { case .success(let product)://订阅成功 print("VIP Purchase Successfully.") //更新订阅信息 self.transactions.append(product.transaction) //检索交易信息 self.purchaseVerificationProductVIP(with: product.productId) case .error(let error)://订阅失败 MP_HUD.error("Sorry. Subscription purchase failed", delay: 1.0, completion: nil) MP_AnalyticsManager.shared.VIP_buy_failureAction(productId, error: "The current project status is not supported") print("VIP: \(error.localizedDescription)") } } } ///交易验证 private func purchaseVerificationProductVIP(with productId:String) { //生成一个验证服务器 let receiptValidator = AppleReceiptValidator(service: checkIsSandbox() ? .sandbox:.production, sharedSecret: "d29627e4f78b4b50a0ce5166acd8aa9f") SwiftyStoreKit.verifyReceipt(using: receiptValidator, forceRefresh: true) { [weak self] result in guard let self = self else { return } switch result { case .success(let receipt)://验证成功 print("VIP Receipt: \(receipt)") //检索是否为终身项目 if productId == "lifetimevip" { //终身项目 let purchaseResult = SwiftyStoreKit.verifyPurchase(productId: productId, inReceipt: receipt) switch purchaseResult { case .purchased(let item): print("VIP LifeTime verifyPurchase successed current item: \(item)") //到期时间一百年后 let lifeDate = Date().addingTimeInterval(60 * 60 * 24 * 365 * 100) //本地记录VIP到期时间 UserDefaults.standard.set(lifeDate, forKey: "PurchaseVIPDate") UserDefaults.standard.synchronize() //结算交易信息 self.showSuccessfullyHUD() self.finishedAllTransactionsVIP() case .notPurchased://订阅失败 self.finishedAllTransactionsVIP() self.showFailedHUD() } }else { //非终身项目,要计算项目时间 let purchaseResult = SwiftyStoreKit.verifySubscription(ofType: .autoRenewable, productId: productId, inReceipt: receipt) switch purchaseResult { case .purchased(let expiryDate, let items): print("VIP \(productId) valid until \(expiryDate)") UserDefaults.standard.set(expiryDate, forKey: "PurchaseVIPDate") UserDefaults.standard.synchronize() //结算交易信息 self.showSuccessfullyHUD() self.finishedAllTransactionsVIP() case .expired(let expiryDate, let items)://过期了 self.finishedAllTransactionsVIP() self.showFailedHUD() case .notPurchased://订阅失败 self.finishedAllTransactionsVIP() self.showFailedHUD() } } //清理旧版信息 if let purchasedProducts = UserDefaults.standard.array(forKey: "PurchasedProducts") as? [String], !purchasedProducts.isEmpty { print("清理旧版VIP信息") UserDefaults.standard.set(nil, forKey: "PurchasedProducts") } case .error(let error)://验证失败 self.finishedAllTransactionsVIP() self.showFailedHUD() } } } //展示交易成功 private func showSuccessfullyHUD() { if showHUD == true { MP_HUD.success("Successfully purchased", delay: 1.5, completion: nil) } } //展示交易失败 private func showFailedHUD() { if showHUD == true { MP_HUD.error("Failed purchased", delay: 1.5, completion: nil) } } //结算交易信息组 private func finishedAllTransactionsVIP() { self.transactions.forEach { item in SwiftyStoreKit.finishTransaction(item) } self.transactions.removeAll() } private func checkIsSandbox() -> Bool { var isSandbox = false #if DEBUG isSandbox = true #endif return isSandbox } }