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

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];
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,095评论 4 62
  • 此生只为诗词度, 唯有清心吾成全; 不与世事洞明柬, 只愿你有尽贤言。 ~ 送给 中国古典文学院士, 著...
    秋叶的味道阅读 414评论 6 6
  • 丹江一幼 孙亚婷 第一天我们迎来了张时慈老师的"音乐课程模式"张老师她用形象生动的语言和精美的课件为我们带...
    Syt_0de4阅读 230评论 0 0
  • 一.获取字符串中:a.所有的正整数b.所有的负整数c.所有的浮点数d.所有的非负浮点数 二、不定项选择题 能够完全...
    跟我念一遍阅读 1,162评论 0 2
  • 【学号】2017101446 【姓名】张莹 【性别】女 【城市】云南楚雄 【简书号】zy08081422 【擅长】...
    17数446张莹阅读 414评论 0 0