Files
keyboard/keyBoard/Class/Vender/FGIAPService/FGIAPService.m
2025-11-13 19:20:57 +08:00

236 lines
9.7 KiB
Objective-C

//
// FGIAPManager.m
// MaltBaby
//
// Created by FoneG on 2020/5/7.
//
#import "FGIAPService.h"
#import "FGIAPServiceUtility.h"
#import "NSObject+FGIsNullOrEmpty.h"
static NSMutableDictionary *FGIAPServiceErrorMapsFromTransaction (SKPaymentTransaction *transaction) {
NSMutableDictionary *errorMaps = [NSMutableDictionary dictionary];
[errorMaps setValue:transaction.transactionIdentifier?:@"" forKey:@"transactionIdentifier"];
[errorMaps setValue:transaction.originalTransaction.transactionIdentifier?:@"" forKey:@"originalTransaction.transactionIdentifier"];
[errorMaps setValue:transaction.payment.applicationUsername?:@"" forKey:@"applicationUsername"];
[errorMaps setValue:transaction.payment.productIdentifier?:@"" forKey:@"productIdentifier"];
return errorMaps;
}
@interface FGIAPService () <SKPaymentTransactionObserver, SKRequestDelegate>
@property (nonatomic, strong) id<FGIAPVerifyTransaction> verifyTransaction;
@property (nonatomic, copy) FGIAPManagerBuyBlock buyProductCompleteBlock;
@property (nonatomic, strong) NSString *productIdentifier;
@property (nonatomic, strong) SKPayment *APPStorePayment;
@end
@implementation FGIAPService
- (instancetype)initWithTransaction:(id<FGIAPVerifyTransaction>)verifyTransaction{
if (self = [super init]) {
_verifyTransaction = verifyTransaction;
if ([SKPaymentQueue defaultQueue]) {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
}
return self;
}
- (void)buyProduct:(SKProduct *)product onCompletion:(nonnull FGIAPManagerBuyBlock)completion{
if (![SKPaymentQueue canMakePayments]) {
completion(@"Failed to obtain the internal purchase permission", FGIAPManagerPurchaseRusultFail);
return;
}
if ([product.productIdentifier isNSStringAndNotEmpty]) {
_productIdentifier = product.productIdentifier;
_buyProductCompleteBlock = completion;
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
if ([SKPaymentQueue defaultQueue]) {
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
}else{
completion(@"The selected payment does not exist", FGIAPManagerPurchaseRusultFail);
}
}
- (void)tryShouldAddStorePayments{
if (_APPStorePayment && [SKPaymentQueue defaultQueue]) {
[[SKPaymentQueue defaultQueue] addPayment:self.APPStorePayment];
}
}
#pragma mark - SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
FGLog(@"%s %ld", __func__, [SKPaymentQueue defaultQueue].transactions.count);
if (![[SKPaymentQueue defaultQueue].transactions isNSArrayAndNotEmpty]) {
/// all transactions finished
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchasing:
FGLog(@"Add an payment to the list");
break;
case SKPaymentTransactionStatePurchased:
FGLog(@"Customer paid successfully");
[self completeTransaction:transaction retryWhenreceiptURLisEmpty:YES];
break;
case SKPaymentTransactionStateRestored:
FGLog(@"The transaction has been recovered from the user's purchase history");
[self completeTransaction:transaction retryWhenreceiptURLisEmpty:YES];
break;
case SKPaymentTransactionStateFailed:
FGLog(@"Failure of commodity transaction");
[self failedTransaction:transaction withError:FGIAPServiceErrorTypeNone];
break;
case SKPaymentTransactionStateDeferred:
FGLog(@"Merchandise is suspended");
break;
default:
///
break;
}
}
}
- (BOOL)paymentQueue:(SKPaymentQueue *)queue shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product API_AVAILABLE(ios(11.0)){
BOOL shouldAddStorePayment = NO;
if (_verifyTransaction && [_verifyTransaction respondsToSelector:@selector(paymentQueue:shouldAddStorePayment:forProduct:)]) {
shouldAddStorePayment = [_verifyTransaction paymentQueue:queue shouldAddStorePayment:payment forProduct:product];
}
if (shouldAddStorePayment == NO) {
_APPStorePayment = payment;
}
return shouldAddStorePayment;
}
#pragma mark - SKRequestDelegate
- (void)requestDidFinish:(SKRequest *)request{
if ([request isKindOfClass:[SKReceiptRefreshRequest class]]) {
SKReceiptRefreshRequest *RefreshRequest = (SKReceiptRefreshRequest *)request;
SKPaymentTransaction *transaction = [RefreshRequest.receiptProperties valueForKey:@"transaction"];
if (transaction) {
[self completeTransaction:transaction retryWhenreceiptURLisEmpty:NO];
}else{
[self failedTransaction:transaction withError:FGIAPServiceErrorTypeReceiptNotExist];
}
}
}
- (void)request:(SKRequest *)request didFailWithError:(nonnull NSError *)error{
if ([request isKindOfClass:[SKReceiptRefreshRequest class]]) {
SKReceiptRefreshRequest *RefreshRequest = (SKReceiptRefreshRequest *)request;
SKPaymentTransaction *transaction = [RefreshRequest.receiptProperties valueForKey:@"transaction"];
[self failedTransaction:transaction withError:FGIAPServiceErrorTypeReceiptNotExist];
}
}
#pragma mark - private method
- (void)completeTransaction:(SKPaymentTransaction *)transaction retryWhenreceiptURLisEmpty:(BOOL)retry{
FGLog(@"%s %@ %@", __func__, transaction.transactionIdentifier, transaction.originalTransaction.transactionIdentifier);
if (![transaction.transactionIdentifier isNSStringAndNotEmpty]) {
[self failedTransaction:transaction withError:FGIAPServiceErrorTypeTransactionIdentifierNotExist];
return;
}
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
if ([[NSFileManager defaultManager] fileExistsAtPath:receiptURL.path]) {
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
NSString *receiptDataText = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
[self checkReceipt:receiptDataText withTransaction:transaction handler:nil];
}else if (retry){
SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{@"transaction":transaction}];
receiptRefreshRequest.delegate = self;
[receiptRefreshRequest start];
}else{
[self failedTransaction:transaction withError:FGIAPServiceErrorTypeReceiptNotExist];
}
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction withError:(FGIAPServiceErrorType)error{
FGLog(@"%s Transaction error:%@ code:%ld", __func__, transaction.error.localizedDescription, transaction.error.code);
BOOL finish = error == FGIAPServiceErrorTypeNone;
if (finish && [SKPaymentQueue defaultQueue]) {
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
NSMutableDictionary *logStatistics = [NSMutableDictionary dictionaryWithDictionary:FGIAPServiceErrorMapsFromTransaction(transaction)];
if (self.verifyTransaction && [self.verifyTransaction respondsToSelector:@selector(pushServiceErrorLogStatistics:error:)]) {
[logStatistics setValue:@(error) forKey:@"error"];
[self.verifyTransaction pushServiceErrorLogStatistics:logStatistics error:error];
}
if (_buyProductCompleteBlock) {
FGIAPManagerPurchaseRusult result = FGIAPManagerPurchaseRusultHalfSuccess;
if (error == FGIAPServiceErrorTypeNone) {
result = transaction.error.code == SKErrorPaymentCancelled ? FGIAPManagerPurchaseRusultCancel : FGIAPManagerPurchaseRusultFail;
}
_buyProductCompleteBlock(transaction.error.localizedDescription, result);
}
}
- (void)checkReceipt:(NSString *)receipt withTransaction:(SKPaymentTransaction *)transaction handler:(FGIAPVerifyTransactionBlock)handler{
WS(wSelf);
if (_verifyTransaction && [_verifyTransaction respondsToSelector:@selector(pushSuccessTradeReultToServer:transaction:complete:)]) {
[_verifyTransaction pushSuccessTradeReultToServer:receipt transaction:transaction complete:^(NSString * _Nonnull message, NSError * _Nullable requestErr) {
//polling verify transaction
if (requestErr && requestErr.code != FGIAPServerOverdueErrorCode) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[wSelf checkReceipt:receipt withTransaction:transaction handler:handler];
});
return;
}
[wSelf finishTransaction:transaction result: FGIAPManagerPurchaseRusultSuccess message:message];
}];
}else{
NSAssert(NO, @"You must configure the method: - pushSuccessTradeReultToServer:transaction:complete:");
}
}
- (void)finishTransaction:(SKPaymentTransaction *)transaction result:(FGIAPManagerPurchaseRusult)result message:(NSString *)msg{
FGLog(@"%s finishTransaction:%@", __func__, transaction.transactionIdentifier);
if ([SKPaymentQueue defaultQueue]) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
if ([transaction.payment.productIdentifier isEqualToString:self.productIdentifier]) {
self.productIdentifier = nil;
if (_buyProductCompleteBlock) {
_buyProductCompleteBlock(msg, result);
}
}
}
@end