更新UI
This commit is contained in:
235
keyBoard/Class/Vender/FGIAPService/FGIAPService.m
Normal file
235
keyBoard/Class/Vender/FGIAPService/FGIAPService.m
Normal file
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user