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

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

协议,税务和银行业务

  • 协议,税务和银行业务


    协议,税务和银行业务

    申请合同
  • 这里提示地址太长,按照要求重新填写一下就可以

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


    • 联系人信息

添加相关联系人信息之后,注意在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开发-内购丢单处理方式

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

推荐阅读更多精彩内容

  • 转载 公司项目中有虚拟产品,所以要使用苹果内购.自此记录一下苹果内购的流程.前提是已有开发者账号 协议,税务和银行...
    George2016阅读 1,250评论 9 6
  • 公司项目中有虚拟产品,所以要使用苹果内购.自此记录一下苹果内购的流程.前提是已有开发者账号 协议,税务和银行业务 ...
    better栋阅读 1,963评论 0 7
  • 公司有个功能叫付费加速制作模型。这属于服务费,之前用的支付宝和微信支付,后来微信通过其官方公众号发出通知,称因为苹...
    charlotte2018阅读 4,024评论 0 9
  • 参考链接iOS开发之内购-AppStore iOS App提交指南(二)-协议、税务和银行业务iOS应用内付费(I...
    sjzLovecj阅读 2,926评论 4 4
  • 你看我沉默不语, 而在心中, 你我已经有了一本书的故事, 只差一个结局, 它藏在你的眼里, 藏在你的微笑里, 藏在...
    猜丁壳你个头啊阅读 178评论 0 0