// // IAPTestView.swift // AIGrammar // // Created by oscar on 2024/6/19. // import SwiftUI import StoreKit enum IAPProductTest: String, CaseIterable { case premiumFeature1 = "grammar_1_month" case premiumFeature2 = "grammar_1_week" } class IAPManagerTest: ObservableObject { @Published var products: [Product] = [] @Published var purchasedProducts: [Product] = [] init() { Task { await requestProducts() await updatePurchasedProducts() await listenForTransactions() } } func requestProducts() async { do { let products = try await Product.products(for: IAPProductTest.allCases.map { $0.rawValue }) DispatchQueue.main.async { // 按照产品的权重排序 self.products = products // 打印每个产品及其相关变量 for product in self.products { print("--------------------------") print("Product ID: \(product.id)") print("Product Title: \(product.displayName)") print("Product Description: \(product.description)") print("Product Price: \(product.price)") print("Product displayPrice: \(product.displayPrice)") print("Product priceFormatStyle: \(product.priceFormatStyle)") print("Product subscriptionPeriodFormatStyle: \(product.subscriptionPeriodFormatStyle)") print("Product subscriptionPeriodUnitFormatStyle: \(product.subscriptionPeriodUnitFormatStyle)") print("--------------------------") } } } catch { print("Failed to fetch products: \(error.localizedDescription)") } } func buy(product: Product) async { do { let uuid = UUID() let token = Product.PurchaseOption.appAccountToken(uuid) print("purchase appAccountToken: \(uuid.uuidString)") let result = try await product.purchase(options: [token]) switch result { case .success(let verification): if case .verified(let transaction) = verification { // 在这里不需要处理交易完成,详细处理放在 listenForTransactions 中 // 如果购买成功了,再点击这个按钮,会直接到这里 print("Purchase initiated for product: \(product.id)") } case .userCancelled: print("User cancelled the purchase.") case .pending: print("Purchase is pending.") default: break } } catch { print("Failed to purchase product: \(error.localizedDescription)") } } func updatePurchasedProducts() async { for await result in Transaction.currentEntitlements { if case .verified(let transaction) = result { if let product = products.first(where: { $0.id == transaction.productID }) { DispatchQueue.main.async { self.purchasedProducts.append(product) } } } } } func listenForTransactions() async { for await transactionResult in Transaction.updates { if case .verified(let transaction) = transactionResult { if let product = products.first(where: { $0.id == transaction.productID }) { DispatchQueue.main.async { self.purchasedProducts.append(product) } } // 打印详细的交易信息,为什么会有历史交易? print("Transaction ID: \(transaction.id)") print("Product ID: \(transaction.productID)") print("Purchase Date: \(transaction.purchaseDate)") //print("Transaction State: \(transaction.revocationReason ?? "None")") //print("Original Transaction ID: \(transaction.originalID ?? "None")") // 获取票据数据 // 获取 jsonRepresentation let jsonData = try transaction.jsonRepresentation // 将 Data 转换为 String if let jsonString = String(data: jsonData, encoding: .utf8) { print("Transaction Receipt: \(jsonString)") } else { print("Failed to convert JSON data to string.") } await transaction.finish() } } } func restorePurchases() async { do { try await AppStore.sync() await updatePurchasedProducts() print("Purchases restored") } catch { print("Failed to restore purchases: \(error.localizedDescription)") } } } struct IAPTestView: View { @StateObject var iapManager = IAPManager() //@StateObject var iapManager = IAPManagerTest() var body: some View { VStack(spacing: 20) { if iapManager.products.isEmpty { Text("Loading products...") } else { ForEach(iapManager.products, id: \.id) { product in VStack { Text(product.displayName) .font(.title) Button("Buy \(product.displayName)") { Task { await iapManager.buy(product: product){ result in switch result { case .success(let message): logger.info("succ") case .failure(let error): logger.error("error") } } } } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(10) } } Button("Restore Purchases") { Task { await iapManager.restorePurchases(){ result in switch result { case .success(let message): logger.info("restore purchase succ. message: \(message)") case .failure(let error): logger.error("restore purchase error. message: \(error)") } } } } .padding() .background(Color.green) .foregroundColor(.white) .cornerRadius(10) } } .onAppear { Task { //await iapManager.requestProducts() } } } } #Preview { IAPTestView() }