203 lines
7.2 KiB
Swift
203 lines
7.2 KiB
Swift
//
|
||
// 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()
|
||
}
|