import UIKit import StoreKit @objc public protocol JXIAPManagerDelegate { /// 购买成功 @objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String) /// 购买失败 @objc optional func jx_iapPayFailed(productId: String, code: JXIAPManagerCode, msg: String?) /// 恢复商品(仅限永久有效商品) @objc optional func iapPayRestore(productIds: [String], transactionIds: [String]) // /// 加载 // @objc optional func iapPayShowHud() // /// 系统错误 // @objc optional func iapSysWrong() // /// 验证成功 // @objc optional func verifySuccess() // /// 验证失败 // @objc optional func verifyFailed() } @objc public enum JXIAPManagerCode: Int { ///未知错误 case unknown ///取消交易 case cancelled ///没有商品 case noProduct } public class JXIAPManager: NSObject { public static let manager: JXIAPManager = JXIAPManager() public weak var delegate: JXIAPManagerDelegate? private var payment: SKPayment? private var product: SKProduct? private var productId: String? private var discount: SKPaymentDiscount? private var orderId: String? private var applicationUsername: String? private var receiptCompletion: ((URL?) -> Void)? override init() { super.init() SKPaymentQueue.default().add(self) } public func start(productId: String, orderId: String, applicationUsername: String?, discount: SKPaymentDiscount? = nil) { self.product = nil self.productId = productId self.orderId = orderId self.discount = discount self.applicationUsername = applicationUsername let set = Set([productId]) let productsRequest = SKProductsRequest(productIdentifiers: set) productsRequest.delegate = self productsRequest.start() } /// 购买商品 private func buyProduct() { guard let product = self.product else { return } // 要购买商品,开个小票 let payment = SKMutablePayment(product: product) payment.applicationUsername = self.applicationUsername if let discount = self.discount { payment.paymentDiscount = discount self.discount = nil } self.payment = payment // 去收银台排队,准备购买 SKPaymentQueue.default().add(payment) } } //MARK: -------------- SKProductsRequestDelegate -------------- extension JXIAPManager: SKProductsRequestDelegate { public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { guard let product = response.products.first else { DispatchQueue.main.async { if let productId = self.productId { self.productId = nil self.delegate?.jx_iapPayFailed?(productId: productId, code: .noProduct, msg: nil) } } return } self.product = product self.buyProduct() } } //MARK: -------------- SKPaymentTransactionObserver -------------- extension JXIAPManager: SKPaymentTransactionObserver { ///购买回调 public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .purchased: DispatchQueue.main.async { self.completeTransaction(transaction: transaction) } SKPaymentQueue.default().finishTransaction(transaction) case .failed: DispatchQueue.main.async { self.failedTransaction(transaction: transaction) } SKPaymentQueue.default().finishTransaction(transaction) // case .restored: // self.restoreTransaction(transaction: transaction) case .purchasing: break default: SKPaymentQueue.default().finishTransaction(transaction) break } } } // func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool { // return true // } /// 恢复购买回调 public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { } } extension JXIAPManager { private func completeTransaction(transaction: SKPaymentTransaction) { guard let encodeStr = getAppStoreReceipt() else { return } guard let transactionIdentifier = transaction.transactionIdentifier else { return } guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return } self.productId = nil self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier) } private func failedTransaction(transaction: SKPaymentTransaction) { let error = transaction.error as? SKError guard let productId = self.productId else { return } self.productId = nil switch error?.code { case SKError.paymentCancelled: self.delegate?.jx_iapPayFailed?(productId: productId, code: .cancelled, msg: error?.localizedDescription) default: self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown, msg: error?.localizedDescription) } } } extension JXIAPManager: SKRequestDelegate { public func getAppStoreReceipt() -> String? { guard let receiptURL = Bundle.main.appStoreReceiptURL else { return nil } let receiptData = NSData(contentsOf: receiptURL) return receiptData?.base64EncodedString(options: .endLineWithLineFeed) } ///获取凭证 public func fetchReceipt(completion: @escaping (URL?) -> Void) { let receiptURL = Bundle.main.appStoreReceiptURL if let url = receiptURL, FileManager.default.fileExists(atPath: url.path) { completion(url) return } let request = SKReceiptRefreshRequest() request.delegate = self request.start() // 在 delegate 回调里处理 self.receiptCompletion = completion } public func requestDidFinish(_ request: SKRequest) { let receiptURL = Bundle.main.appStoreReceiptURL if let url = receiptURL, FileManager.default.fileExists(atPath: url.path) { receiptCompletion?(url) } else { receiptCompletion?(nil) } } public func request(_ request: SKRequest, didFailWithError error: Error) { print("Receipt request failed: \(error)") receiptCompletion?(nil) } }