美文网首页支付集成内购1
iOS 苹果内购的血与泪(坑爹的苹果)

iOS 苹果内购的血与泪(坑爹的苹果)

作者: 大佬的世界我不懂 | 来源:发表于2019-03-25 10:49 被阅读57次

iOS苹果内购

这边文章对于注册流程和注意事项说的非常的详细了但是里面还是有很多的坑,我自己封了一个类用于针对自己项目的使用地址如下

个人demo里面有个

屏幕快照 2019-03-25 上午10.28.18.png
里面需要注意的几个点:
#pragma mark - SKPaymentTransactionObserver // 监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    [CXLoadingHud dismissHud];
    for (SKPaymentTransaction *tran in transactions) {
        //NSLog(@"%ld====",(long)tran.transactionState);
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased: //交易完成
                //订阅特殊处理
                if(tran.originalTransaction){
                    //如果是自动续费的订单originalTransaction会有内容
                    NSLog(@"自动续费的订单,originalTransaction = %@",tran.originalTransaction.transactionIdentifier);
                    //tran.originalTransaction.transactionIdentifier
                    //SKPaymentTransaction
                    [self completeTransaction:tran isAutomatically:YES];
                    //[self verifyPurchaseWithPaymentTransaction:tran isTestServer:NO];
                }else{
                    //普通购买,以及 第一次购买 自动订阅 第一次自动订阅一定会走这个方法,如果订购成功后一定会走上面的判断
                   // NSLog(@"%@-------",tran.transactionIdentifier);
                    [self completeTransaction:tran isAutomatically:NO];
                }
                
                break;
            case SKPaymentTransactionStatePurchasing://商品添加进列表
#if DEBUG
                
                //NSLog(@"%ld====",tran.error.code);
                //NSLog(@"%@====",[[NSString alloc]initWithData:tran.payment.requestData encoding:NSUTF8StringEncoding]);
                //[TDGAVirtualCurrency onChargeRequst:@"" iapId:@"" currencyAmount:0 currencyType:@"" virtualCurrencyAmount:0 paymentType:@""];
#endif
                break;
            case SKPaymentTransactionStateRestored://购买过
#if DEBUG
                NSLog(@"已经购买过商品");
#endif
                // 消耗型不支持恢复购买
                //[[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStateFailed://交易失败
  如果tran.error 打印报错的话,沙盒测试的时候总是报无法连接itunes ,再确保你自己的前期准备工作是ok的话,大概率都是苹果自己的沙盒测试服务器自己gg了,经历过一次,坑爹的玩意,咋都找不出来,最后是苹果自己的问题
                  NSLog(@"%@====",tran.error);
                //SKErrorUnknown
                [self failedTransaction:tran];
                
                break;
            default:
                break;
        }
    }
}

// 交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction isAutomatically:(BOOL)isAutomatically{
    // Your application should implement these two methods.
    //    票据的校验是保证内购安全完成的非常关键的一步,一般有三种方式:
    //    1、服务器验证,获取票据信息后上传至信任的服务器,由服务器完成与App Store的验证(提倡使用此方法,比较安全)我选择的这种,但是会丢单,不过我暂时没有处理,准备用一个plist文件存储下在app再次启动的时候请求下后台,不过这个只是减少了丢单率而且,苹果的支付做得很垃圾
    //    2、本地票据校验
    //    3、本地App Store请求验证
    
    //    NSString * productIdentifier = transaction.payment.productIdentifier;
    //    NSString * receipt = [transaction.transactionReceipt base64EncodedString];
    //    if ([productIdentifier length] > 0) {
    //
    //    }
    NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
    // 向自己的服务器验证购买凭证
    //NSError *error;
    //转化为base64字符串
    NSString *receiptString=[receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//网上转换成base64后后台一直解析不了需要如下处理除去receiptdata中的特殊字符
    NSString *receipt1=[receiptString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    NSString *receipt2=[receipt1 stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    NSString *receipt3=[receipt2 stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
    //最终将这个receipt3的发送给服务器去验证就没问题啦!
    //自动订阅(自动续费月卡)需要多加一个参数
    
    NSString * product_id = transaction.payment.productIdentifier;
    NSString * transaction_id = transaction.transactionIdentifier;
    
    
    NSMutableDictionary * requestContents = [[NSMutableDictionary alloc]init];
#希望各位处理的时候能将  transaction_id 和 receipt都传给后台因为苹果的receipt解析后里面有很多数据,transaction_id能够帮助后台迅速定位最新的订单,因为测试的时候发现receipt里面有个数组,一般是最后一条数据是最新的,但是坑爹的苹果总是变,有时候是数组的第一条,所以transaction_id至关重要,而且在自动订阅和消耗性两类中,自动订阅的 transaction_id2和transaction_id在第一次请求的时候是一致的,后面的话,要区分只能transaction_id所以这个传给后台至关重要
transaction_id2 = transaction.originalTransaction.transactionIdentifier;
        NSString * transaction_id = transaction.transactionIdentifier;
        [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id,@"originalTransaction":transaction_id2}];
    }else{
        if (self.parmas.allKeys.count > 0) {
            [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"uid":self.parmas[@"uid"],@"amount":self.parmas[@"amount"],@"actorid":self.parmas[@"userRoleId"],@"server":self.parmas[@"serverId"],@"order_no":self.parmas[@"cpOrderNo"],@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id}];
        }
    }
    
    NSString * parameters = [self parameters:requestContents];
    NSString * url = isAutomatically ? autoURL : consumptionURL;
    NSURL *storeURL = [NSURL URLWithString:url];
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:[parameters dataUsingEncoding:NSUTF8StringEncoding]];
    [storeRequest setTimeoutInterval:30];
    
    NSURLSession *session = [NSURLSession sharedSession];
    
    NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //服务器返回的responseObject是gbk编码的字符串,通过gbk编码转码就行了,转码方法如下:
        NSString*gbkStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        //转码之后再转utf8解析
        NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:[gbkStr dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
      
        if (jsonDict.allKeys.count > 0) {
            if ([[jsonDict objectForKey:@"code"]intValue] == 0) {
                //[CXLoadingHud showHudWithText:@"购买成功" delay:2];
                NSDictionary * dataDict = jsonDict[@"data"];
                [[CXInformationCollect collectInfo]fb_mobile_purchase:dataDict[@"amount"] currency:@""];
                [[CXInformationCollect collectInfo]af_purchase:@{@"amount":dataDict[@"amount"]}];
            }else if ([[jsonDict objectForKey:@"code"]intValue] == 1){
                [CXLoadingHud showHudWithText:@"服务器验签失败" delay:2];
                
            }
        }
        
    }];
    
    [dataTask resume];

    //本地像苹果app store验证,上面是像自己的服务器验证
    //[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
    // 验证成功与否都注销交易,否则会出现虚假凭证信息一直验证不通过,每次进程序都得输入苹果账号
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    //[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}

相关文章

  • iOS 苹果内购的血与泪(坑爹的苹果)

    iOS苹果内购 这边文章对于注册流程和注意事项说的非常的详细了但是里面还是有很多的坑,我自己封了一个类用于针对自己...

  • Flutter 接入iOS苹果内购支付踩坑过程

    如何配置内购商品 坑1:项目与价格配置 苹果内购支付和我们平时接入支付宝或者微信支付有很大的差别。 苹果内购支付的...

  • [iOS]苹果内购的踩坑

    1,TransactionState 2. 卡单,提示已购买将免费恢复 在Appdelegate中didFinis...

  • 苹果内购的坑

    苹果的凭证查询的数据如果是消耗性只能查询一次,如果是订阅型也会有时效性,订阅型如果是同一组会保留一个原始订单号(第...

  • ios-内购(IAP)

    StoreKit 框架内购:在ios中专指苹果内购,在app内购买商品时使用苹果的支付方式进行购买;如果在app内...

  • 苹果内购In-app purchase

    关于苹果内购(IAP)的一些问题以及那些坑: 最近在研究苹果内购功能,所以,在网上找了一些资料,进行学习。但是,内...

  • iOS 苹果内购

    公司性质乃是少儿英语在线教育培训。app内需要上课。因此苹果规定需要集成内部购买功能,做之前一直不了解怎么形成一个...

  • iOS 苹果内购遇到的那些坑

    1、首先需要在 https://itunesconnect.apple.com 创建一个APP,如果还没有在 ht...

  • iOS 内购的实现

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

  • 苹果内购踩的坑

    内购官方文档 https://developer.apple.com/library/archive/docume...

网友评论

    本文标题:iOS 苹果内购的血与泪(坑爹的苹果)

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