订阅时传递app account token的StoreKit实现疑问
问题:订阅时传递App Account Token的实现方法
我需要在订阅内购时传递“app account token”,但不知道如何在现有StoreManager类中实现这个逻辑。我见过使用product.purchase(options: [.appAccountToken(token)])的写法,但不清楚怎么整合到当前代码里。
现有StoreManager代码
import Foundation import StoreKit final class StoreManager: NSObject { static let shared = StoreManager() let storeObserver = StoreObserver() var transactionState: SKPaymentTransactionState? var myProducts = [SKProduct]() private let subscriptionIndentifiers: Set<String> = [ XXX.Subscription.monthly.productId, XXX.Subscription.annual.productId ] private override init() { super.init() SKPaymentQueue.default().add(storeObserver) } func getProducts() { let request = SKProductsRequest(productIdentifiers: subscriptionIndentifiers) request.delegate = self request.start() } func selectedType(type: SubscribeType) { switch type { case .month: purchase(product: myProducts[1]) case .years: purchase(product: myProducts[0]) } } func purchase(product: SKProduct) -> Bool { if !StoreManager.shared.canMakePayments() { return false } else { if !UserDefaultService.isLogin() { return false } else { let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) } } return true } func canMakePayments() -> Bool { return SKPaymentQueue.canMakePayments() } } extension StoreManager: SKProductsRequestDelegate, SKRequestDelegate { func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { debugPrint("Did receive response") if !response.products.isEmpty { for fetchedProduct in response.products { DispatchQueue.main.async { self.myProducts.append(fetchedProduct) } } } for invalidIdentifier in response.invalidProductIdentifiers { debugPrint("Invalid identifiers found: \(invalidIdentifier)") } } func requestDidFinish(_ request: SKRequest) { DispatchQueue.main.async { debugPrint("request did finish ") } } }
解决方案
你看到的product.purchase(options:)是StoreKit 2的异步API,和原代码使用的StoreKit 1同步队列方式不同,要整合到现有代码里,需要做以下修改:
1. 重写purchase方法为异步函数
将原来同步返回Bool的purchase方法,改成支持async/await的异步版本,同时接收appAccountToken参数:
func purchase(product: SKProduct, appAccountToken: Data) async throws -> Bool { guard StoreManager.shared.canMakePayments(), UserDefaultService.isLogin() else { return false } let purchaseResult = try await product.purchase(options: [.appAccountToken(appAccountToken)]) // 根据购买结果返回状态,可根据业务需求调整逻辑 switch purchaseResult { case .success(_): return true case .userCancelled, .pending: return false default: return false } }
2. 修改selectedType方法调用异步purchase
由于purchase变成了异步方法,需要用Task包裹调用逻辑,同时传入生成好的appAccountToken:
func selectedType(type: SubscribeType) { // 从本地获取用户的唯一标识并转为Data,示例用UUID(需替换成实际业务的用户标识) guard let appAccountToken = UserDefaultService.getCurrentUserId()?.data(using: .utf8) else { // 无用户标识的处理逻辑 return } let targetProduct: SKProduct switch type { case .month: targetProduct = myProducts[1] case .years: targetProduct = myProducts[0] } Task { do { let isSuccess = try await purchase(product: targetProduct, appAccountToken: appAccountToken) // 这里可以添加购买结果的UI反馈逻辑 debugPrint(isSuccess ? "购买成功" : "购买取消/失败") } catch { debugPrint("购买出错:\(error.localizedDescription)") } } }
3. 关键说明
appAccountToken要求为Data类型,通常可以将用户的唯一ID(如UUID、业务用户ID)转成Data使用- 若需要兼容旧版本StoreKit逻辑,可以保留原purchase方法,新增带token的异步版本
- StoreKit 2的交易仍会进入
SKPaymentQueue,原有的StoreObserver可以继续监听交易状态做后续处理
内容的提问来源于stack exchange,提问作者Evg Enii
相关产品推荐
相关产品推荐

