iOS内购遇到问题记录(未完待续)

网上有很多内购教程,推荐几篇比较写的比较好的
文章一: iOS内购(IAP,In App Purchases-在APP内部支付),设置及使用
文章二: iOS开发内购全套图文教程,这篇文章比较老,但是下面的评论可以看看,很有价值
文章三: iOS 内购2017.4

1、内购集成成功,但是请求到的商品数是0
// 获取商品的查询结果
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

打印商品的数量response.products.count结果一直是零,打印response.invalidProductIdentifiers发现商品是无效状态
解决方法:

可能出现的商品列表为0的情况:

  • 1.银行信息未填写完整。
  • 2.Bundle ID 不统一, bundleID要与iTunes Connect上你App的相同,不然是请求不到产品信息的
2、购买消耗性产品后, 再次购买会提示您已购买此APP内购项目,此项目将免费恢复
再次购买曾购买过的商品

解决方法:

监听购买结果,当失败和成功时代码中要调用如下代码:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
该方法通知苹果支付队列该交易已完成,不然就会已发起相同 ID 的商品购买就会有此项目将免费恢复的提示。

3、每次重新进入选购商品页面,都会提示之前购买商品后的成功或者失败提示即再次运行-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions这个方法

解决方法:

在购买成功或者失败后,没有调用[[SKPaymentQueue defaultQueue] finishTransaction:transaction];告诉苹果支付队列,改交易已经完成,
在购买成功或者失败后,加上这句代码

4、注意事项

在沙盒环境下真机测试内购时,请去app store中注销你的apple ID,不然发起支付购买请求后会直接case:SKPaymentTransactionStateFailed。使用沙盒测试员的账号时不需要真正花钱的。

5、校验票据

票据的校验是保证内购安全完成的非常关键的一步,一般有三种方式:
1、服务器验证,获取票据信息后上传至信任的服务器,由服务器完成与App Store的验证(提倡使用此方法,比较安全)
2、本地票据校验
3、本地App Store请求验证

a)、本地票据校验

票据验证部分部分可参考此文章

本地票据校验的一般步骤
要验证收据,请按顺序执行以下测试:
1.找到收据。
如果没有收据,则验证失败。
2.验证收据是否由Apple 正确签署
如果收据不是由 Apple 签署,则验证失败。
3.验证收据中的Bundle Identifier(数据包标识符)与在Info.plist文件中含有您要的CFBundleIdentifier值的硬编码常量相匹配。
如果两者不匹配,则验证失败。
4.验证收据中的版本标识符字符串与在Info.plist文件中含有您要的CFBundleShortVersionString值(macOS)或
CFBundleVersion 值(iOS)的硬编码常量相匹配。
如果两者不匹配,则验证失败。
5.按照“计算 GUID 的哈希(Hash)(第 8 页)”所述计算GUID 的哈希(Hash)。
如果结果与收据中的哈希(Hash)不匹配,则验证失败。
如果通过所有测试,则验证成功。

注意: Bundle Identifier(数据包标识符)和版本标识符字符串是UTF-8 字符串,而不仅仅是一系列字节。确保您相应地编写比较逻辑代码。

如果您的 App 支持“批量购买计划”,请检查收据的有效日期。

b)、App Store验证:
// 14.交易成功,与服务器比对
// 交易结束,当交易结束后还要去appstore上验证支付信息是否都正确,只有所有都正确后,我们就可以给用户发放我们的虚拟物品了。
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{

    NSString * str=[[NSString alloc]initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
    NSString *environment = [self environmentForReceipt:str]; // 判断当前的环境(正式环境还是测试环境)
    NSLog(@"environment:%@",environment);
    
    // 验证凭据,获取到苹果返回的交易凭据
    // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址,目前苹果公司提倡的获取购买凭证的方法
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 从沙盒中获取到购买的票据信息
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    /**
        BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
        BASE64是可以编码和解码的
        base64位的产品验证码单,base64是服务端和苹果进行校验所必须的,苹果的文档要求凭证经过Base64加密
    */
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *sendString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];

    NSURL *StoreURL=nil;
    // 沙盒状态下使用:https://sandbox.itunes.apple.com/verifyReceipt来验证
    // 生产环境下使用:https://buy.itunes.apple.com/verifyReceipt
    if ([environment isEqualToString:@"environment = Sandbox"]) {
        
        StoreURL= [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];
    }
    else{
        
        StoreURL= [[NSURL alloc] initWithString: @"https://buy.itunes.apple.com/verifyReceipt"];
    }
    
    //这个二进制数据由服务器进行验证;zl
    NSData *postData = [NSData dataWithBytes:[sendString UTF8String] length:[sendString length]];

    NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:StoreURL];
    
    [connectionRequest setHTTPMethod:@"POST"];
    [connectionRequest setTimeoutInterval:50.0];//120.0---50.0zl
    [connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
    [connectionRequest setHTTPBody:postData];

    //开始请求
    NSError *error=nil;
    NSData *responseData=[NSURLConnection sendSynchronousRequest:connectionRequest returningResponse:nil error:&error];
    if (error) {
        NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
        return;
    }
    NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];

    /*
     *  这里可以等待上面请求的数据完成后并且state = 0 验证凭据成功来判断后进入自己服务器逻辑的判断,也可以直接进行服务器逻辑的判断,验证凭据也就是一个安全的问题。楼主这里没有用state = 0 来判断。
     *  完整结束此次在App Store的交易,没有这句代码的调用,下次购买会提示已经购买该商品
     *  [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
     */
    NSLog(@"请求成功后的数据:%@",dic);
    
    NSString *product = transaction.payment.productIdentifier;
    
    NSLog(@"+++ transaction.payment.productIdentifier:%@",product);
    
    if ([product length] > 0)
    {
        NSArray *tt = [product componentsSeparatedByString:@"."];
        
        NSString *bookid = [tt lastObject];
        
        if([bookid length] > 0)
        {
            
            NSLog(@"打印bookid%@",bookid);
            //这里可以做操作吧用户对应的虚拟物品通过自己服务器进行下发操作,或者在这里通过判断得到用户将会得到多少虚拟物品,在后面([self getApplePayDataToServerRequsetWith:transaction];的地方)上传上面自己的服务器。
        }
    }
    //此方法为将这一次操作上传给我本地服务器,记得在上传成功过后一定要记得销毁本次操作。调用[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
//    [self getApplePayDataToServerRequsetWith:transaction];

    
}

-(NSString *)environmentForReceipt:(NSString *)str
{
    str = [str stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    str = [str stringByReplacingOccurrencesOfString: @"" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"\"" withString:@""];
    NSArray *arr = [str componentsSeparatedByString:@";"];
    
    // 存储收据环境的变量
    if (arr.count > 2) {
        NSString *environment = arr[2];
        return environment;
    }
    
    return nil;
  
}

注意:

获取到票据以后我们通过App Store来验证票据是否真实
沙盒状态下使用:https://sandbox.itunes.apple.com/verifyReceipt来验证
生产环境下使用:https://buy.itunes.apple.com/verifyReceipt
附常见的验证状态代码:

状态码

如果此时未获取到票据的信息,使用SKReceiptRefreshRequest来刷新票据结果。

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

推荐阅读更多精彩内容

  • 最近有个项目客户总是反应掉单,于是乎就看了看内购相关的东西,发现坑还真是不少,这里做个总结。 IAP,即in-Ap...
    糖炒0栗子阅读 17,277评论 0 22
  • 自己开发的视频直播项目,牵涉到充值金币,用到了苹果公司的内购,趴坑了两天,这里总结下实现苹果内购。 一. 创建测试...
    Leo丶Dicaprio阅读 3,247评论 8 7
  • iOS应用如果涉及到支付功能,分为两类:第三方支付和苹果内购。那么什么情况下选择使用第三方支付,又在什么情况下选择...
    ZfRee阅读 38,316评论 36 66
  • 每个优秀的人,并不是与生俱来就带着光环,也不一定是比别人幸运。他们只是在任何一件小事上,都对自己有所要求,不因舒适...
    圆圆的兔兔阅读 136评论 0 0
  • 慵懒是罪,却总被无罪释放。 每天醒来,总觉得后背像是与床缝在了一起,感觉腰肢失去了脊梁骨,撑不起疲软的身躯。一个翻...
    景漓脩阅读 214评论 0 0