美文网首页
苹果IAP总结

苹果IAP总结

作者: 屮艸芔 | 来源:发表于2017-10-18 18:53 被阅读29次

    最近公司项目要增加内购功能,摸索了几天,遇到不少问题,先记录下来,有图有真相,我尽量多贴些图片。

    1、首先你要有一个app,无论上架与否都可以,如果没有,就新建一个,怎么新建APP就不多说了,有一条要注意,APP的APPID信息里一定要支持In-App Purchases。

    2、进入iTunes Connect 选择我的APP,在APP的详情页,选择功能选项,添加APP内要出售的物品

    添加物品的时候会让你选择物品类型,按你的需求选择就行了,有几个商品就添加几个,

    注意,物品的资料要填写完整,必填项一定要填,

    审核信息里的图片要上传,备注也要写上,

    3、当你的物品状态是这样的,就说明可以在代码里获取物品了,

    当然,这时候你要直接写代码获取,肯定是获取不到的,因为还有一些非APP信息需要补充,想当初我就是没逼着产品完善信息,自己一直找错误,浪费了许多时间。

    协议、税务和银行业务,这里面的信息怎么填写,这篇文章写的很详细,就不多说了,让产品参考着写吧。

    4、为了测试支付情况,我们还需要新建一个测试用的Apple id,在iTunes Connect里选择用户和职能

    选择沙箱技术测试员,添加测试账号,可以参考这篇文章

    5,以上都完成之后,终于可以打开编译器了,对,打开xcode,

    打开Xcode 的这个功能。

    6,写代码吧,

    引入#import <StoreKit/StoreKit.h>

    遵守协议

    添加监听

    [[SKPaymentQueue defaultQueue]addTransactionObserver:self];

    (别忘了移除监听)

    请求物品列表可以从自己的服务器,也可以从苹果的服务器请求,但是从苹果请求的话,慢慢等吧,很慢的,而且不稳定。

    下面的代码是从点击物品开始,直到完成付款并通过自己的服务器验证。

    //点击购买按钮- (void)clickPurcaseBtnAction{   

     //点击按钮的时候判断app是否允许apple支付  

      if ([SKPaymentQueue canMakePayments]) {     

       //请求苹果后台商品     

       [self requestProductData:_productId];   

     }  

      else    {      

      NSLog(@"用户不允许应用内购买");  

      }}

    //去苹果服务器请求商品

    -(void)requestProductData:(NSString *)type {  

      //根据商品ID查找商品信息  

      NSArray *product = [[NSArray alloc] initWithObjects:type, nil];   

     NSSet *nsset = [NSSet setWithArray:product];   

     //创建SKProductsRequest对象,用想要出售的商品的标识来初始化,然后附加上对应的委托对象。   

     //该请求的响应包含了可用商品的本地化信息。  

      SKProductsRequest *request = [[SKProductsRequest alloc]  initWithProductIdentifiers:nsset];    

    request.delegate = self;   

     [request start];

    }

    // SKProductsRequestDelegate

    //查询成功的回调

    -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:  (SKProductsResponse *)response {    

    NSArray *product = response.products;    

    //invalidProductIdentifiers是不被App Store所识别的产品id字符串数组,通常为空   

     NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);   

     //如果没应用的银行信息没填写完整,你添加的商品会出现在无效商品列表里   

     NSLog(@"无效商品列表 :%@",response.invalidProductIdentifiers);   

     if(product == nil) {  

          return;  

      }    

    //数组的count代表回调的产品ID数组的长度   

     if (product.count == 0) {      

      NSLog(@"无法获得产品信息,购买失败");   

            return;   

     }   

     //SKProduct对象包含了在App Store上注册的商品的本地化信息。   

     SKProduct *storeProduct = nil;  

      for (SKProduct *pro in product) {     

       if ([pro.productIdentifier isEqualToString:_productId]) {        

        storeProduct = pro;     

       }   

     }   

     if(storeProduct == nil) {     

       return;  

      }    

    //创建一个支付对象,并放到队列中    

    self.g_payment = [SKMutablePayment paymentWithProduct:storeProduct];   

     //设置购买的数量   

     self.g_payment.quantity = 1;  

      [[SKPaymentQueue defaultQueue] addPayment:self.g_payment];

    }

    //查询失败的回调

    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {   

     NSLog(@"请求商品失败%@", error);

    }

    //交易有结果时会调用此回调函数,SKPaymentTransactionObserver - 交易的回调

    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray*)transactions {

    for (SKPaymentTransaction *transaction in transactions) {

    switch (transaction.transactionState) {

    case SKPaymentTransactionStatePurchasing: //正在购买

    break;

    case SKPaymentTransactionStatePurchased:  //购买成功

    [self completedTransaction:transaction Product:self.productId];

    break;

    case SKPaymentTransactionStateFailed://购买失败

    {

    UIAlertView *alt3 = [[UIAlertView alloc]initWithTitle:@"提示" message:@"购买失败" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];

    [alt3 show];

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

    }

    break;

    case SKPaymentTransactionStateRestored:  //恢复购买

    {

    //当上次购买过程中,因某种原因以外中断而没能调用完成购买的API时,再次进入APP苹果会提醒恢复购买,可以在这里继续处理

    }

    break;

    case SKPaymentTransactionStateDeferred:  //最终状态未确认

    {

    UIAlertView *alt5 = [[UIAlertView alloc]initWithTitle:@"提示" message:@"无法获得产品信息,购买失败" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];

    [alt5 show];

    }

    break;

    default:

    break;

    }

    }

    }

    -(void)completedTransaction:(SKPaymentTransaction*)transaction Product:(NSString *)productId{

    //NSString * productIdentifier = transaction.payment.productIdentifier;

    NSString *transactionReceiptString= nil;

    NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];

    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];

    transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

    把transactionReceiptString发送给自己的服务器让后台验证就行了,还有一点就是对于sandbox环境和生产环境,校验的地址是不同的,后台校验的时候要区分测试和生产环境,注意:提交苹果审核后,审核的时候苹果用的也是测试环境,后台去验证的时候需要在测试环境验证,过审后再去生产环境验证。

    https://buy.itunes.apple.com/verifyReceipt (生产)

    https://sandbox.itunes.apple.com/verifyReceipt (sandbox测试)

    /* 下面的代码是客户端自己验证的方法

    NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", transactionReceiptString];//拼接请求数据

    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];

    NSURL *url=[NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];

    NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];

    requestM.HTTPBody = bodyData;

    requestM.HTTPMethod=@"POST";

    //创建连接并发送同步请求

    NSError *error=nil;

    NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];

    if (error) {

    NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);

    return;

    }

    NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];

    NSLog(@"%@",dic);

    if([dic[@"status"] intValue]==0){

    [[UIApplication sharedApplication].keyWindow makeToast:@"购买成功" duration:1.5 position:CSToastPositionCenter];

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

    NSDictionary *dicReceipt= dic[@"receipt"];

    NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];

    NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识

    //如果是消耗品则记录购买数量,非消耗品则记录是否购买过

    //NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];

    if ([productIdentifier isEqualToString:@"123"]) {

    //            NSInteger purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量

    //            [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];

    }else{

    //  [defaults setBool:YES forKey:productIdentifier];

    }

    //在此处对购买记录进行存储,可以存储到开发商的服务器端

    }else{

    NSLog(@"购买失败,未通过验证");

    NSString *str = [NSString stringWithFormat:@"失败了%@",dic[@"status"]];

    [[UIApplication sharedApplication].keyWindow makeToast:str duration:1.5 position:CSToastPositionCenter];

    }

    */

    }

    遇到的问题

    1、银行信息不完整造成的无法获取商品列表

    2、苹果服务器慢,网络问题等造成的购买失败

    3、交给服务器验证的字符串,服务器需要再进行UTF8编码一次再去验证,否则会返回21002,收据数据不符合格式。

    4、顺便注上苹果反馈的错误码:

    21000App Store无法读取你提供的JSON数据

    21002 收据数据不符合格式

    21003 收据无法被验证

    21004 你提供的共享密钥和账户的共享密钥不一致

    21005 收据服务器当前不可用

    21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中

    21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证

    21008 收据信息是产品环境中使用,但却被发送到测试环境中验证

    现在代码还比较散乱,只是实现了功能,后面准备封装一下,遇到问题再来补充吧~

    相关文章

      网友评论

          本文标题:苹果IAP总结

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