美文网首页程序员收录iOS支付技术
iOS开发-2017苹果内购最新教程

iOS开发-2017苹果内购最新教程

作者: 看我的大白眼 | 来源:发表于2017-02-07 16:52 被阅读6337次

    公司项目中有虚拟产品,所以要使用苹果内购.自此记录一下苹果内购的流程.前提是已有开发者账号

    协议,税务和银行业务

    • 协议,税务和银行业务


      协议,税务和银行业务
      申请合同
    • 这里提示地址太长,按照要求重新填写一下就可以

    添加联系人
    • 同意协议并提交
    • 设置相关信息


      • 联系人信息

    添加相关联系人信息之后,注意在role(角色)中,为联系人选择身份(高级管理人员 ,金融,技术,法律,营销),点击Done保存

    联系人
    • 银行信息

      1. 添加银行信息
      添加银行

      2.选择国家

      选择国家

      3.填写CNAPS

      CNAPS 银行网点联行号.我们可以直接到银行客服电话查询 CNAPS, 快速便捷.还可以通过下图的Look up CNAPS Code 查询. 不过有的查询不到

      CNAPS
      Look up CNAPS Code

    4.确认银行信息

    银行信息

    5.填写公司银行账号信息

    公司银行信息
    • 税务信息

    税务信息有三个选项:美国税务、澳大利亚税务、加拿大税务。我们在这里选择美国税务就可以

    税务信息

    这里有两个问题:如下图.我选择的都是NO


    填写税务相关信息

    9b.Foreign TIN 是填写公司税务识别码.公司营业执照上同意社会信用代码去掉前两位和最后一位就是公司税务识别码(15位)

    点击提交之后会让你确认信息,确认无误后再次点击提交(提交后无法修改)

    创建测试APP

    新建APP在这里就不详细描述了

    添加内购

    • 选择功能,我们添加一个app内购项目


      添加内购
    • 根据项目需求,选择内购项目


      消耗性项目
    • 填写内购项目的相关信息

    1. 商品名称根据你的消费道具的实际意义来说明
    2. 产品ID是比较重要的,只要唯一即可,在实际应用中,一定要认真填写。
    3. 选择价格
    • 本地化版本

    将在 App Store 上可见的 App 内购买项目名称。可以参考App Store中其他应用的内购项目描述
    描述不得少于10个字符.没有重新截图

    本地化
    • 审核信息

    1.只会在审核中使用屏幕快照,不会将其显示在 App Store 上。屏幕快照必须具有适合您 App 平台的有效尺寸。
    2.能够有助于我们进行审核的关于您的 App 内购买项目的其他信息,如测试帐户(包括用户名、密码等)。审核备注不得超过 4000 个字符。

    审核信息
    • 内购项目则添加完成

    申请沙盒测试账号(用来测试购买项目)

    我们不必用真实的人民币测试购买内购项目

    • 首先我们回到iTunes Connect中,在这里我们选择用户和职能。
    • 添加沙箱技术测试员


      测试人员
    • 添加测试员详细信息

    所有信息都可以随意填写,不用管是否真实。但是App Store地区必须对应
    注意: 邮件地址不能填写与 Apple ID 关联的, 密码保护问题必须在 6 到 35 个字符之间。密码必须包含至少一个大写字母。

    信息

    所有准备工作都已完成.打开项目开始撸代码

    在项目中实现购买

    首先在项目工程中加入storekit.framework,加入头文件#import <StoreKit/StoreKit.h>
    遵守代理SKPaymentTransactionObserver,SKProductsRequestDelegate

     //添加一个交易队列观察者
      [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
                
     //self.productIds是在开发者平台填写的产品id
     self.productId = @"701";
     if ([SKPaymentQueue canMakePayments]) {
           [self requestProductData:self.productId];
      }else{
            DLog(@"不允许程序内付费");
      }
    
    • 去苹果服务器请求产品信息
    // 去苹果服务器请求产品信息
    - (void)requestProductData:(NSString *)productId {
        
        
        [SVProgressHUD show];
        
        NSArray *productArr = [[NSArray alloc]initWithObjects:productId, nil];
        
        NSSet *productSet = [NSSet setWithArray:productArr];
        
        SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
        
        request.delegate = self;
        [request start];
        
    }
    
    • SKProductsRequestDelegate
    // 收到产品返回信息
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
        
        
        NSArray *productArr = response.products;
        
        if ([productArr count] == 0) {
            [SVProgressHUD dismiss];
            DLog(@"没有该商品");
            return;
        }
        
        DLog(@"productId = %@",response.invalidProductIdentifiers);
        DLog(@"产品付费数量 = %zd",productArr.count);
        
        SKProduct *p = nil;
        
        for (SKProduct *pro in productArr) {
            DLog(@"description:%@",[pro description]);
            DLog(@"localizedTitle:%@",[pro localizedTitle]);
            DLog(@"localizedDescription:%@",[pro localizedDescription]);
            DLog(@"price:%@",[pro price]);
            DLog(@"productIdentifier:%@",[pro productIdentifier]);
            if ([pro.productIdentifier isEqualToString:self.productId]) {
                p = pro;
            }
        }
        
        SKPayment *payment = [SKPayment paymentWithProduct:p];
        
        //发送内购请求
        [[SKPaymentQueue defaultQueue] addPayment:payment];
        
    }
    
    
    • SKRequestDelegate
    - (void)requestDidFinish:(SKRequest *)request {
        [SVProgressHUD dismiss];
    }
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
        [SVProgressHUD showErrorWithStatus:@"支付失败"];
    }
    
    
    • SKPaymentTransactionObserver监听购买结果
    // 监听购买结果
    
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
    
        for (SKPaymentTransaction *tran in transactions) {
            switch (tran.transactionState) {
                case SKPaymentTransactionStatePurchased: //交易完成
                    // 发送到苹果服务器验证凭证
                    [self verifyPurchaseWithPaymentTrasaction];
                    [[SKPaymentQueue defaultQueue]finishTransaction:tran];
                    break;
                case SKPaymentTransactionStatePurchasing: //商品添加进列表
    
                    break;
                case SKPaymentTransactionStateRestored: //购买过
                    // 发送到苹果服务器验证凭证
                    
                    [[SKPaymentQueue defaultQueue]finishTransaction:tran];
                    break;
                case SKPaymentTransactionStateFailed: //交易失败
                    
                    [[SKPaymentQueue defaultQueue]finishTransaction:tran];
                    [SVProgressHUD showErrorWithStatus:@"购买失败"];
                    break;
    
                default:
                    break;
            }
        }
    }
    
    • 发送到苹果服务器验证凭证
    
    //沙盒测试环境验证
    #define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
    //正式环境验证
    #define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
    // 验证购买
    - (void)verifyPurchaseWithPaymentTrasaction {
        
        // 验证凭据,获取到苹果返回的交易凭据
        // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
        NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
        // 从沙盒中获取到购买凭据
        NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
        // 发送网络POST请求,对购买凭据进行验证
        //测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
        //正式验证地址:https://buy.itunes.apple.com/verifyReceipt
        NSURL *url = [NSURL URLWithString:SANDBOX];
        NSMutableURLRequest *urlRequest =
        [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
        urlRequest.HTTPMethod = @"POST";
        NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
        NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
        urlRequest.HTTPBody = payloadData;
        // 提交验证请求,并获得官方的验证JSON结果 iOS9后更改了另外的一个方法
        NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
        // 官方验证结果为空
        if (result == nil) {
            NSLog(@"验证失败");
            return;
        }
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
        if (dict != nil) {
            // 比对字典中以下信息基本上可以保证数据安全
            // bundle_id , application_version , product_id , transaction_id
            NSLog(@"验证成功!购买的商品是:%@", @"_productName");
        }
    
    }
    
    • 移动观察者
    - (void)dealloc {
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    }
    
    

    测试内购

    必须是没有越狱的真机
    测试之前现在iPhone上的Apple id 注销

    • 使用现有Apple id

    使用在沙箱测试员中添加的账号即可

    账号
    • 购买


      购买
    • 完成购买


      完成

    官方文档

    • 2017.03.15更新

    APP Store首发以为会卡在内购上,但是顺利的审核过了,但是版本更新的时候,审核员如何做内购测试呢? 首先你要了解xcode运行的APP只能用沙盒测试账号进行购买,不能使用真实的Apple id. 而你在应用商店里下载的只能使用真实的Apple id.

    解决方案:

    进行二次验证:测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的时候需要判断返回Status Code决定是否去沙盒进行二次验证,为了线上用户的使用,验证的顺序肯定是先验证正式环境,此时若返回值为21007,就需要去沙盒二次验证,因为此购买的是在沙盒进行的

    注意:

    验证操作让后台去验证

    丢单处理详看下篇博客

    iOS开发-内购丢单处理方式

    相关文章

      网友评论

      • 硅谷小虾米:同一个开发者账号下已经有一个应用创建过内购项目了(应用也上线了),现在又新建了一个项目中还要用到内购的话是直接创建内购项目吗?
      • dushoupeng:设置银行卡,账户名一定是英文,这个怎么弄?
        看我的大白眼:@dushoupeng 是的,我们公司就单独开了一个外汇账号
        dushoupeng:@看我的大白眼 公司没有英文名。。。是要去开一个外汇交易账号么?银行的说现有账户不能添加英文名
        看我的大白眼:只要开户的公司英文名和填写的一直就可以
      • c4b5db2c94af:你好 在线等~ 新版内购的样式怎么弄得? iOS11之后的弹窗
        看我的大白眼:ios11正常使用,没有单独适配
      • 剪刀_石头_布:请问一下这个测试验证地址和正式验证地是怎么回事
      • 敢去做:你好 , 请问内购测试成功后,这些测试代码可以删除吗?苹果审核的时候又是以什么方式来测试内购的呢?谢谢!
        看我的大白眼:@HAPPY小元宝 在备注信息里填写上就行
        HAPPY小元宝:@看我的大白眼 你好,请问在哪块给他提供沙盒账号呢?
        看我的大白眼:@敢去做 测试代码是指?测试商品吗?审核的时候要提供沙盒账号的
      • 流年啊奈我何:模拟器能测试内购吗?
      • a浮生若梦a:- (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response 。这个 NSArray *product = response.products;里面的product一直为空,获取不到产品信息 是怎么回事? 还有就是填写好税务,银行之后,审核需要多久?
      • 0bd0f50638f4:您好 我也是山东聊城的 近期也要做一个用到苹果内购的项目 你能加下我QQ吗 有个问题要问您 419282477 或者我加您也行
      • Ko_Neko:请问下 ,如果是个人app,税务这里要怎么填写呢?
      • LD_左岸:公司银行信息哪里 银行账号填的是公司的对公账号 账号的持有这是 公司中文全称 但是不可能填中文
        在这个地方我看有的博客说填公司全称的汉语拼音
        大神怎吗填的?
        LD_左岸:@看我的大白眼公司对公账户上写的名称是中文 如北京大神科技发展有限公司 昨天填的时候我填的汉语拼音 bei jing da shen ke ji you xian gong si 今天和苹果客服确认了一下 他们要英文名称 我就填了公司申请邓白氏码时的英文名称!但是这个英文名称和对公账号不对应啊 我和财务说了 财务让试试
        看我的大白眼:@看我的大白眼 你们公司的对公账户支持外汇吗? 这个地方得注意! 或者提前咨询一下银行! 我开始就填写错了,导致银行不给打款!
        看我的大白眼:你是说的第5点的公司名称吗? 这个地方只要和公司对公账户上写的公司名称一直就可以!
      • LD_左岸:大神 我看填写税务相关信息 那张图上 您用红框标注出来了9b.Foreign TIN 这一项我填的公司的纳税人识别号是不是对的?

        第二就是 银行账户 我填的是 公司的对公账户 账户持有人我填的是公司的全称的汉语拼音 比如 北京大神科技发展有限公司 我填的就是 bei jing da shen ke ji fa zhan you xian gong si
        打电话给苹果客服了 客服不给说 说 必须得代理人打电话才给说.
      • 不抄完整本三国不改名:帮了我很大忙,多谢:blush:
      • LD_左岸:大神您好,申请合同哪--->我点了Request之后 弹窗提示我:Add New Legal Entity
        Address line 1 cannot be more than 40 characters


        这个是什么意思
        Address Line 1 这一行 这不报超过40个字符了 而且这一行里填写的地址 也是自动生成的 应该和我那个申请证书地址一样 我现在把这一行的东西 分成两部分 在Address Line 1这一行里填 城市名 在Address Line 2这一行里填具体的街道 是否可行
        LD_左岸:大神内购收到的钱是 大于多少钱就打给我们财务 还是这个月的钱到下个月打给我们财务
        我在 付款和财务报告 里看到 有两笔销量 估计总收入是100 CNY 但是我们并没收到钱..
        LD_左岸:@看我的大白眼 谢指点:smile:
        看我的大白眼:地址删除一部分就行 留关键信息
      • 井空烀地瓜:您好 楼主 您说的验证操作让后台去验证, 这步是怎么做
      • cy9Lyz:14.a 不用勾吗?
      • 牧童s:前面流程正常,代理监听结果返回failed,里面内容是 '无法连接到iTuenes Store', 但是确认购买框也弹出来了,无法交易成功,怎么破
        牧童s:@看我的大白眼 恩恩,打电话给苹果,说是他们的问题,正在修复。
        看我的大白眼:还有就是这几天内购很不稳定,你试试其他APP的内购,确定一下是不是苹果的问题
        看我的大白眼:是不是用的不是沙盒账号呀
      • 我有一头小毛驴从来都不骑:你好,我想问一下,我的ProcutId是数组,我应该怎么用你的方法呢?
        我有一头小毛驴从来都不骑:@看我的大白眼 那列表的话也不能用str接收啊,看你代码是只接收一个productid的
        看我的大白眼:我们的内购列表是在网络上请求下来的
        看我的大白眼:每一个内购产品都绑定一个唯一的procutId, 你所说的ProcutId是数组,应该是写成这样的形式[@"procutId1",@"procutId2"],我在APP里是通过我们接口把数据拿下来的
      • 张芳涛:再问一个问题,有时候设置了代理方法之后就会自动的走购买结果监听方法,这样就会导致报错,,请问您遇到过么?
        看我的大白眼:@张芳涛 加个qq聊聊吧 957430432
        张芳涛:就在当前控制器,没有界面跳转
        看我的大白眼:你看看这个控制器销毁了吗?是不是没有移除掉监听. 我现在也正在和后台对接这一块.确实发现了你说的这问题
      • 张芳涛:按照这个方法,支付结果监听走了两次。
        张芳涛:对,就是这个,你的意思是在第一个结果的时候不做处理是么?
        看我的大白眼:你是说这个方法- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions走了两次 这个的确是走两次的,第一次的状态是SKPaymentTransactionStatePurchasing(商品添加进列表),购买之后是再次走一次进行判断交易完成还是交易失败
      • 张芳涛:支付完成之后的弹框(设置完成,您已成功购买)是必须要显示的么?可以下个办法去掉么?
        张芳涛:@看我的大白眼 谢谢了
        看我的大白眼:@张芳涛 这是用沙盒测试的!正式环境可能没有吧!项目还没有上线,暂未测试!只是猜测
      • 十一岁的加重:感谢有技术的人分享好文章啊
      • gwk_iOS:基本差不多 前两天正好用过 填税表个银行 弄了我好久
        看我的大白眼:@gwk_ios 银行这里我也是走了很多弯路
      • 超_iOS:添加法律实体这个总是提示: An account corresponding to your name/address and/or Taxpayer Identification Number has already been created. If you believe you are reaching this message in error, you may Contact Us.
        看我的大白眼:@_超 http://www.jianshu.com/p/0e7ad25d1fb7 你看一下这个
        超_iOS:@看我的大白眼 这是填完信息后的错误提示
        看我的大白眼:这是给你发的邮件么

      本文标题:iOS开发-2017苹果内购最新教程

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