苹果内购上线总结

一, 如何定义内购商品?

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

二,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 {
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
    NSString *encodedReceipt = @"";
    if (!receipt) {
        NSLog(@"no receipt");
        /* No local receipt -- handle the error. */
    } else {
        /* Get the receipt in encoded format */
        encodedReceipt = [receipt base64EncodedStringWithOptions:0];
    }
  //调接口服务器校验,该方法自行实现
  // [self varifyPurchaseData:paymentTransactionp andReceipt:encodedReceipt];
}

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

[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内购的完整流程

你可能感兴趣的:(苹果内购上线总结)