美文网首页iOS DeveloperiOS内购以及支付流程
Apple 内购流程(代码开发级别)

Apple 内购流程(代码开发级别)

作者: 大老虎_Robin | 来源:发表于2017-05-22 11:36 被阅读87次

    Apple 内购流程:
    1、向苹果服务器,发送请求,获取可购买商品信息。
    2、SKProductsRequest 协议 。获取商品数据列表。
    3、确定购买商品(或回复商品)。
    4、监测交易过程。
    5、验证交易收据(receipt)。
    1、向苹果服务器,发送请求,获取可购买商品信息。

    //提交商品信息请求
    - (void)requestProductWithProductArray:(NSArray *)productIdAry {
         NSSet *set = [[NSSet alloc] initWithArray:productIdAry];
        
        //“异步”请求有哪些可售商品
        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
        //启动服务
        [request start];
    }
    

    2、SKProductsRequest 协议 。获取商品数据列表。

    #pragma mark - SKProductsRequest Delegate
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
        //获取商品数据
        NSArray *productAry = response.products;
        if ([productAry count] <= 0) {
            //暂无商品信息
            return;
        }
        
        //初始化本地商品信息个数
        if (self.productDic == nil) {
            self.productDic = [NSMutableDictionary dictionaryWithCapacity:response.products.count];
        }
    
        //遍历商品数据
        for (SKproduct *product in response.products) {
            [self.productDis setObject:product forKey:product.productIdentifier];
            
            //打印产品列表信息
            NSLog(@"product ID : %@",product.productIdentifier);
            NSLog(@"product Description : %@",product.description);
            NSLog(@"product Title :%@",product.localizedTitle);
        }
    
    #warning - Todo 可以将商品信息列表传给ViewController以供显示商品列表。
    #warning - Todo 但不建议使用此信息显示商品列表,可以用此信息进行产品ID的验证。
    }
    
    #pragma mark - SKProductsRequest Delegate
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
        NSLog(@"---------------- 错误 ---------------");
    }
    
    #pragma mark - SKProductsRequest Delegate
    - (void)requestDidFinish:(SKPRequest *)request {
        NSLog(@"--------------- 反馈信息结束 ---------------");
    }
    

    3、确定购买商品

    #pragma mark - Buy Product
    - (void)buyProduct:(NSString *)productID {
        //购买商品
        SKProduct *buyProduct = self.productDic[productID];
    
        SKPayment *payment = [SKPayment paymentWithProduct:buyProduct];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    

    4、回复商品

    #pragma mark - Restore Product
    - (void)restorePurchase {
        //回复已经完成的所有交易(仅限永久有效商品)。
        [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    }
    

    5、监测交易过程

    #pragma mark - SKPaymentTransaction Observer
    - (void)paymentQueue:(SKPaymentQueue *)queue updateTransactions:(NSArray<SKPaymentTransaction *> *)transaction {
        for (SKPaymentTransaction *transaction in transactions) {
            NSLog(@"交易队列状态 :%@",transactions);
            switch (transaction.transactionState) {
                case SKPaymentTransactionStatePurchased:    //购买成功
                    //  向苹果服务器进行验证
                    if (self.checkAfterPay) {
                        //支付成功了,并开始向苹果服务器进行验证(若CheckAfterPay为NO,则不会经过此步骤)
                    }else {
                        //商品完全购买成功且验证成功了。(若CheckAfterPay为NO,则会在购买成功后直接触发此方法)
                    }
                    //  目前都是全部进行验证。
                    [self verifyPruchaseWithPaymentTransaction:transaction isTestServer:NO];
                    break;
                    
                case SKPaymentTransactionStatePurchasing:   //  正在购买
                    NSLog(@"正在购买...");
                    break;
                    
                case SKPaymentTransactionStateRestored:     //  恢复成功
                    //  将交易从交易队列中删除
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
                    
                case SKPaymentTransactionStateFailed:       //  购买取消或失败
                    if (transaction.error.code != SKErrorPaymentCancelled) {
                        NSLog(@"购买失败!");
                    }else{
                        NSLog(@"购买取消!");
                    }
                    //  将交易从交易队列中删除
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
                
                default:
                    //  将交易从交易队列中删除
                    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                    break;
            }
        }
    }
    

    6、验证交易收据(receipt)

    #pragma mark - Verify Receipt
    - (void)verifyPruchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag {
        //  验证凭据,获取到苹果返回的交易凭据
        //  appStore Receipt URL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
        NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
        //  从沙盒中获取到购买凭据receipte
        NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
        
        
        if (!receipt) {
            //  交易凭证为空,验证失败
            //  KIAPPurchVerFailed
            return;
        }else {
            //  购买成功将交易凭证发送给服务端进行再次校验
            //  kIAppurchSuccess
        }
        
        // 在网络中传输数据,大多情况下是传输的字符串而不是二进制数据
        // 传输的是BASE64编码的字符串
        /**
         BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
         BASE64是可以编码和解码的
         */
        NSString *encodeStr = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        NSString *encodeLoad = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
        NSData *receiptData = [encodeLoad dataUsingEncoding:NSUTF8StringEncoding];
        
        //  发送网络POST请求,对购买凭证进行验证
        //  In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
        //  In the real environment, use https://buy.itunes.apple.com/verifyReceipt
        NSString *serverStr = @"https://buy.itunes.apple.com/verifyReceipt";
        if (flag) {
            serverStr = @"https://sandbox.itunes.apple.com/verifyReceipt";
        }
        
        NSURL *storeURL = [NSURL URLWithString:serverStr];
        
        NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
        [storeRequest setHTTPMethod:@"POST"];
        [storeRequest setHTTPBody:receiptData];
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [NSURLConnection sendAsynchronousRequest:storeRequest
                                           queue:queue
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                   if (connectionError) {
                                       // 无法连接服务器,购买校验失败
                                       // KIAPPurchVerFailed
                                       
                                   } else {
                                       NSError *error;
                                       NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                                                                    options:0
                                                                                                      error:&error];
                                       NSLog(@"----验证结果 %@",jsonResponse);
                                       if (!jsonResponse) {
                                           // 苹果服务器校验数据返回为空校验失败
                                           // KIAPPurchVerFailed
                                       }
                                       // 先验证正式服务器,如果正式服务器返回21007再去苹果测试服务器验证,沙盒测试环境苹果用的是测试服务器
                                       NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
                                       if (status && [status isEqualToString:@"21007"]) {
    #warning - Todo 给ViewController提示
                                           [self verifyPruchaseWithPaymentTransaction:transaction isTestServer:YES];
                                           
                                       }else if(status && [status isEqualToString:@"0"]){
    #warning - Todo 给ViewController提示
                                           //   kIAPPurchVerSuccwss 购买成功切验证成功
                                       }
                                   }
                               }];
        
        // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        
    }
    

    PS:验证流程

    订单正确性的验证:
    1.iOS客户端(购买成功)→ 到苹果服务器验证 → 苹果服务器返回验证结果,做相应处理
    2.iOS客户端(购买成功)→ 后台 → 苹果服务器验证 → 苹果服务器返回验证结果,做相应处理

    服务器要做的是:

    1.接收iOS前端发过来的购买凭证。

    2.判断凭证是否已经存在或验证过,然后存储该凭证。

    3.将该凭证发送到对应环境下的苹果服务器验证,并将验证结果返回给客户端。

    4.根据需求,是否修改用户相应信息。

    注意事项

    1.bundleID要与iTunes Connect上你App的相同,不然是请求不到产品信息的

    2.在沙盒环境进行测试内购的时候,要使用没有越狱的苹果手机。

    3.在沙盒环境下真机测试内购时,请去app store中注销你的apple ID,不然发起支付购买请求后会直接case:SKPaymentTransactionStateFailed。使用沙盒测试员的账号时不需要真正花钱的。

    4.如果只添加了一个沙盒测试员账号,当一个真机已经使用了这个账号,另一个真机再使用这个账号支付也是会发生错误的。那就去多建几个沙盒测试员账号使用不同的,反正也是免费的,填写也很快。

    5.监听购买结果,当失败和成功时代码中要调用:

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

    该方法通知苹果支付队列该交易已完成,不然就会已发起相同 ID 的商品购买就会有此项目将免费恢复的提示。

    请在本地做一下凭证存储

    现在订单正确性的验证是:iOS客户端(购买成功)→ 后台→后台到苹果服务器验证→处理后台返回结果做相应逻辑处理。

    注意:
    如果Your App 和 Your Server 中断了链接
    当我们前端购买成功后,凭证(receipt)本地保留一份,当与后台验证成功后,再将本地保留的凭证删除。
    否者一直使用本地已经保留的凭证与后台交互。

    测试前提条件:
    1.在itunesConnect中填写测试账号。
    2.在itunesConnect中填写税务单(就是银行账号,开户名,收款机构等等的税务单)。
    3.交易收据内容(receipt)

     "receipt":
        {
            "original_purchase_date_pst":"2015-06-22 20:56:34 America/Los_Angeles", //购买时间,太平洋标准时间
            "purchase_date_ms":"1435031794826", //购买时间毫秒
            "unique_identifier":"5bcc5503dbcc886d10d09bef079dc9ab08ac11bb",//唯一标识符
            "original_transaction_id":"1000000160390314", //原始交易ID
            "bvrs":"1.0",//iPhone程序的版本号
            "transaction_id":"1000000160390314", //交易的标识
            "quantity":"1", //购买商品的数量
            "unique_vendor_identifier":"AEEC55C0-FA41-426A-B9FC-324128342652", //开发商交易ID
            "item_id":"1008526677",//App Store用来标识程序的字符串
            "product_id":"cosmosbox.strikehero.gems60",//商品的标识 
            "purchase_date":"2015-06-23 03:56:34 Etc/GMT",//购买时间
            "original_purchase_date":"2015-06-23 03:56:34 Etc/GMT", //原始购买时间
            "purchase_date_pst":"2015-06-22 20:56:34 America/Los_Angeles",//太平洋标准时间
            "bid":"com.cosmosbox.StrikeHero",//iPhone程序的bundle标识
            "original_purchase_date_ms":"1435031794826"//毫秒
     }
    
    

    相关文章

      网友评论

      • 棍武中原:请问楼主,苹果给我返回没有商品是什么意思?
        商品ID填写对的,代码里面也调了。

      本文标题:Apple 内购流程(代码开发级别)

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