美文网首页为了更好的活着
苹果支付的集成遇到的一些坑

苹果支付的集成遇到的一些坑

作者: 哈酒拎壶冲 | 来源:发表于2017-08-27 15:26 被阅读60次

    最近公司要求项目中集成苹果支付功能,我考以前完全没有搞过有没有,经过几天的摸爬滚打 ,终于依靠强大的度娘完美的实现了苹果支付功能集成并成功上线,接下来把集成的过程做下记录 重温一次

    在正式上代码之前先说明下 苹果支付有俩种模式 一 服务器模式流程 相对更安全 二 内置模式 相对简单但是不是很安全 (个人建议 如果交易金额很大 建议使用第一种模式 接下来我使用的是第二种模式 因为我们涉及的金额很小

    接下来直接上代码 截图


    首先创建协议(关于这块儿不细说 网上很多教程)

    屏幕快照 2017-08-27 14.46.56.png

    设置内购类型(选择类型一定要选准确 不然绝对悲剧 ID 最好是boundle ID + 数值 之后把信息填完整 到准备审核状态)

    屏幕快照 2017-08-27 14.49.16.png

    创建沙盒模拟账号(这个账号是你的测试icloud账号 上线时候要提供给苹果审核测试用 和你本身有的账号并不冲突)

    屏幕快照 2017-08-27 14.57.19.png

    然后开始集成代码

    引入库 设置代理

    // 1.首先导入支付包
    #import <StoreKit/StoreKit.h>
    //代理
    SKPaymentTransactionObserver,SKProductsRequestDelegate
    

    初始化商品数据 并请求服务器

    //请求苹果商品
    - (void)getRequestAppleProduct
    {
        // 7.这里的com.czchat.CZChat01就对应着苹果后台的商品ID,他们是通过这个ID进行确定的。
        NSArray *product = [[NSArray alloc] initWithObjects:@"com.shzqt.company.shzqt04",nil];
        
        NSSet *nsset = [NSSet setWithArray:product];
        
        // 8.初始化请求
        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
        request.delegate = self;
        
        // 9.开始请求
        [request start];
       // 因为苹果支付要调用苹果服务器 所以有时候会很慢 你点击了会等待响应 所以这里要做个文字提升
        [self.view makeToast:@"正在请求支付,请稍等" duration:2.0 position:CSToastPositionCenter];
    }
    

    正在验证商品信息 正在生成购买请求

    // 10.接收到产品的返回信息,然后用返回的商品信息进行发起购买请求
    - (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
    {
        NSArray *product = response.products;
        
        //如果服务器没有产品
        if([product count] == 0){
            NSLog(@"nothing");
            return;
        }
        
        SKProduct *requestProduct = nil;
        for (SKProduct *pro in product) {
            NSLog(@"%@", [pro description]);
            NSLog(@"%@", [pro localizedTitle]);
            NSLog(@"%@", [pro localizedDescription]);
            NSLog(@"%@", [pro price]);
            NSLog(@"%@", [pro productIdentifier]);
            
            // 11.如果后台消费条目的ID与我这里需要请求的一样(用于确保订单的正确性)
            if([pro.productIdentifier isEqualToString:@"com.shzqt.company.shzqt04"]){
                requestProduct = pro;
            }
        }
        // 文字提升 服务器收到商品信息 正在发生购买请求
        [self.view makeToast:@"正在发送购买请求" duration:2.0 position:CSToastPositionCenter];
        // 12.发送购买请求
        SKPayment *payment = [SKPayment paymentWithProduct:requestProduct];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    

    收到反馈信息 如果成功 验证购买单号确定购买

    //反馈请求的产品信息结束后
    - (void)requestDidFinish:(SKRequest *)request{
        NSLog(@"信息反馈结束");
    }
    //请求失败
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
        NSLog(@"error:%@", error);
        [self.view makeToast:@"请求超时,请重新连接" duration:3.0 position:CSToastPositionCenter];
    }
    // 14.交易结束,当交易结束后还要去appstore上验证支付信息是否都正确,只有所有都正确后,我们就可以给用户方法我们的虚拟物品了。
    - (void)completeTransaction:(SKPaymentTransaction *)transaction
    {
        //    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        NSString * str=[[NSString alloc]initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
        
        NSString *environment=[self environmentForReceipt:str];
        NSLog(@"----- 完成交易调用的方法completeTransaction 1--------%@",environment);
        
        
        // 验证凭据,获取到苹果返回的交易凭据
        // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
        NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
        // 从沙盒中获取到购买凭据
        NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
        /**
         20      BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
         21      BASE64是可以编码和解码的
         22      */
        NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        
        NSString *sendString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
        NSLog(@"_____%@",sendString);
        NSURL *StoreURL=nil;
        if ([environment isEqualToString:@"environment=Sandbox"]) {
            
            StoreURL= [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];
        }
        else{
            
            StoreURL= [[NSURL alloc] initWithString: @"https://buy.itunes.apple.com/verifyReceipt"];
        }
        //这个二进制数据由服务器进行验证;zl
        NSData *postData = [NSData dataWithBytes:[sendString UTF8String] length:[sendString length]];
        
        NSLog(@"++++++%@",postData);
        NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:StoreURL];
        
        [connectionRequest setHTTPMethod:@"POST"];
        [connectionRequest setTimeoutInterval:50.0];//120.0---50.0zl
        [connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
        [connectionRequest setHTTPBody:postData];
        
        //开始请求
        NSError *error=nil;
        NSData *responseData=[NSURLConnection sendSynchronousRequest:connectionRequest returningResponse:nil error:&error];
        if (error) {
            [self.view makeToast:@"请求超时" duration:3.0 position:CSToastPositionCenter];
            NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
            
            return;
        }
        NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
        NSLog(@"请求成功后的数据:%@",dic);
        //这里可以等待上面请求的数据完成后并且state = 0 验证凭据成功来判断后进入自己服务器逻辑的判断,也可以直接进行服务器逻辑的判断,验证凭据也就是一个安全的问题。楼主这里没有用state = 0 来判断。
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
        NSString *product = transaction.payment.productIdentifier;
        NSLog(@"transaction.payment.productIdentifier++++%@",product);
        if ([product length] > 0)
        {
            NSArray *tt = [product componentsSeparatedByString:@"."];
            
            NSString *bookid = [tt lastObject];
            
            if([bookid length] > 0)
            {
                
                NSLog(@"打印bookid%@",bookid);
                //这里可以做操作吧用户对应的虚拟物品通过自己服务器进行下发操作,或者在这里通过判断得到用户将会得到多少虚拟物品,在后面([self getApplePayDataToServerRequsetWith:transaction];的地方)上传上面自己的服务器。
            }
        }
        //此方法为将这一次操作上传给我本地服务器,记得在上传成功过后一定要记得销毁本次操作。调用[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
        //    [self getApplePayDataToServerRequsetWith:transaction];
    }
    

    验证成功 购买弹框出现

    屏幕快照 2017-08-27 15.14.08.png

    购买状态监控

    // 13.监听购买结果
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
        for(SKPaymentTransaction *tran in transaction){
            
            switch (tran.transactionState) {
                case SKPaymentTransactionStatePurchased:
                    NSLog(@"交易完成");
                    //imgView.hidden = YES;
                    //                if (![isShowTime2 isEqualToString:@"1.0.08.25"]) {
                    //[self paysuccess];
                    [self completeTransaction:tran];
                    
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                    
    //                }
                    break;
                case SKPaymentTransactionStatePurchasing:
                    NSLog(@"商品添加进列表");
                    
                    break;
                case SKPaymentTransactionStateRestored:
                    NSLog(@"已经购买过商品");
                    
                    break;
                case SKPaymentTransactionStateFailed:
                    NSLog(@"交易失败");
    //                [self.view makeToast:@"交易失败" duration:3.0 position:CSToastPositionCenter];
                    break;
                default:
                    break;
            }
        }
    }
    //结束后一定要销毁
    - (void)dealloc
    {
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
    -(NSString * )environmentForReceipt:(NSString * )str
    {
        str= [str stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
        
        str = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        
        str = [str stringByReplacingOccurrencesOfString:@"\t" withString:@""];
        
        str=[str stringByReplacingOccurrencesOfString:@" " withString:@""];
        
        str=[str stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        
        NSArray * arr=[str componentsSeparatedByString:@";"];
        
        //存储收据环境的变量
        NSString * environment=arr[2];
        return environment;
    }
    

    因为避免订单创建一半没有交易 所以每次推出 和进入支付页面要清空订单

    //[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    

    到此 苹果支付代码就集成完毕了 上线时候要先提交IPA包 然后在提交内购项目
    如果有人使用了本攻略 有问题请及时交流 毕竟 提出一个问题,往往比解决一个问题更重要。 还需要更进一步 需要大家帮助

    相关文章

      网友评论

      • 李某lkb:感觉还不错,到时候集成就看这篇文章了.
        哈酒拎壶冲:@李某lkb ::pray:
      • 哈酒拎壶冲:关于内购已经上线修改价格的问题,直接在itunes connect 里边修改价格就可以了 修改完价格记得改变截图 然后等30分钟左右 APP支付界面价格就变化了 不用重新上线

      本文标题: 苹果支付的集成遇到的一些坑

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