1
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user