美文网首页iOS 开发每天分享优质文章iOS应用 支付
ios内购恢复以及多个内购项目恢复验证完毕判断代码

ios内购恢复以及多个内购项目恢复验证完毕判断代码

作者: 785ac245e5c9 | 来源:发表于2018-06-26 16:58 被阅读448次

    也没有什么技术含量的东西,发上来方便大家

    .h文件

    #import <Foundation/Foundation.h>
    
    typedef void (^IAPCompletionHandle)(SIAPPurchType type,NSData *data);
    
    @interface STRIAPManager : NSObject
    
    + (instancetype)shareSIAPManager;
    //开始内购
    - (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle;
    //恢复内购
    -(void)restorePurchaseWithcompleteHandle:(IAPCompletionHandle)handle;
    @end
    

    .m文件

    @interface STRIAPManager () <SKPaymentTransactionObserver, SKProductsRequestDelegate> {
        NSString *_purchID;
        IAPCompletionHandle _handle;
    }
    
    //判断一份交易获得验证的次数  key为随机值
    @property(nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *transactionCountMap;
    
    @property(nonatomic, strong) NSMutableDictionary<NSString *, NSMutableSet<SKPaymentTransaction *> *> *transactionFinishMap;
    
    @property(nonatomic,assign)ENUMRestoreProgress restoreProgress;
    
    @end
    
    @implementation STRIAPManager {
    
    }
    
    #pragma mark - init
    
    + (instancetype)shareSIAPManager {
        static STRIAPManager *IAPManager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            IAPManager = [[STRIAPManager alloc] init];
        });
        return IAPManager;
    }
    
    - (instancetype)init {
        if (self = [super init]) {
            // 购买监听写在程序入口,程序挂起时移除监听,这样如果有未完成的订单将会自动执行并回调 paymentQueue:updatedTransactions:方法
            [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        }
        return self;
    }
    
    - (void)dealloc {
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
    
    #pragma mark - public method
    
    //开始购买
    - (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle {
        
    
        if (purchID) {
            if ([SKPaymentQueue canMakePayments]) {
                _purchID = purchID;
                _handle = handle;
                NSSet *set = [NSSet setWithArray:@[purchID]];
                SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
                request.delegate = self;
                [request start];
            } else {
                [self handleActionWithType:SIAPPurchNotArrow data:nil];
            }
        }
    }
    
    //恢复购买
    - (void)restorePurchaseWithcompleteHandle:(IAPCompletionHandle)handle {
        
        //开始恢复
        _restoreProgress = ENUMRestoreProgressStart;
        
        _handle = handle;
    
        [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    }
    
    
    
    /*
    SKPaymentTransactionStatePurchasing,    // Transaction is being added to the server queue.
    SKPaymentTransactionStatePurchased,     // Transaction is in queue, user has been charged.  Client should complete the transaction.
    SKPaymentTransactionStateFailed,        // Transaction was cancelled or failed before being added to the server queue.
    SKPaymentTransactionStateRestored,      // Transaction was restored from user's purchase history.  Client should complete the transaction.
    */
    
    #pragma mark - SKPaymentTransactionObserver
    
    //队列操作后的回调
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
    
        //判断是否为恢复购买的请求
        if (_restoreProgress == ENUMRestoreProgressStart) {
            _restoreProgress = ENUMRestoreProgressUpdatedTransactions;
        }
        
        NSString *operationId = [[NSUUID UUID] UUIDString];
    
        [self.transactionFinishMap setValue:[NSMutableSet set] forKey:operationId];
        [self.transactionCountMap setValue:@(transactions.count) forKey:operationId];
    
        for (int i = 0; i < transactions.count; i++) {
    
            SKPaymentTransaction *tran = transactions[i];
    
            //购买成功
    
            if (tran.transactionState == SKPaymentTransactionStatePurchased) {
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [self completeTransaction:tran operationId:operationId];
            }
                //购买中
            else if (tran.transactionState == SKPaymentTransactionStatePurchasing) {
    #if DEBUG
                NSLog(@"正在购买");
    #endif
            }
                //恢复购买
            else if (tran.transactionState == SKPaymentTransactionStateRestored) {
    #if DEBUG
                NSLog(@"已经购买过商品");
    #endif
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [self restoreTransaction:tran operationId:operationId];
    
            }
                //购买失败
            else if (tran.transactionState == SKPaymentTransactionStateFailed) {
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [self failedTransaction:tran];
            }
    
    
        }
    
    }
    
    //恢复购买结束回调
    - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue NS_AVAILABLE_IOS(3_0){
    
        //没有进入- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions 方法
        //恢复产品数量为0  提前结束
        if(_restoreProgress != ENUMRestoreProgressUpdatedTransactions){
            [self handleActionWithType:SIAPPurchRestoreNotBuy data:nil];
        }
        
        _restoreProgress = ENUMRestoreProgressFinish;
    
    }
    
    //恢复购买失败
    - (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error NS_AVAILABLE_IOS(3_0){
        
        //恢复失败
        if(_restoreProgress != ENUMRestoreProgressUpdatedTransactions){
            [self handleActionWithType:SIAPPurchRestoreFailed data:nil];
        }
        
        _restoreProgress = ENUMRestoreProgressFinish;
    
    }
    #pragma mark - transaction action
    
    //恢复购买
    - (void)restoreTransaction:(SKPaymentTransaction *)transaction operationId:(NSString *)operationId {
    
        [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO operationId:operationId];
    
    }
    
    // 完成交易
    - (void)completeTransaction:(SKPaymentTransaction *)transaction operationId:(NSString *)operationId {
    
        [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO operationId:operationId];
    }
    
    
    // 交易失败
    - (void)failedTransaction:(SKPaymentTransaction *)transaction {
        if (transaction.error.code != SKErrorPaymentCancelled) {
            [self handleActionWithType:SIAPPurchFailed data:nil];
        } else {
            [self handleActionWithType:SIAPPurchCancle data:nil];
        }
    }
    
    - (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag operationId:(NSString *)operationId {
    
        //    // Your application should implement these two methods.
    //    NSString *productId = transaction.payment.productIdentifier;
    //    NSString *receipt = [transaction.transactionReceipt base64Encoding];
    //    if ([productId length] > 0) {
    //        // 向自己的服务器验证购买凭证
    //    }
        //交易验证
        NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
        NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
    
        if (!receipt) {
            // 交易凭证为空验证失败
            [self handleActionWithType:SIAPPurchVerFailed data:nil];
            return;
        }
        // 购买成功将交易凭证发送给服务端进行再次校验
        [self handleActionWithType:SIAPPurchSuccess data:receipt];
    
        NSError *error;
        NSDictionary *requestContents = @{
                @"receipt-data": [receipt base64EncodedStringWithOptions:0]
        };
        NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                              options:0
                                                                error:&error];
    
        if (!requestData) { // 交易凭证为空验证失败
            [self handleActionWithType:SIAPPurchVerFailed data:nil];
            return;
        }
    
        //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
        //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
    
        NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
        if (flag) {
            serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
        }
        NSURL *storeURL = [NSURL URLWithString:serverString];
        NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
        [storeRequest setHTTPMethod:@"POST"];
        [storeRequest setHTTPBody:requestData];
    
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                   if (connectionError) {
                                       // 无法连接服务器,购买校验失败
                                       [self handleActionWithType:SIAPPurchVerFailed data:nil];
                                   } else {
                                       NSError *error;
                                       NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                       if (!jsonResponse) {
                                           // 苹果服务器校验数据返回为空校验失败
                                           [self handleActionWithType:SIAPPurchVerFailed data:nil];
                                       }
    
                                       // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
                                       NSString *status = [NSString stringWithFormat:@"%@", jsonResponse[@"status"]];
                                       if (status && [status isEqualToString:@"21007"]) {
                                           [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES operationId:operationId];
                                       } else if (status && [status isEqualToString:@"0"]) {
                                           //订单校验成功
    
                                           //APP添加商品
                                           NSString *productId = transaction.payment.productIdentifier;
    
                                           for (PurchProductModel *model in [[AuthManager sharedManager] getAllProductList]) {
                                               if ([model.productId isEqualToString:productId]) {
                                                   [[AuthManager sharedManager] addProduct:model.productType];
                                                   break;
                                               }
                                           }
                                           //总数量
                                           NSInteger totalCount = [[self.transactionCountMap valueForKey:operationId] integerValue];
    
                                           //已执行数量
                                           NSMutableSet *finishSet = [self.transactionFinishMap valueForKey:operationId];
                                           [finishSet addObject:transaction];
    
                                           //需在添加对象后获得对象数量 不然有极低的可能遇到并发问题 而导致不执行回调 
                                           [self handleActionWithType:SIAPPurchVerSuccess data:nil invokeHandle:[finishSet count]  == totalCount];
                                       }
    #if DEBUG
                                       NSLog(@"----验证结果 %@", jsonResponse);
    #endif
                                   }
                               }];
    
    
        // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
    }
    
    
    #pragma mark - SKProductsRequestDelegate
    
    //发送请求后 会回调  执行这个方法
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
        NSArray *products = response.products;
        if ([products count] <= 0) {
    #if DEBUG
            NSLog(@"--------------没有商品------------------");
    #endif
            return;
        }
    
        SKProduct *p = nil;
        for (SKProduct *pro in products) {
            if ([pro.productIdentifier isEqualToString:_purchID]) {
                p = pro;
                break;
            }
        }
    
    #if DEBUG
        NSLog(@"productID:%@", response.invalidProductIdentifiers);
        NSLog(@"产品付费数量:%lu", (unsigned long) [products count]);
        NSLog(@"%@", [p description]);
        NSLog(@"%@", [p localizedTitle]);
        NSLog(@"%@", [p localizedDescription]);
        NSLog(@"%@", [p price]);
        NSLog(@"%@", [p productIdentifier]);
        NSLog(@"发送购买请求");
    #endif
    
        SKPayment *payment = [SKPayment paymentWithProduct:p];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    
    //请求失败
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    #if DEBUG
        NSLog(@"------------------错误-----------------:%@", error);
    #endif
    }
    
    - (void)requestDidFinish:(SKRequest *)request {
    #if DEBUG
        NSLog(@"------------反馈信息结束-----------------");
    #endif
    }
    
    
    #pragma mark - private method
    
    //适配器模式
    - (void)handleActionWithType:(SIAPPurchType)type data:(NSData *)data invokeHandle:(Boolean)invoke {
    
    #ifdef DEBUG
        switch (type) {
            case SIAPPurchSuccess:
                NSLog(@"购买成功");
                break;
            case SIAPPurchFailed:
                NSLog(@"购买失败");
                break;
            case SIAPPurchCancle:
                NSLog(@"用户取消购买");
                break;
            case SIAPPurchVerFailed:
                NSLog(@"订单校验失败");
                break;
            case SIAPPurchVerSuccess:
                NSLog(@"订单校验成功");
                break;
            case SIAPPurchNotArrow:
                NSLog(@"不允许程序内付费");
                break;
            case SIAPPurchRestoreNotBuy:
                NSLog(@"购买数量为0");
                break;
            default:
                break;
        }
    #endif
        
        //因为购买成功并不是最后一个步骤 没有意义 不进行处理
        if (type == SIAPPurchSuccess) {
            return;
        }
        
        
        if (invoke && _handle) {
            _handle(type, data);
        }
    
    }
    
    //完成回调 自己的block
    - (void)handleActionWithType:(SIAPPurchType)type data:(NSData *)data {
    
        [self handleActionWithType:type data:data invokeHandle:true];
    
    }
    
    #pragma mark - getter & setter
    
    
    - (NSMutableDictionary *)transactionFinishMap {
        if (!_transactionFinishMap) {
            _transactionFinishMap = [NSMutableDictionary dictionary];
        }
        return _transactionFinishMap;
    }
    
    
    - (NSMutableDictionary *)transactionCountMap {
        if (!_transactionCountMap) {
            _transactionCountMap = [NSMutableDictionary dictionary];
        }
        return _transactionCountMap;
    }
    
    @end
    
    

    内购恢复过程枚举

    //内购恢复过程
    typedef NS_ENUM(NSInteger, ENUMRestoreProgress) {
        ENUMRestoreProgressStop = 0, //尚未开始请求
        ENUMRestoreProgressStart = 1, //开始请求
        ENUMRestoreProgressUpdatedTransactions = 2, //更新了事务
        ENUMRestoreProgressFinish = 3, //完成请求
    };
    

    内购购买状态枚举

    //内购
    typedef NS_ENUM(NSInteger, SIAPPurchType) {
        SIAPPurchSuccess = 0,       // 购买成功
        SIAPPurchFailed = 1,        // 购买失败
        SIAPPurchCancle = 2,        // 取消购买
        SIAPPurchVerFailed = 3,     // 订单校验失败
        SIAPPurchVerSuccess = 4,    // 订单校验成功
        SIAPPurchNotArrow = 5,      // 不允许内购
        SIAPPurchRestoreNotBuy = 6,      // 恢复购买数量为0
        SIAPPurchRestoreFailed = 7,      // 恢复失败
    };
    

    相关文章

      网友评论

        本文标题:ios内购恢复以及多个内购项目恢复验证完毕判断代码

        本文链接:https://www.haomeiwen.com/subject/hqymyftx.html