iOS开发内购零丢单问题

文章的主要内容

你好!这是我发表的第一段关于苹果技术的博客,如果有说的不对的地方请指教,今天要分享一下自己对于苹果内购的经验。苹果内购的前期准备工作基本相同,关于申请内购项目的时候要注意的是,内购项目和APP是同时审核的,所以先创建的内购得等新版本审核成功后才能正式购买。本片文章主要是关于内购代码级别的分析。

内购分析

关于内购这块,我们项目已经经过3个人修改,当时上线的时候没有啥问题,但是随着内购内容越来越多,就出现了问题,丢单问题随之出现,经过我查看了苹果的内购官方文档,要求所有的内购统一管理,苹果的官方解释,创建一个观察者,然后在软件启动的时候定义观察者;

 -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
 [[SKPaymentQueue defaultQueue] addTransactionObserver:inPurchasingManage];
}

我定义的观察者是appdalagate里的一个属性,所以在其他界面使用时直接调用appdalagate.inPurchasingManage即可,注销该方法放在软件进程杀死状态下:

-(void)applicationWillTerminate:(UIApplication *)application
{   
 [[SKPaymentQueue defaultQueue] removeTransactionObserver:inPurchasingManage];
 }

这样不管在哪个界面发起内购的话,都会走观察者里面的代码。

观察者代码构建

首先是根据内购产品的ID发起内购,因为这个ID,我是从后台拿过来的,前边不做赘述,

#pragma mark - 内购
-(void)requestProductData:(NSString *)type{
    NSArray *product = [[NSArray alloc] initWithObjects:type,nil]; 
    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];
}

通过这段代码就能成功呼起苹果的内购支付,并且走了
<SKProductsRequestDelegate,SKPaymentTransactionObserver>代理回调方法,这些苹果官方说明都有讲,虽然现在用的是swift语言,但是对于我们还依然用OC的人来说,不在话下:

#pragma mark - 实现SKProductsRequest的代理方法
    -(void)productsRequest:(SKProductsRequest *)request            didReceiveResponse:(SKProductsResponse *)response
{
      NSArray *product = response.products;
      if([product count] == 0){
      //如果这个参数没有,证明没有这个产品,也就不需要走下面的代码了
        return;
      } 
      SKProduct *p = nil;
      for (SKProduct *pro in product) {
        if([pro.productIdentifier isEqualToString:Singleton.currentProId]){
            p = pro;
        }
      }
      SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:p];
      NSString *userOrderNo = [NSString stringWithFormat:@"%@#%@",userID,orderNo];
      payment.applicationUsername = userOrderNo;
      [[SKPaymentQueue defaultQueue] addPayment:payment];
}

如果订单存在的话,就已经呼起内购了,说道这里可能有些人会质疑我存储订单号的方法,因为好多博客不推荐用applicationUsername来存储重要的数据,因为觉得可能会丢失数据,但是没有明确的操作会导致这个属性丢失,因为这里如果存着订单号,就很有可能消失,这样就会出现丢单问题,其实这个问题我也思考了,先不说这个参数容不容易丢,即使丢了怕啥,再给它申请个订单号不就得了。这个是我们厉害的后台想出来的办法,具体的订单号问题往下看,再说说我存的userID,是为了区别用户存的,避免了钱充错人的问题,(这里说一下存在的问题,如果一个用户付完款之后退出的账号,这时又赶巧applicationUsername丢失,如果用户下一次登录的是另一个账号,有可能充错人,但是不会丢单),下面是我重点介绍的逻辑:

#pragma mark - 实现SKPaymentQueue的回调方法
/*
 队列中的交易发生改变时,就会调用该方法
 */
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    /*
     SKPaymentTransactionStatePurchasing,    正在购买
     SKPaymentTransactionStatePurchased,     已经购买(购买成功)
     SKPaymentTransactionStateFailed,        购买失败
     SKPaymentTransactionStateRestored,      恢复购买
     SKPaymentTransactionStateDeferred       未决定
     */
    for (SKPaymentTransaction *transation in transactions) {
        NSString *productIdentifier = transation.payment.productIdentifier;
        switch (transation.transactionState) {
            case SKPaymentTransactionStatePurchasing:
            break;
                
            case SKPaymentTransactionStatePurchased:{

                NSString *newOrderNo = transation.payment.applicationUsername;
               //这个方法就是用户支付成功后回调方法,这样就可以拿着订单号和用户id去校验了了
                break;
                
            case SKPaymentTransactionStateFailed:{
      
                // 用户取消或者支付失败取消订单
                NSString *newOrderNo = transation.payment.applicationUsername;
               
                break;
                
            case SKPaymentTransactionStateRestored:
                //如果显示已经购买过该商品,则恢复该商品的购买性 <非消耗型商品>               
                 [self restoreCompletedTransactions:transation];             
                break;                
            case SKPaymentTransactionStateDeferred:{
                NSString *newOrderNo = transation.payment.applicationUsername;          
                [queue finishTransaction:transation];
                break;
            default:
                break;
        }
    }
}

SKPaymentTransactionStatePurchased 这个方法就是用户支付成功后回调方法,applicationUsernam就是发起内购时,我们赋值的用户id和订单号的拼接,这里讲一下我们的出来,不管这个订单号有没有,我们都会拿着receiptData去校验,获取方法如下,这时只要receiptData存在,后台就会去查询用户之前是否发起过一个订单号,如果有直接二次校验,如果没有,那就给用户申请一个新的订单号,然后再进行校验。这样不管用户有没有订单号都无所谓。

    NSData *receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];

着重所以下finishTransaction方法的处理

 [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

我只有在用户二次校验成功后才会调用该方法,如果不走这个方法,每次软件启动的时候,都会走上述的代理,所以如果你没有验证的话,用户卸载软件再安装以后还会走这代理,然后再去后天校验就行了,保证零丢单就是这么简单,不知道你们看懂没有?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值