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

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,则表示当前的收据为沙盒环境下收据,需要调用https://sandbox.itunes.apple.com/verifyReceipt进行验证。

苹果反馈的状态码;

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

21002 收据数据不符合格式

21003 收据无法被验证

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

21005 收据服务器当前不可用

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

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

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

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

参考:

http://www.himigame.com/iphone-cocos2d/550.html

http://www.360doc.com/content/14/1113/15/12282510_424834793.shtml

时间: 2024-08-01 22:38:55

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

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

1.苹果iTunes Connect内购产品信息录入. 1)创建app内购买项目(Create New),选择类型: 1.消耗型项目 对于消耗型 App 内购买项目,用户每次下载时都必须进行购买.一次性服务通常属于消耗型项目,例如钓鱼App 中的鱼饵. 2.非消耗型项目 对于非消耗型 App 内购买项目,用户仅需要购买一次.不会过期或随使用而减少的服务通常为非消耗型项目,例如游戏App 的新跑道. 3.自动续订订阅 通过自动续订订阅,用户可以购买指定时间期限内的更新和动态内容.除非用户取消选择,

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

本文会给大家详细介绍iOS内购,这是本人16年5月底的开发过程,希望对看完此篇文章的人有所帮助. 本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统. 部分地方直接摘自网络,基本上是我的逻辑,省时省心省力. 一. 创建测试App 首先你需要登录 App的ItunesConnection,你会看到如下界面 简单的介绍一下这几个选项 1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等.2.销售和趋势主要是来查看App在各个平台的下载量

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

本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统. 一. 创建测试App 首先你需要登录 App的ItunesConnection,你会看到如下界面 简单的介绍一下这几个选项 1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等. 2.销售和趋势主要是来查看App在各个平台的下载量,收入等方面数据,里面有曲线图等图文结合的方式给我们参考. 3.付款和财务报告显示的是你的收入以及付款等相关信息. 4.iAd主要是跟广告有关,开发者可

pickle结合redis,decode('utf-8','ignore')解码各种bytes ,支付宝沙盒测试

#视图函数层class Login(APIView): authentication_classes = [] permission_classes = [] parser_classes = [JSONParser] def post(self,request): response={'status':100,'msg':None} name=request.data.get('name') pwd=request.data.get('pwd') #去数据库校验该用户是否存在 user=Use

iOS应用内支付

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

[iOS基础控件 - 6.11.5] 沙盒 & 数据存储

A.沙盒 每个APP都有一个沙盒,是独立存在的 1.Xcode5和Xcode6的模拟器文件目录 a.模拟器路径改版 (1)Xcode5中模拟器路径为:/Users/用户名/Library/Application Support/iPhone Simulator (2)Xcode6中模拟器路径为:/Users/用户名/ Library/Developer/CoreSimulator 应用程序包:(上图中的Layer)包含了所有的资源文件和可执行文件 b.沙盒路径改版 (1)Xcode5中沙盒的路径

iOS应用内支付(IAP)服务端端校验详解

IAP流程 IAP流程分为两种: 一种是直接使用Apple的服务器进行购买和验证, 另一种就是自己假设服务器进行验证.由于国内网络连接Apple服务器验证非常慢,而且也为了防止黑客伪造购买凭证,通用做法是自己架设服务器进行验证. 下面我们通过图来看看两种方式的差别: 使用Apple服务器 自己架设服务器 这里重要主要讲讲服务端这一块的校验过程因为涉及到校验合法性. 1.首先拿到客户端的支付receiptData和transaction信息 2.服务端通过receiptData通过向苹果服务器po

应用内购(In-App Purchase)常见问题解答

http://www.cocoachina.com/ios/20150612/12110.html 本文档为您解答应用内购相关的常见问题. 配置(Configuration) 1.我必须上传一个二进制文件来测试应用内购么? 不,测试应用内购(In-App Purchase,以下简称IAP)不需要上传二进制文件. 注:在应用程序准备好接受审核批准之前,不要将开发中的二进制文件上传到iTunes Connect.如果二进制文件出现在iTunes Connect中,但功能不完整,那么App Revie

<转>iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!

原文地址:http://blog.csdn.net/xiaominghimi/article/details/6937097 //——2012-12-11日更新   获取"产品付费数量等于0这个问题"的原因 看到很多童鞋问到,为什么每次都返回数量等于0?? 其实有童鞋已经找到原因了,原因是你在 ItunesConnect 里的 “Contracts, Tax, and Banking”没有完成设置账户信息. 确定 ItunesConnect 里 “Contracts, Tax, and