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状态吗。测试验证通过的用户名,和充值金额最好用数据库记录下来,方便公司资金核对。

ios 应用内支付(In-App Purchase,沙盒测试,后台验证)相关推荐

  1. [iOS]应用内支付(内购)的个人开发过程及坑!

    本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统. 一. 创建测试App 首先你需要登录 App的ItunesConnection,你会看到如下界面 简 ...

  2. iOS-内购注意 沙盒二次验证

    1.发送请求,注意请求ID NSString *productID = @"这是创建内购项目填写的ID"; NSArray *product = [[NSArray alloc] ...

  3. iOS开发 - 内购沙盒测试正常,因产品返回数为0被苹果审核多次拒绝 (In-app purchase products return 0)

    最近给app增加内购移除广告功能,刚开始还比较顺利,把网上找到的内购代码,简单修改加入到app内,基本能用,测试完成后,提交审核.第一次提交,有点问题被拒,根据具体问题进行改正后,再提交,审核就通过了 ...

  4. Java接入苹果支付 – IAP支付 – IOS应用内支付- 完整版

    本页目录 IAP介绍 IAP参考连接 接入IAP快速导航: 准备工作 认证协议(签订银行信息) 设定商品价格 productId 上线设置 注册自己的沙箱账号: 添加沙箱账号 Java编码 大致编码如 ...

  5. iOS 应用内购买(In-App Purchase)之开发

    iOS 应用内购买(In-App Purchase)之协议.税务和银行业务 使用IAP之前,需要签订协议,查看上面的链接. IAP开发 添加App内购项目 登录 iTunes Connect ,选择我 ...

  6. iOS应用内支付(IAP)的注意事项

    来源:http://blog.csdn.net/xinruiios/article/details/9289573 IAP的全称是In-App Purchase,应用内付费.这种业务模式允许用户免费下 ...

  7. [App Store Connect帮助]二、 添加、编辑和删除用户(5)创建一个沙盒测试员帐户...

    如果您的 App 使用了 App 内购买项目或 Apple Pay,您可以在 App Store Connect 中创建沙盒测试员帐户,以便您向用户提供该 App 前,可以使用该帐户在测试环境中运行您 ...

  8. ios沙盒测试无法连接到Appstore

    ios沙盒测试无法连接到Appstore 沙盒测试的时候一直报以下错误: 2018-10-15 21:56:57.823099+0800 iOS[5989:1703401] 读取本地苹果订单 -- 2 ...

  9. Ios11 IphoneX 内购沙盒测试 无限弹登录框问题解决

    最近升级了iOS11的,经常遇到内购沙盒测试的时候,发现输入沙盒账号密码之后,居然又弹出了登录框,之后便是循环弹登录框,刚开始还以为是代码问题,后来发现了是ios11升级后沙盒测试的问题. 下面有几种 ...

最新文章

  1. Python八种数据导入方法,你掌握了吗?
  2. [转] C# TextBox、DataGrideView中的数据绑定
  3. Browser-Bookmark-Codeing
  4. 红橙Darren视频笔记 状态栏设置颜色 获取高度 设置全屏
  5. 【干货】推荐系统解构.pdf(附下载链接)
  6. fonts.googleapis.com加载过慢导致的项目启动过慢的问题
  7. android修改用户名和密码错误,Android应用开发Android Studio 修改用户名、密码、URL等操作教程...
  8. 三星玄龙骑士与你一同欢度JDG俱乐部四周年,让游戏体验升级
  9. 在线扒站复活版可预览网站html源码
  10. sncr脱硝技术流程图_脱硝技术介绍(SCR和SNCR)
  11. 计算机组成原理课程设计-logisim仿真补码一位乘
  12. 中科院信工所经验_信工所六室面试经历
  13. oracle官网数据库使用迅雷下载方法
  14. cranberry病毒导致mycat应用都崩溃解决办法
  15. 关于小G蛋白活化检测试剂盒
  16. 网络连通性测试ping和tracert命令
  17. python如何计算环比增长率
  18. Markdown编写表格模板
  19. 【第二部分 | CSS】2:点缀一下html元素
  20. 3DS MAX插件制作绚丽的拖光效果-3D建模教程

热门文章

  1. 关于bat和sh后缀的文件
  2. 这5款黑科技手机APP,瞬间提高手机逼格!
  3. 多云和混合云场景下的 API 管理:挑战与选择
  4. css圆角边框(css圆角边框无效)
  5. 【FastText——总结笔记】
  6. swagger2maven依赖_swagger2技术
  7. 基于flowable的开源OA
  8. 微信域名防封之微信域名检测
  9. 分享八个我常用的资源网站
  10. mysql连接出现(1040, ‘ny connections‘)