苹果内购上线总结

作者: 程序媛vivid | 来源:发表于2018-11-08 16:36 被阅读74次
一, 如何定义内购商品?

线上购买线上使用,不涉及到线下实物的交易都需要用苹果内购。简言之,就是虚拟商品,比如线上音乐,电子图书,设计作品,游戏币等。

二,Appstore Connect线上配置
1.协议、税务和银行业务填写;
协议、税务和银行业务
2.创建内购项目;
创建内购项目! 选择内购项目类型
3.设置沙箱测试账号;
沙箱测试账号

由于回头总结,截图未能一一提供,在此只提示设置入口而后按引导一步步去做就行了。

三,代码集成
1.添加和移除监听设置
// 添加观察者
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// 移除观察者
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
2.通过产品ID获取产品信息列表
- (void)getProductInfo:(NSString *)productIdentifier{
    self.selectProductId = productIdentifier;
    if (![SKPaymentQueue canMakePayments]){
        if (self.failurePurchaseBlock) {
            self.failurePurchaseBlock(@"不允许付费购买商品");
        }
        return;
    }

    if (productIdentifier.length > 0){
        NSArray * product = [[NSArray alloc] initWithObjects:productIdentifier, nil];
        NSSet *set = [NSSet setWithArray:product];
        SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
        request.delegate = self;
        [request start];//开始请求
    }
}
/*
查询成功后的回调(经由getProductInfo函数发起的产品信息查询,成功后返回执行的回调)。
*/
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
  NSArray *myProduct = response.products;
  if (myProduct.count == 0){
      if (self.failurePurchaseBlock) {
          self.failurePurchaseBlock(@"无法获取商品信息");
      }
      return;
  }
  //选择用户选择的档位发起购买请求
  for (SKProduct *sKProduct in myProduct) {
      
      NSLog(@"pro info");
      NSLog(@"SKProduct 描述信息:%@", sKProduct.description);
      NSLog(@"localizedTitle 产品标题:%@", sKProduct.localizedTitle);
      NSLog(@"localizedDescription 产品描述信息:%@",sKProduct.localizedDescription);
      NSLog(@"price 价格:%@",sKProduct.price);
      NSLog(@"productIdentifier Product id:%@",sKProduct.productIdentifier);
      
      if([sKProduct.productIdentifier isEqualToString: self.selectProductId]){
          [self buyProduct:sKProduct];
          break;
      }
  }
}
3.发起购买请求
/*解决掉单的问题:
 1.将需要传给后台服务器的参数(比如订单id,用户id)放到SKMutablePayment的applicationUsername字段里面;
 */
-(void)buyProduct:(SKProduct *)product{
    // 1.创建票据
    NSString *orderId = @"123";
    NSString *userId = @"abc";
    NSString *userName = [NSString stringWithFormat:@"%@-%@",userId,orderId];
    SKMutablePayment *skpayment = [SKMutablePayment paymentWithProduct:product];
    skpayment.applicationUsername = userName;
    
    // 2.将票据加入到交易队列
    [[SKPaymentQueue defaultQueue] addPayment:skpayment];
}
4.实现观察者监听付钱的代理方法,只要交易发生变化就会走下面的方法
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
    /*
     SKPaymentTransactionStatePurchasing,    正在购买
     SKPaymentTransactionStatePurchased,     已经购买
     SKPaymentTransactionStateFailed,        购买失败
     SKPaymentTransactionStateRestored,      回复购买中
     SKPaymentTransactionStateDeferred       交易还在队列里面,但最终状态还没有决定
     */
    self.sKPaymentQueue = queue;
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:{
                NSLog(@"正在购买");
            }break;
            case SKPaymentTransactionStatePurchased:{
                NSLog(@"购买成功");
               //这里可以做一个是否同一用户的判断,因为如果是更新数据时产生的订单,跟下单时的用户未必是同一个用户
               //isLogin:是否登录状态; userId:该订单的用户id;currentUserId:当前登录用户的id;
NSArray *arr = [transaction.payment.applicationUsername componentsSeparatedByString:@"-"];
                NSString *userId = @"";
                if (arr.count>0) {
                    userId = arr[0];
                }
                if (isLogin && [currentUserId isEqualToString: userId]) {
                //取出凭证
                 [self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction];
                }
            }
                break;
            case SKPaymentTransactionStateFailed:{
                NSLog(@"购买失败");
                if (self.failurePurchaseBlock) {
                    self.failurePurchaseBlock(@"购买失败");
                }
                // 购买失败也要把这个交易移除掉
                [queue finishTransaction:transaction];
            }break;
            case SKPaymentTransactionStateRestored:{
                NSLog(@"回复购买中,也叫做已经购买");
                // 回复购买中也要把这个交易移除掉
                [queue finishTransaction:transaction];
            }break;
            case SKPaymentTransactionStateDeferred:{
                NSLog(@"交易还在队列里面,但最终状态还没有决定");
            }break;
            default:
                break;
        }
    }
}
5.获取凭证,并且将凭证发送给后台校验
-(void)buyAppleStoreProductSucceedWithPaymentTransactionp:(SKPaymentTransaction *)paymentTransactionp {
    NSString * productIdentifier = paymentTransactionp.payment.productIdentifier;
     NSLog(@"productIdentifier Product id:%@", productIdentifier);
    NSString *transactionReceiptString= nil;
    
    //系统IOS7.0以上获取支付验证凭证的方式应该改变,切验证返回的数据结构也不一样了。
    NSString *version = [UIDevice currentDevice].systemVersion;
    if([version intValue] >= 7.0){
        // 验证凭据,获取到苹果返回的交易凭据
        // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
        NSURLRequest * appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];
        NSError *error = nil;
        NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];
        transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    }
    //调接口服务器校验,该方法自行实现
    [self varifyPurchaseData:paymentTransactionp andReceipt:transactionReceiptString];
}

注意:后台校验成功后,需要把这个成功的交易移除掉

[self.sKPaymentQueue finishTransaction:transaction];
四,踩过的坑汇总
1.创建沙盒测试账号的时候报错:Unknown Errors while creating Sandbox Tester, Please check Error Log, email=***

解决:密码设置复杂一些。

2.购买成功后的校验处理方案
  • 为了保证安全,购买成功后,需要通过绑定产品id,订单号,凭证提供给后台,让后台调苹果接口解析凭证进行校验;
  • 由于测试和审核的时候用的是沙盒测试,上线后才用正式环境,所以后台需要做二次校验。
  1. 即是先校验正式环境,如果成功则是上线后购买成功;
  2. 如果校验失败,再校验测试环境,成功的话则是沙盒测试下购买成功,失败则是沙盒测试下购买失败;
3.审核遇到的问题

(1)Guideline 3.1.1 - Business - Payments - In-App Purchase
We noticed that your app uses in-app purchase products to purchase credits or currencies that are not consumed within the app, which is not appropriate for the App Store.

通过跟苹果审核的客服电话沟通, 苹果内购不能包含“提现”等有套现或洗钱嫌疑的功能——去掉“提现”的功能即可;

(2)一个 App 内购买项目被退回,并在以下列表中以高亮显示

点击高亮的内购项目,编辑一下描述的文字,写得更加符合项目并且详细一些,点击保存。

4.掉单问题处理

网上有说把创建的订单信息存储在本地,但是思量后发现有不少的问题,如果app被删掉了,存储的订单信息一样会被同步删除,除非存储在钥匙串里面,否则还是有概率调单。
后来参考了另一篇文章提到的applicationUsername字段,于是解决了这个问题,而且不需要做各种存取操作。

下面看看如何去获取未完成凭证校验的订单(哪些情况会产生掉单):
1.苹果内购过程中,苹果服务器响应慢,用户杀掉进程的情况;
2.拿到凭证,传给公司后台服务器过程中,网络问题导致接口访问失败等情况。

解决步骤:

  1. 创建订单的时候,将orderId和userId存储在applicationUsername里面(可以将更多的字段转为json字符串,取出的时候再转为字典);
  2. 在需要更新苹果未完成订单的页面添加监听方式(即是代码集成第一步),可以在是启动app页面,或刷新金额的页面;
  3. 在购买成功的代理中,判断订单中的用户id和当前登录的用户id是否一致,如果一致,则取出订单id和凭证一起传给后台服务器;(注:该页面如果没有登录Apple ID的话,会弹出弹框提示用户登录)
  4. 当后台服务器接收到凭证等数据成功时,通过finishTransaction结束该交易。

这样下来,几乎是不会再有掉单的问题了。

参考资料:
iOS内购最新讲解
真·iOS内购的完整流程

相关文章

  • 苹果内购上线总结

    一, 如何定义内购商品? 线上购买线上使用,不涉及到线下实物的交易都需要用苹果内购。简言之,就是虚拟商品,比如线上...

  • ios苹果内购iaphelper

    https://www.jianshu.com/p/647d6d3ce471 苹果内购上线总结 https://w...

  • iOS开发-内购丢单处理方式

    之前写了一篇关于内购的博客iOS开发-2017苹果内购最新教程,上线审核很顺利,但是上线后很多问题暴露出来.用户购...

  • iOS-苹果内购

    网上关于苹果内购已经有无数的文章,最近刚好接触苹果内购,简单总结一下整体流程:① 苹果结算账户信息,公司信息填写(...

  • iOS 内购的实现

    自己开发的视频直播项目,牵涉到充值金币,用到了苹果公司的内购,趴坑了两天,这里总结下实现苹果内购。 一. 创建测试...

  • iOS 11 如何测试苹果商店内内购

    如何测试苹果商店内内购? 有什么现象? 0、苹果商店的内购,用户在苹果商店中选择内购产品时,如果用户已经安装好此应...

  • Flutter上线项目实战——苹果内购

    一、以下是消耗类和非消耗类的正常流程(订阅类的不太清楚) 1.进入充值页面,向app server获取produc...

  • 苹果内购和Apple Pay 的区别

    苹果内购 和 Apple Pay 的区别 关注苹果支付和内购也是从苹果发布打赏功能必须走“内购”流程后,引发微信以...

  • 苹果内购流程

    苹果内购流程

  • 苹果内购IAP 简单总结

    1. 何为苹果内购IAP IAP(in-app-purchase),指苹果平台上所有在应用内购买的虚拟商品(商品的...

网友评论

    本文标题:苹果内购上线总结

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