|
|
@@ -88,37 +88,212 @@ extension QSLVipManager {
|
|
|
|
|
|
// 恢复订阅
|
|
|
func restoreAction(complete: restoreComplete?) {
|
|
|
-
|
|
|
if complete != nil {
|
|
|
self.restoreCompleteClosure = complete
|
|
|
}
|
|
|
|
|
|
- // 支付前查询自动续费的订单,将订单置为支付状态
|
|
|
- let transactions = SKPaymentQueue.default().transactions
|
|
|
- if transactions.count > 0 {
|
|
|
- for tran in transactions {
|
|
|
- if tran.transactionState == .purchased {
|
|
|
- SKPaymentQueue.default().finishTransaction(tran)
|
|
|
- }
|
|
|
+ self.isPaying = true
|
|
|
+ self.isCheck = true
|
|
|
+
|
|
|
+ print("开始恢复订阅")
|
|
|
+ QSLVipManager.shared.checkTransAction {isSuccess, response in
|
|
|
+ if(isSuccess){
|
|
|
+ self.restoreOrder()
|
|
|
+ }else{
|
|
|
+ self.isRestoring = false
|
|
|
+ self.restoreCompleteClosure?(false)
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ func restoreOrder() {
|
|
|
|
|
|
- self.isPaying = true
|
|
|
- self.isCheck = false
|
|
|
+ if isRestoring { return }
|
|
|
|
|
|
- print("开始恢复订阅")
|
|
|
+ if let receiptUrl = Bundle.main.appStoreReceiptURL {
|
|
|
+ let receiptData = try? Data(contentsOf: receiptUrl)
|
|
|
+ if let receiptString = receiptData?.base64EncodedString(options: .endLineWithLineFeed) {
|
|
|
+ debugPrint("receiptString = \(String(describing: receiptString))")
|
|
|
+ self.requestRestoreOrder(receiptData: receiptString)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ isRestoring = true
|
|
|
SKPaymentQueue.default().restoreCompletedTransactions()
|
|
|
|
|
|
- Timer.scheduledTimer(withTimeInterval: 15.0, repeats: false) { timer in
|
|
|
- if let restoreCompleteClosure = self.restoreCompleteClosure {
|
|
|
- print("恢复订阅 15秒超时")
|
|
|
- restoreCompleteClosure(false)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ func checkTransAction(complete1: ((Bool, [String:Any]) -> Void)?) {
|
|
|
+
|
|
|
+ if #available(iOS 13.0, *) {
|
|
|
+ Task{
|
|
|
+ var inSubscribeAppleGoodId = ""
|
|
|
+ var inSubscribeAppAccountToken = ""
|
|
|
+
|
|
|
+
|
|
|
+ if #available(iOS 15.0, *) {
|
|
|
+ var activeTransactions: [String: StoreKit.Transaction] = [:] // groupID → transaction
|
|
|
+ var allActiveProductIDs: [String] = [] // 所有有效的商品ID
|
|
|
+ var pendingDowngradeProductID: String? = nil
|
|
|
+
|
|
|
+
|
|
|
+ // 首先检查未完成的交易,获取降级后的商品ID
|
|
|
+ for await result in Transaction.unfinished {
|
|
|
+ guard case .verified(let transaction) = result,
|
|
|
+ transaction.productType == .autoRenewable
|
|
|
+ else {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 检查这个未完成的交易是否是降级交易
|
|
|
+ if let product = try? await Product.products(for: [transaction.productID]).first {
|
|
|
+ print("发现未完成的订阅交易: \(transaction.productID)")
|
|
|
+
|
|
|
+
|
|
|
+ // 如果未完成的交易购买时间比当前有效订阅晚,很可能是降级
|
|
|
+ let purchaseDate = transaction.purchaseDate
|
|
|
+
|
|
|
+
|
|
|
+ // 检查当前有效订阅来判断是否为降级
|
|
|
+ for await currentResult in Transaction.currentEntitlements {
|
|
|
+ guard case .verified(let currentTransaction) = currentResult,
|
|
|
+ currentTransaction.productType == .autoRenewable
|
|
|
+ else {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 如果未完成交易的购买时间晚于当前交易,且产品不同,则可能是降级
|
|
|
+ if purchaseDate > currentTransaction.purchaseDate
|
|
|
+ && transaction.productID != currentTransaction.productID
|
|
|
+ {
|
|
|
+ print("检测到降级订阅: \(transaction.productID)")
|
|
|
+ pendingDowngradeProductID = transaction.productID
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 然后检查当前有效的订阅
|
|
|
+ for await result in Transaction.currentEntitlements {
|
|
|
+ guard case .verified(let transaction) = result,
|
|
|
+ transaction.productType == .autoRenewable
|
|
|
+ else {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 跳过过期或撤销的
|
|
|
+ if let expirationDate = transaction.expirationDate,
|
|
|
+ expirationDate < Date()
|
|
|
+ {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if transaction.revocationDate != nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 添加到所有有效商品ID列表
|
|
|
+ allActiveProductIDs.append(transaction.productID)
|
|
|
+ print("发现有效订阅: \(transaction.productID)")
|
|
|
+
|
|
|
+
|
|
|
+ // 获取订阅组 ID 和产品信息
|
|
|
+ guard let product = try? await Product.products(for: [transaction.productID]).first,
|
|
|
+ let groupID = product.subscription?.subscriptionGroupID
|
|
|
+ else {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 如果还没找到降级商品,继续通过订阅状态检查
|
|
|
+ if pendingDowngradeProductID == nil {
|
|
|
+ do {
|
|
|
+ let statuses = try await product.subscription?.status ?? []
|
|
|
+ for status in statuses {
|
|
|
+ switch status.state {
|
|
|
+ case .subscribed:
|
|
|
+ // 检查是否有pending的降级
|
|
|
+ if case .verified(let renewalInfo) = status.renewalInfo,
|
|
|
+ let autoRenewProductID = renewalInfo.autoRenewPreference,
|
|
|
+ autoRenewProductID != transaction.productID
|
|
|
+ {
|
|
|
+ // 发现pending的产品变更,通常是降级
|
|
|
+ print(
|
|
|
+ "通过renewalInfo检测到pending的订阅变更: \(autoRenewProductID)")
|
|
|
+ pendingDowngradeProductID = autoRenewProductID
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ print("检查订阅状态失败: \(error)")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 每个订阅组只保留最新的
|
|
|
+ if let current = activeTransactions[groupID] {
|
|
|
+ if transaction.purchaseDate > current.purchaseDate {
|
|
|
+ activeTransactions[groupID] = transaction
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ activeTransactions[groupID] = transaction
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ print("所有有效订阅商品ID: \(allActiveProductIDs)")
|
|
|
+ print("按订阅组分组的订阅数量: \(activeTransactions.count)")
|
|
|
+
|
|
|
+
|
|
|
+ // 优先使用通过未完成交易或renewalInfo找到的降级商品ID
|
|
|
+ if let pendingProductID = pendingDowngradeProductID {
|
|
|
+ inSubscribeAppleGoodId = pendingProductID
|
|
|
+ // 对于pending的订阅,我们无法获取appAccountToken,使用当前订阅的
|
|
|
+ if let transaction = activeTransactions.values.first {
|
|
|
+ inSubscribeAppAccountToken = transaction.appAccountToken?.uuidString ?? ""
|
|
|
+ }
|
|
|
+ print("使用降级订阅商品ID: \(pendingProductID)")
|
|
|
+ } else if let transaction = activeTransactions.values.first {
|
|
|
+ // 如果有多个订阅组,这里可能需要根据业务逻辑选择合适的订阅
|
|
|
+ inSubscribeAppleGoodId = transaction.productID
|
|
|
+ inSubscribeAppAccountToken = transaction.appAccountToken?.uuidString ?? ""
|
|
|
+ print("当前使用的订阅商品ID: \(transaction.productID)")
|
|
|
+
|
|
|
+
|
|
|
+ // 如果有多个订阅,打印警告
|
|
|
+ if activeTransactions.count > 1 {
|
|
|
+ print("⚠️ 检测到多个订阅组的订阅,当前使用第一个: \(transaction.productID)")
|
|
|
+ print("所有订阅组的商品ID: \(activeTransactions.values.map { $0.productID })")
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ print("无当前有效订阅")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(inSubscribeAppleGoodId.count == 0 || inSubscribeAppAccountToken.count == 0){
|
|
|
+ complete1?(false, [:])
|
|
|
+ }else{
|
|
|
+ complete1?(true, ["appleGoodsId":inSubscribeAppleGoodId,"appAccountToken":inSubscribeAppAccountToken])
|
|
|
+ }
|
|
|
+ print("inSubscribeAppleGoodId:\(inSubscribeAppleGoodId)")
|
|
|
+ print("inSubscribeAppAccountToken:\(inSubscribeAppAccountToken)")
|
|
|
+
|
|
|
}
|
|
|
- self.restoreCompleteClosure = nil
|
|
|
- // 停止定时器并从运行循环中移除
|
|
|
- timer.invalidate()
|
|
|
+ }else{
|
|
|
+ complete1?(false, [:])
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// 网络请求
|
|
|
@@ -234,7 +409,7 @@ extension QSLVipManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-extension QSLVipManager: SKPaymentTransactionObserver {
|
|
|
+extension QSLVipManager: SKPaymentTransactionObserver, SKRequestDelegate {
|
|
|
|
|
|
// 提交内购
|
|
|
func submitIAP() {
|
|
|
@@ -332,10 +507,10 @@ extension QSLVipManager: SKPaymentTransactionObserver {
|
|
|
}
|
|
|
self.payCompleteCloure = nil
|
|
|
// 需要前台响应
|
|
|
- if let restoreCompleteClosure = self.restoreCompleteClosure {
|
|
|
- restoreCompleteClosure(false)
|
|
|
- }
|
|
|
- self.restoreCompleteClosure = nil
|
|
|
+// if let restoreCompleteClosure = self.restoreCompleteClosure {
|
|
|
+// restoreCompleteClosure(false)
|
|
|
+// }
|
|
|
+// self.restoreCompleteClosure = nil
|
|
|
SKPaymentQueue.default().finishTransaction(tran)
|
|
|
|
|
|
case .deferred:
|
|
|
@@ -349,5 +524,59 @@ extension QSLVipManager: SKPaymentTransactionObserver {
|
|
|
|
|
|
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
|
|
|
print("恢复购买失败\(error.localizedDescription)")
|
|
|
+ self.restoreCompleteClosure?(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
|
|
+ completeRestoreTransactions(queue, error: nil)
|
|
|
+ }
|
|
|
+
|
|
|
+ func completedHandle(withTrans: SKPaymentTransaction) {
|
|
|
+
|
|
|
+ let receiptURL = Bundle.main.appStoreReceiptURL
|
|
|
+ var receiptData:NSData? = nil
|
|
|
+ if let url = receiptURL {
|
|
|
+ receiptData = NSData(contentsOf: url)
|
|
|
+ }
|
|
|
+
|
|
|
+ let receiptString = receiptData?.base64EncodedString(options: .endLineWithLineFeed)
|
|
|
+ if let receiptStr = receiptString {
|
|
|
+ self.requestRestoreOrder(receiptData: receiptStr)
|
|
|
+ }else{
|
|
|
+ self.isRestoring = false
|
|
|
+ let req = SKReceiptRefreshRequest()
|
|
|
+ req.delegate = self
|
|
|
+ req.start()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func completeRestoreTransactions(_ queue: SKPaymentQueue, error: Error?) {
|
|
|
+ if queue.transactions.count > 0 {
|
|
|
+ let transaction = queue.transactions[0]
|
|
|
+ print("restore:::\(transaction)")
|
|
|
+ completedHandle(withTrans: transaction)
|
|
|
+ } else {
|
|
|
+ self.restoreCompleteClosure?(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public func requestDidFinish(_ request: SKRequest) {
|
|
|
+ if request.isKind(of: SKProductsRequest.self) {
|
|
|
+
|
|
|
+ }else if request.isKind(of: SKReceiptRefreshRequest.self) {
|
|
|
+ debugPrint("刷新本地凭证请求完成(注意:失败和成功都算完成) SKReceiptRefreshRequest: \(request)")
|
|
|
+ // 开始本地内购支付凭证验证
|
|
|
+ self.restoreOrder()
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public func request(_ request: SKRequest, didFailWithError error: Error) {
|
|
|
+ if request.isKind(of: SKProductsRequest.self) {
|
|
|
+
|
|
|
+ }else if request.isKind(of: SKReceiptRefreshRequest.self) {
|
|
|
+ self.restoreCompleteClosure?(false)
|
|
|
+ }
|
|
|
}
|
|
|
}
|