diff --git a/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.h b/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.h index 81dfbda..b2ddddc 100644 --- a/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.h +++ b/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.h @@ -20,6 +20,13 @@ FOUNDATION_EXPORT NSNotificationName const KBIAPDidCompletePurchaseNotification; - (void)verifyReceipt:(NSString *)receipt completion:(void (^)(BOOL success, NSString * _Nullable message, NSInteger statusCode))completion; +/// 校验 StoreKit 2 交易的 JWS 签名串 +/// - Parameters: +/// - payload: 交易 JWS 字符串 +/// - completion: 回调 +- (void)verifySignedPayload:(NSString *)payload + completion:(void (^)(BOOL success, NSString * _Nullable message, NSInteger statusCode))completion; + @end NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.m b/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.m index 20a456e..9496869 100644 --- a/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.m +++ b/keyBoard/Class/Pay/M/IAPVerifyTransactionObj.m @@ -46,6 +46,29 @@ NSNotificationName const KBIAPDidCompletePurchaseNotification = @"KBIAPDidComple }]; } +- (void)verifySignedPayload:(NSString *)payload + completion:(void (^)(BOOL success, NSString * _Nullable message, NSInteger statusCode))completion { + if (![payload isKindOfClass:NSString.class] || payload.length == 0) { + if (completion) { + completion(NO, KBLocalized(@"Payload missing"), KBBizCodeReceiptError); + } + return; + } + NSDictionary *params = @{ @"signedPayload": payload ?: @"" }; + __weak typeof(self) weakSelf = self; + [self.payVM applePayReqWithParams:params needShow:NO completion:^(NSInteger sta, NSString * _Nullable msg) { + dispatch_async(dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf) self = weakSelf; + (void)self; + BOOL success = (sta == KBBizCodeSuccess); + NSString *tip = msg ?: (success ? KBLocalized(@"Success") : KBLocalized(@"Payment failed")); + if (completion) { + completion(success, tip, sta); + } + }); + }]; +} + #pragma mark - FGIAPVerifyTransaction - (void)pushSuccessTradeReultToServer:(NSString *)receipt diff --git a/keyBoard/Class/Pay/StoreKit2Manager/KBStoreKitBridge.swift b/keyBoard/Class/Pay/StoreKit2Manager/KBStoreKitBridge.swift index f4379fd..f70e24f 100644 --- a/keyBoard/Class/Pay/StoreKit2Manager/KBStoreKitBridge.swift +++ b/keyBoard/Class/Pay/StoreKit2Manager/KBStoreKitBridge.swift @@ -51,30 +51,12 @@ final class KBStoreKitBridge: NSObject, StoreKitDelegate { do { try await self.manager.purchase(productId: productId) - let state = self.manager.currentState - switch state { - case .purchaseSuccess(let id) where id == productId: - let receipt = try await Self.fetchReceiptData() - self.verifyReceipt(receipt, completion: completion) - case .purchasePending(let id) where id == productId: + + if let payload = await Self.latestJWSPayload(for: productId) { + self.verifySignedPayload(payload, completion: completion) + } else { await MainActor.run { - completion(false, "Purchase pending approval.") - } - case .purchaseCancelled(let id) where id == productId: - await MainActor.run { - completion(false, "Purchase cancelled.") - } - case .purchaseFailed(let id, let error) where id == productId: - await MainActor.run { - completion(false, error.localizedDescription) - } - case .error(let error): - await MainActor.run { - completion(false, error.localizedDescription) - } - default: - await MainActor.run { - completion(false, "Purchase failed.") + completion(false, "Unable to obtain transaction payload.") } } } catch { @@ -119,12 +101,12 @@ final class KBStoreKitBridge: NSObject, StoreKitDelegate { return true } - private func verifyReceipt(_ receipt: String, completion: @escaping (Bool, String?) -> Void) { - receiptVerifier.verifyReceipt(receipt) { success, message, _ in + private func verifySignedPayload(_ payload: String, completion: @escaping (Bool, String?) -> Void) { + receiptVerifier.verifySignedPayload(payload) { success, message, _ in DispatchQueue.main.async { if success { NotificationCenter.default.post( - name: NSNotification.Name(rawValue: NSNotification.Name.KBIAPDidCompletePurchase.rawValue), + name: NSNotification.Name.KBIAPDidCompletePurchase, object: nil ) } @@ -133,22 +115,11 @@ final class KBStoreKitBridge: NSObject, StoreKitDelegate { } } - private static func fetchReceiptData() async throws -> String { - if let receipt = try loadReceiptFromBundle() { - return receipt + private static func latestJWSPayload(for productId: String) async -> String? { + guard let result = await Transaction.latest(for: productId) else { return nil } + if case .verified = result { + return result.jwsRepresentation } - try await AppStore.sync() - if let receipt = try loadReceiptFromBundle() { - return receipt - } - throw StoreKitError.verificationFailed - } - - private static func loadReceiptFromBundle() throws -> String? { - guard let url = Bundle.main.appStoreReceiptURL else { return nil } - guard FileManager.default.fileExists(atPath: url.path) else { return nil } - let data = try Data(contentsOf: url, options: .alwaysMapped) - guard !data.isEmpty else { return nil } - return data.base64EncodedString() + return nil } }