iOS应用内付费(IAP)开发步骤

iOS应用内付费(IAP)开发步骤

1.苹果iTunes Connect内购产品信息录入。

1)创建app内购买项目(Create New),选择类型:

1.消耗型项目

对于消耗型 App 内购买项目,用户每次下载时都必须进行购买。一次性服务通常属于消耗型项目,例如钓鱼App 中的鱼饵。

2.非消耗型项目

对于非消耗型 App 内购买项目,用户仅需要购买一次。不会过期或随使用而减少的服务通常为非消耗型项目,例如游戏App 的新跑道。

3.自动续订订阅

通过自动续订订阅,用户可以购买指定时间期限内的更新和动态内容。除非用户取消选择,否则订阅(例如杂志订阅等)会自动续订。

4.免费订阅

通过免费订阅,开发者可以将免费订阅内容放入“报刊杂志”。用户注册免费订阅后,该订阅内容将会出现在与该用户Apple ID 关联的所有设备上。请注意,免费订阅不会过期,并且仅在支持报刊杂志功能的 App 中提供。

5.非续订订阅

非续订订阅允许有时限性的营销服务。对于 App 内购买项目中的限时访问内容,就需使用非续订订阅。例如,导航App 中语音导航功能的一周订阅,或者年度订阅已存档的视频或音频的在线目录。

一定要根据自己应用的情况选择正确,不然会被App Store审核团队拒绝。应用内的虚拟币要采用消耗型的,有固定时限的会员选择自动续订订阅。也可以只选择虚拟币充值自己后台购买的情况解决会员问题。

2)生成共享密钥

共享密钥是在您联系我们的服务器获取 App 内购买项目收据时使用的唯一代码。没有共享密钥,您将无法在沙箱技术模式下测试自动续订 App 内购买项目。另外,共享密钥不能在 App Store 使用。

注:无论与哪个 App 相关联,您的所有自动续订订阅都将使用同一共享密钥。

此共享密钥用于后台服务器验证用户购买项目的凭证,生成新密要服务器也立即改变验证密钥。共享密钥在验证自动续订订阅类型项目的时候必须需要。

3)内购项目的状态

A) Pending Developer Approval – Your in apppurchase has been created but has not been tested in a sandbox environment andapproved by you.

B) Approved By Developer – Your in apppurchase has been tested in a sandbox environment and has been approved by you.

C) Waiting For Review – You have submittedyour in app purchase to be reviewed by Apple.

D) In Review – Your in app purchase iscurrently being reviewed by Apple no edits can be made.

E) Developer Action Required – In app purchasedetail changes that you submitted have been rejected. You are required to takeaction to edit the detail information or cancel the request to change thedetail information before this in app purchase can be reviewed again.(内购项目详情界面会提示那个地方出现了问题,稍微修改一下再次提交就行了)

F) Ready for Sale – Apple has approved your inapp purchase to go live on the App Store with its associated application. Thein app purchase must be cleared for sale in iTunes Connect to be Ready forSale.

G) Rejected – Apple has rejected your in apppurchase during the review process. If you have not already been contacted byApple with more information about your rejection, you may inquire through theContact Us section of iTunes Connect. A rejected in app purchase cannot bereinstated. You must create a new in app purchase if you still wish for it tobe sold.

H) Developer Removed from Sale – You havemarked your in app purchase as not cleared for sale in iTunes Connect.

2.app端程序代码编写(代码仅供参考)

#pragma mark - 支付以单利的形式展开

+(PurchasesObject*)SharePurchases

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

if (_purchase == nil) {

_purchase = [[super alloc]init];

[[SKPaymentQueue defaultQueue]addTransactionObserver:_purchase];

}

});

return _purchase;

}

+(id)allocWithZone:(struct _NSZone *)zone

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

if (_purchase == nil) {

_purchase = [[super allocWithZone:zone]init];

}

});

return _purchase;

}

+(id)alloc

{

return _purchase;

}

#pragma mark -支付钻石会员

_alter = [[UIAlertView alloc]initWithTitle:@游客模式购买仅限当前设备使用所购买的权限,推荐您登录购买 message:nil delegate:self cancelButtonTitle:@取消 otherButtonTitles:@登陆购买(推荐),@游客模式购买 ,nil];

//游客购买很重要,会被AppStore审核团队拒绝。

#pragma mark -开始支付,根据录入内购项目的产品id去AppStore请求产品信息。

if ([SKPaymentQueue canMakePayments]) {

NSSet * set = [NSSet setWithArray:@[ProductID]];

SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];

request.delegate = self;

[request start];

}

else

{

NSLog(@无权限购买);

}

#pragma mark -SKProductsRequestDelegate 获取appstroe产品信息

- (void)productsRequest:(SKProductsRequest *)requestdidReceiveResponse:(SKProductsResponse *)response {

self.mySelfView.userInteractionEnabled = NO;

[AFTools showHUD:@获取产品信息 atView:self.mySelfView];

NSLog(@-----------收到产品反馈信息--------------);

NSArray *myProduct = response.products;

if (myProduct.count == 0) {

[AFTools alertWithTitle:@购买失败 message:@无法获取产品信息];

NSLog(@无法获取产品信息,购买失败。);

return;

}

NSLog(@产品products==%@,myProduct);

NSLog(@产品id==%@,response.invalidProductIdentifiers);

NSLog(@产品数量==========%lu,(unsigned long)myProduct.count);

for(SKProduct *product in myProduct){

// SKMutablePayment

NSLog(@SKProduct描述信息%@, [product description]);

NSLog(@产品标题 %@ , product.localizedTitle);

NSLog(@产品描述信息: %@ , product.localizedDescription);

NSLog(@价格: %@ , product.price);

NSLog(@Product id: %@ , product.productIdentifier);

SKMutablePayment *MpayMent = [SKMutablePayment paymentWithProduct:product];

NSLog(@===%@,MpayMent.requestData);

if ([[SKPaymentQueue defaultQueue]respondsToSelector:@selector(addPayment:)]){

[[SKPaymentQueue defaultQueue] addPayment:MpayMent];

}

else

{

}

}

}

#pragmamark-SKPaymentTransactionObserver支付结果

- (void)paymentQueue:(SKPaymentQueue *)queueupdatedTransactions:(NSArray *)transactions

{

for (SKPaymentTransaction *transaction in transactions)

{

switch (transaction.transactionState)

{

case SKPaymentTransactionStatePurchased://交易完成

NSLog(@交易完成transactionIdentifier= %@, transaction.transactionIdentifier);

[self completeTransaction:transaction];

break;

case SKPaymentTransactionStateFailed://交易失败

[self failedTransaction:transaction];

NSLog(@交易失败);

break;

case SKPaymentTransactionStateRestored://已经购买过该商品

[self restoreTransaction:transaction];

NSLog(@已买过商品);

break;

case SKPaymentTransactionStatePurchasing://商品添加进列表

NSLog(@商品添加进列表);

break;

default:

break;

}

}

}

- (void)paymentQueue:(SKPaymentQueue *)queueremovedTransactions:(NSArray *)transactions

{

NSLog(@---------------移除-------------);

}

- (void)paymentQueue:(SKPaymentQueue *)queuerestoreCompletedTransactionsFailedWithError:(NSError *)error

{

NSLog(@---------------重复支付失败-------------);

}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {

NSLog(@-------------------支付完成--------------------);

[self commitSeversSucceeWithTransaction:transaction];

}

-(void)restoreTransaction: (SKPaymentTransaction *)transaction

{

NSLog(@----------------重复支付-----------------);

[self commitSeversSucceeWithTransaction:transaction];

}

- (void)commitSeversSucceeWithTransaction:(SKPaymentTransaction *)transaction

{

NSString * productIdentifier = transaction.payment.productIdentifier;

NSString *transactionReceiptString= nil;

//系统IOS7.0以上获取支付验证凭证的方式应该改变,切验证返回的数据结构也不一样了。

if(IOSSystemVersion>=7.0)

{

NSURLRequest*appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];

NSError *error = nil;

NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequestreturningResponse:nil error:&error];

transactionReceiptString = [receiptDatabase64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

}

else

{

NSData * receiptData = transaction.transactionReceipt;

transactionReceiptString = [receiptDatabase64EncodedString];

}

// 向自己的服务器验证购买凭证

}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

最好在客户端上键一个数据库,跟踪订单的状态,防止用户订单在某个环节出现问题时无法寻找到订单进行二次处理。

去AppStore请求数据时有时候会出现错误,你可以iTunes connect里的connect us去给他们写邮件反馈问题。但是大部分时间你等等就能解决了,对就是什么也不做等着。也许那一天他就好了。

3.后台服务器验证

IOS 内支付有两种模式:

1) 内置模式

2) 服务器模式

内置模式的流程可以简单的总结为以下几步:

1) app从app store 获取产品信息

2) 用户选择需要购买的产品

3) app发送支付请求到app store

4) app store 处理支付请求,并返回transaction信息

5) app将购买的内容展示给用户

服务器模式的主要流程如下所示:

1) app从服务器获取产品标识列表

2) app从app store 获取产品信息

3) 用户选择需要购买的产品

4) app 发送 支付请求到app store

5) app store 处理支付请求,返回transaction信息

6) app 将transaction receipt 发送到服务器

7) 服务器收到收据后发送到app stroe验证收据的有效性

8) app store 返回收据的验证结果

9) 根据app store 返回的结果决定用户是否购买成功

上述两种模式的不同之处主要在于:交易的收据验证,内建模式没有专门去验证交易收据,而服务器模式会使用独立的服务器去验证交易收据。内建模式简单快捷,但容易被破解。服务器模式流程相对复杂,但相对安全。

开发之初,苹果方就很负责的告知:我们的服务器不稳定。真正开发之后,发现苹果方果然是很负责的,不仅是不稳定,而且足够慢。app store server验证一个收据需要3-6s时间。

1.用户能否忍受3-6s的等待时间

2.如果app store server 宕机,如何确保成功付费的用户能够得到正常服务。

对于第一个问题,我们有理由相信用户完全无法忍受,所以采用异步验证的方式,服务器收到客户端的请求后,就将请求放到MCQ中去处理。

对于第二个问题,由于苹果人员很负责人的告知:我们的服务器不稳定,所以不排除收据验证超时的情况。对于验证超时的收据,保存到数据库中并标记为验证超时,定时任务每隔一定的时间去app store验证,确保能够获取收据的验证结果。

在开发过程中,需要测试应用是否能够正常的进行支付,但是又不能进行实际的支付,因此需要使用苹果提供的sandbox Store测试。Store Kit不能在iOS模拟器中使用,测试Store必须在真机上进行。

在sandbox中验证receipt

https://sandbox.itunes.apple.com/verifyReceipt

在生产环境中验证receipt

https://buy.itunes.apple.com/verifyReceipt

在实际开发过程中,服务器端通过issandbox字段标识客户端传递的收据是沙盒环境中的收据还是生产环境中的收据。在提交苹果审核前,沙盒测试均无问题。提交苹果审核后,被告知购买失败,审核未通过。通过查询日志发现,客户端发送的交易收据为沙盒收据,但是issandbox字段却标识为生产环境。

结论:苹果审核app时,仍然在沙盒环境下测试。但是客户端同事在app提交苹果审核时,将issandbox字段写死,设置为生产环境。这样就导致沙盒收据发送到https://buy.itunes.apple.com/verifyReceipt去验证。

那么如何自动的识别收据是否是sandbox receipt呢?

识别沙盒环境下收据的方法有两种:

1.根据收据字段 environment = sandbox。

2.根据收据验证接口返回的状态码

如果status=21007,则表示当前的收据为沙盒环境下收据, t进行验证。

苹果反馈的状态码;

21000App Store无法读取你提供的JSON数据

21002 收据数据不符合格式

21003 收据无法被验证

21004 你提供的共享密钥和账户的共享密钥不一致

21005 收据服务器当前不可用

21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中

21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证

21008 收据信息是产品环境中使用,但却被发送到测试环境中验证

先生产验证后测试验证,可以避免来回切换接口的麻烦。测试验证只要用你自己申请的测试appid的时候才会用到,用户不会拥有测试appid,所以不会走到测试验证这一步。即使生产验证出错,应该也不回返回21007状态吗。测试验证通过的用户名,和充值金额最好用数据库记录下来,方便公司资金核对。

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

推荐阅读更多精彩内容

  • 在我们应用开发中我们经常在自己的项目中使用到支付,下面我们来谈谈iOS这块的支付;iOS支付主要分为两类,第三方支...
    Hither阅读 8,234评论 9 42
  • App Store 审核指南2017/07/27 简介 App 正在改变世界,丰富人们的生活,并为像您一样的开发者...
    小白沐春风阅读 3,546评论 0 2
  • 服务器模式: 使用这种方式,要提供另外的服务器将产品发送给程序。 服务器交付适用于订阅、内容类商品和服务,因为商品...
    水晶兰阅读 1,567评论 1 3
  • 简介 App 正在改变世界,丰富人们的生活,并为像您一样的开发者提供前所未有的创新机会。因此,App Store ...
    o0_0o阅读 3,311评论 2 48
  • 【作者】王红雨 【导师】王玉印 袁文魁 【分舵】天地会 【舵主】王雅叶 【导图解说】《海洋奇缘》观感 动画片中的精...
    王红雨阅读 769评论 7 1