1、视图代码 struct Settings: View { @ObservedObject var shared = IAPManager.shared var body: some View { Button(action: { shared.postIAPPay() }, label: { Text("内购") }) } } 2、内购代码 // // IAPManager.swift // ERdepot // // Created by 方君宇 on 2024/10/19. // import StoreKit @available(iOS 15.0, *) @MainActor class IAPManager:NSObject, ObservableObject { static let shared = IAPManager() private override init() {} @Published var productID = ["supportERdepot20241019"] // 需要内购的产品ID数组 @Published var selectedIosPid = "supportERdepot20241019" // 需要内购的产品ID数组 @Published var products: [Product] = [] // 存储从 App Store 获取的内购商品信息 @Published var loadPurchased = false // 如果开始内购流程,loadPurchased为true,View视图显示加载画布 private var receiptRefreshCompletion: ((Bool) -> Void)? // 视图自动加载loadProduct()方法 func loadProduct() async { print("调取loadProduct方法") do { // 传入 productID 产品ID数组,调取Product.products接口从App Store返回产品信息 // App Store会返回对应的产品信息,如果数组中个别产品ID有误,只会返回正确的产品ID的产品信息 let fetchedProducts = try await Product.products(for: productID) if fetchedProducts.isEmpty { // 判断返回的是否是否为空 // 抛出内购信息为空的错误,可能是所有的产品ID都不存在,中断执行,不会return返回products产品信息 throw StoreError.IAPInformationIsEmpty } DispatchQueue.main.async { self.products = fetchedProducts // 将获取的内购商品保存到products变量 print("成功加载产品: \(fetchedProducts)") // 输出内购商品数组信息 } } catch { print("加载产品失败:\(error)") // 输出报错 } } // purchaseProduct:购买商品的方法,返回购买结果 func purchaseProduct(_ product: Product) { // 在这里输出要购买的商品id print("Purchasing product: \(product.id)") Task { @MainActor in do { let result = try await product.purchase() switch result { case .success(let verification): // 购买成功的情况,返回verification包含交易的验证信息 let transaction = try checkVerified(verification) // 验证交易 savePurchasedState(for: product.id) // 更新UserDefaults中的购买状态 await transaction.finish() // 告诉系统交易完成 print("交易成功:\(result)") case .userCancelled: // 用户取消交易 print("用户取消交易:\(result)") case .pending: // 购买交易被挂起 print("购买交易被挂起:\(result)") default: // 其他情况 throw StoreError.failedVerification // 购买失败 } } catch { print("购买失败:\(error)") await resetProduct() // 购买失败后重置 product 以便允许再次尝试购买 } DispatchQueue.main.async { self.loadPurchased = false // 隐藏内购时的加载画布 } print("loadPurchased:\(loadPurchased)") } } // 验证购买结果 func checkVerified(_ result: VerificationResult) throws -> T { switch result { case .unverified: // unverified校验失败,StoreKit不能确定交易有效 print("校验购买结果失败") throw StoreError.failedVerification case .verified(let signedType): // verfied校验成功 print("校验购买结果成功") return signedType // StoreKit确认本笔交易信息由苹果服务器合法签署 } } // handleTransactions处理所有的交易情况 func handleTransactions() async { for await result in Transaction.updates { // 遍历当前所有已完成的交易 do { let transaction = try checkVerified(result) // 验证交易 // 处理交易,例如解锁内容 savePurchasedState(for: transaction.productID) await transaction.finish() } catch { print("交易处理失败:\(error)") } } } // 当购买失败时,会尝试重新加载产品信息。 func resetProduct() async { self.products = [] await loadProduct() // 调取loadProduct方法获取产品信息 } // 保存购买状态到用户偏好设置或其他存储位置 func savePurchasedState(for productID: String) { UserDefaults.standard.set(true, forKey: productID) print("Purchased state saved for product: \(productID)") } // 通过productID检查是否已完成购买 func loadPurchasedState(for productID: String) -> Bool{ let isPurchased = UserDefaults.standard.bool(forKey: productID) // UserDefaults读取购买状态 print("Purchased state loaded for product: \(productID) - \(isPurchased)") return isPurchased // 返回购买状态 } func showNetErrorToast() { print("报错了") } //拉起苹果支付 func postIAPPay(){ guard !selectedIosPid.isEmpty else { showNetErrorToast() return } // 调起购买 print("调起购买-\(selectedIosPid)") /*ProgressHUD.animate("loading…", AnimationType.circleBarSpinFade,interaction: true)*/ Task { @MainActor in let products = try await Product.products(for: [selectedIosPid]) guard let product = products.first else { print("未找到产品,检查产品 ID 是否正确") throw StoreError.IAPInformationIsEmpty } // 用户确认购买,开始购买流程 let result = try await product.purchase() switch result{ case .success((let verification)): switch verification { case .verified(let transaction): print("交易success = \(verification)") // 延迟 1.2 秒,显示成功的进度指示器 // ProgressHUD.succeed("succeed", delay: 1.2) // 完成交易 await transaction.finish() // 刷新收据 refreshReceiptAndVerify() case .unverified(_, let error): print("交易未验证成功,错误: \(error.localizedDescription)") } break case .userCancelled: //埋点 // DYNetworkManager.shared.postmaidianInfoWithArray(isPresentFromVC == 0 ? AppConfig.DYmdBuyPageCancel:AppConfig.DYmdDrawNoCountToBuyCancel) // 延迟 1.2 秒,显示失败的进度指示器 // ProgressHUD.failed("Cancelled",delay: 1.2) print(" 交易取消") break case .pending: // 延迟 1.2 秒,退出进度指示器 // ProgressHUD.dismiss() break @unknown default: showNetErrorToast() print(" 交易失败") } } } // 内购刷新收据凭据 private func refreshReceiptAndVerify() { let request = SKReceiptRefreshRequest() // 创建一个刷新收据的请求 request.delegate = self // 设置当前对象(通常是类的实例)为请求的代理 request.start() // 启动请求,开始刷新收据 print("刷新收据凭据") } // SKRequestDelegate // 修改 requestDidFinish 方法 func requestDidFinish(_ request: SKRequest) { print("收据刷新完成") // 确保在主线程处理 DispatchQueue.main.async { [weak self] in // 这里的self是弱引用 // 检查 self 是否为 nil,如果 self 为 nil,则会执行 else 语句 guard let self = self else { return } // 给系统一点时间确保票据已更新 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { guard let receiptURL = Bundle.main.appStoreReceiptURL else { print("获取 appStoreReceiptURL 失败") self.showNetErrorToast() return } do { let receiptData = try Data(contentsOf: receiptURL) let receiptString = receiptData.base64EncodedString() // 打印 Base64 编码后的收据字符串 print("成功获取收据,长度: \(receiptString.count)") print("Base64 编码的收据:\(receiptString)") // 发送收据 print("发送收据") // self.postCheckOrder(receiptString) } catch { print("读取收据失败: \(error.localizedDescription)") self.showNetErrorToast() } } } } func request(_ request: SKRequest, didFailWithError error: Error) { print("刷新收据失败: \(error.localizedDescription)") DispatchQueue.main.async { [weak self] in self?.showNetErrorToast() } } } // 定义 throws 报错 enum StoreError: Error { case IAPInformationIsEmpty case failedVerification } // 实现 SKRequestDelegate 方法 extension IAPManager: SKRequestDelegate { } 2、Xcode输出信息: 刷新收据凭据 收据刷新完成 成功获取收据,长度: 6920 Base64 编码的收据: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 发送收据 3、调用PostMan返回信息: { "receipt": { "receipt_type": "ProductionSandbox", "adam_id": 0, "app_item_id": 0, "bundle_id": "com.fangjunyu.ERdepot", "application_version": "1.0.4", "download_id": 0, "version_external_identifier": 0, "receipt_creation_date": "2025-02-26 05:12:07 Etc/GMT", "receipt_creation_date_ms": "1740546727000", "receipt_creation_date_pst": "2025-02-25 21:12:07 America/Los_Angeles", "request_date": "2025-02-26 05:13:39 Etc/GMT", "request_date_ms": "1740546819497", "request_date_pst": "2025-02-25 21:13:39 America/Los_Angeles", "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT", "original_purchase_date_ms": "1375340400000", "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles", "original_application_version": "1.0", "in_app": [ { "quantity": "1", "product_id": "supportERdepot20241019", "transaction_id": "2000000863256811", "original_transaction_id": "2000000863256811", "purchase_date": "2025-02-26 04:53:39 Etc/GMT", "purchase_date_ms": "1740545619000", "purchase_date_pst": "2025-02-25 20:53:39 America/Los_Angeles", "original_purchase_date": "2025-02-26 04:53:39 Etc/GMT", "original_purchase_date_ms": "1740545619000", "original_purchase_date_pst": "2025-02-25 20:53:39 America/Los_Angeles", "is_trial_period": "false", "in_app_ownership_type": "PURCHASED" } ] }, "environment": "Sandbox", "status": 0 }