iOS内购——自动续订型会员集成StoreKit1和StoreKit2

StoreKit1

import UIKit
import StoreKit

//兼容iOS15以前
class StoreKitManager1: NSObject {
    //产品ID(非消耗型或消耗型均可)
    private let productID = "com.your.app.consumable"
    //本地保存的Key
    private let purchaseKey = "com.your.app.purchased"
    //当前请求的产品(用于缓存)
    private var product: SKProduct?
    //购买完成回调(可选)
    var purchaseCompletion: ((Bool) -> Void)?
    //本地检查是否已购买
    var isPurchasedLocally: Bool {
        return UserDefaults.standard.bool(forKey: purchaseKey)
    }
    
    //shared
    static let shared = StoreKitManager1()
    private override init() {
        super.init()
    }
    
    //启动时设置观察者
    func setup() {
        SKPaymentQueue.default().add(self)
    }
    
    //请求产品信息(通常在购买前调用)
    func fetchProducts() {
        let request = SKProductsRequest(productIdentifiers: Set([productID]))
        request.delegate = self
        request.start()
    }
    
    //购买
    func buyProduct() {
        guard let product = self.product else {
            //如果没有缓存产品,先请求再购买(实际可结合回调)
            fetchProducts()
            return
        }
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
    }
    
}

extension StoreKitManager1: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        if let product = response.products.first {
            self.product = product
            print("get product success: \(product.productIdentifier)")
        } else {
            print("no checkout products")
        }
    }
    
    func request(_ request: SKRequest, didFailWithError error: any Error) {
        print("get product error: \(error.localizedDescription)")
    }
    
}

extension StoreKitManager1: SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                //购买成功
                UserDefaults.standard.set(true, forKey: purchaseKey)
                UserDefaults.standard.synchronize()   // 可选
                print("购买成功,已本地保存状态")
                // 完成交易
                SKPaymentQueue.default().finishTransaction(transaction)
                purchaseCompletion?(true)
            case .failed:
                //购买失败
                print("购买失败: \(transaction.error?.localizedDescription ?? "未知错误")")
                SKPaymentQueue.default().finishTransaction(transaction)
                purchaseCompletion?(false)
            case .restored:
                // 恢复购买(非消耗型或自动续期订阅)
                // 同样保存状态
                UserDefaults.standard.set(true, forKey: purchaseKey)
                UserDefaults.standard.synchronize()
                print("购买恢复成功,已本地保存")
                SKPaymentQueue.default().finishTransaction(transaction)
                purchaseCompletion?(true)
            case .purchasing, .deferred:
                // 正在购买或等待确认,无需处理
                break
            @unknown default:
                break
            }
        }
    }
    
}

StoreKit2(适用于iOS15及之后)

import UIKit
import StoreKit

class StoreKitManager2 {
    private let productID = "com.your.app.product"
    private let userID = "user_123"
    
    //单例
    static let shared = StoreKitManager2()
    private init() {
        startTransactionListener()
    }
    
    private(set) var isPurchased: Bool = false {
        didSet {
            DispatchQueue.main.async { [weak self] in
                guard let `self` = self else { return }
                self.purchaseStatusChanged?(self.isPurchased)
            }
        }
    }
    private var products: [Product] = []
    private var isPurchasing: Bool = false
    private var transactionListenerTask: Task<Void, Never>?
    //购买状态变化
    public var purchaseStatusChanged: ((Bool) -> ())?
    //产品信息加载完成
    public var productsLoaded: (([Product]) -> ())?
    //购买尝试完成(成功/失败)
    public var purchaseCompleted: ((Bool) -> ())?
    
    //启动监听
    private func startTransactionListener() {
        transactionListenerTask = Task.detached { [weak self] in
            for await verificationResult in Transaction.updates {
                guard let `self` = self else { break }
                await self.handleTransactionUpdate(verificationResult)
            }
        }
    }
    
    //加载产品
    public func loadProducts() {
        Task { [weak self] in
            guard let `self` = self else { return }
            do {
                let productList = try await Product.products(for: [self.productID])
                await MainActor.run {
                    self.products = productList
                    self.productsLoaded?(productList)
                }
            } catch {
                print("加载产品失败:\(error.localizedDescription)")
            }
        }
    }
    
    //购买
    public func purchase() {
        guard !isPurchasing, let product = products.first else { return }
        Task { [weak self] in
            guard let `self` = self else { return }
            await MainActor.run {
                self.isPurchasing = true
            }
            defer {
                Task { @MainActor in
                    self.isPurchasing = false
                }
            }
            do {
                let result = try await product.purchase()
                switch result {
                case .success(let verificationResult):
                    let transaction = try self.checkVerification(verificationResult)
                    let valid = await self.verifyWithServer(transaction)
                    if valid {
                        await transaction.finish()
                        await self.fetchPurchaseStatus()
                        await MainActor.run {
                            self.purchaseCompleted?(true)
                        }
                    } else {
                        //服务器验证失败
                        await MainActor.run {
                            self.purchaseCompleted?(false)
                        }
                    }
                case .userCancelled:
                    //用户取消购买
                    await MainActor.run {
                        self.purchaseCompleted?(false)
                    }
                case .pending:
                    print("交易等待中")
                @unknown default:
                    break
                }
            } catch {
                print("购买出错:\(error.localizedDescription)")
                await MainActor.run {
                    self.purchaseCompleted?(false)
                }
            }
        }
    }
    
    //刷新状态
    public func refreshPurchaseStatus() {
        Task { [weak self] in
            await self?.fetchPurchaseStatus()
        }
    }
    
    deinit {
        transactionListenerTask?.cancel()
    }
    
    //处理交易更新(来自监听)
    private func handleTransactionUpdate(_ verificationResult: VerificationResult<Transaction>) async {
        do {
            let transaction = try checkVerification(verificationResult)
            guard transaction.revocationDate == nil && !transaction.isUpgraded else {
                //交易被撤销,重新从服务器获取状态
                await fetchPurchaseStatus()
                await transaction.finish()
                return
            }
            if transaction.productID == self.productID {
                // 如果发现有未完成的购买,再次验证(通常购买流程已处理)
                let valid = await verifyWithServer(transaction)
                if valid {
                    await transaction.finish()
                    await fetchPurchaseStatus()
                }
            }
        } catch {
            print("交易失败,有内鬼:\(error.localizedDescription)")
        }
    }
    
    //验证交易签名
    private func checkVerification<T>(_ result: VerificationResult<T>) throws -> T {
        switch result {
        case .unverified(let signedType, let verificationError):
            throw verificationError
        case .verified(let signedType):
            return signedType
        }
    }
    
    //从服务器获取购买状态(模拟)
    private func fetchPurchaseStatus() async {
        //模拟网络延迟
        try? await Task.sleep(nanoseconds: 800_000_000)
        let request = [
            "userId": userID,
            "productId": productID
        ]
        print("查询购买状态: \(request)")
        // 模拟服务器返回(这里固定为 true,实际可改为从网络获取)
        let serverStatus = true
        print("服务器返回状态: \(serverStatus)")
        await MainActor.run {
            self.isPurchased = serverStatus
        }
    }
    
    //模拟服务器验证交易
    private func verifyWithServer(_ transaction: Transaction) async -> Bool {
        //模拟网络延迟
        try? await Task.sleep(nanoseconds: 1_500_000_000)
        let payload = [
            "transactionId": "\(transaction.id)",
            "productId": transaction.productID,
            "userId": userID
        ]
        //模拟服务器响应(假设总是成功)
        let serverResponse = ["valid": true]
        return (serverResponse["valid"]) ?? false
    }
    
}

控制器里调用

import UIKit

class StoreKitManagerViewController: UIViewController {
    //
    private let storeManager2 = StoreKitManager2.shared
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
    
    func storeKitManager1() {
        //在 App 启动时初始化(例如在 AppDelegate 或 SceneDelegate 中):
        StoreKitManager1.shared.setup()
        // 如果需要预加载产品信息
        StoreKitManager1.shared.fetchProducts()
    }
    
    //购买
    func buyTapped1() {
        StoreKitManager1.shared.buyProduct()
        
    }
    
    //是否购买完成
    func buyResult1() {
        print(StoreKitManager1.shared.isPurchasedLocally)
    }
    
    
    
    
    /********************************************/
    
    func storeKitManager2() {
        storeManager2.purchaseStatusChanged = { [weak self] isPurchased in
            //购买状态
            print(isPurchased)
        }
        storeManager2.productsLoaded = { [weak self] products in
            if let product = products.first {
                print("产品信息:\(product)")
            }
        }
        storeManager2.purchaseCompleted = { [weak self] isSuccess in
            print("是否成功购买:\(isSuccess)")
        }
        // 加载产品 & 刷新状态
        storeManager2.loadProducts()
        storeManager2.refreshPurchaseStatus()
    }
    
    //购买
    func buyTapped2() {
        storeManager2.purchase()
    }

    func restoreTapped2() {
        // 重新从服务器拉取
        storeManager2.refreshPurchaseStatus()
    }
    
}

有任何问题请留言讨论

posted @ 2026-07-20 11:52  哇哈爽  阅读(5)  评论(0)    收藏  举报