为什么苹果7 iap支付的时候skpaymentiap transactionidstatefailed

[iOS]应用内支付(内购)的个人开发过程及坑!
[iOS]应用内支付(内购)的个人开发过程及坑!
本文会给大家详细介绍iOS内购,这是本人16年5月底的开发过程,希望对看完此篇文章的人有所帮助。
本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统。
部分地方直接摘自网络,基本上是我的逻辑,省时省心省力。
一. 创建测试App
首先你需要登录 App的ItunesConnection,你会看到如下界面
简单的介绍一下这几个选项
1.我的App主要用于管理自己的App应用,例如编辑资料,上架,下架等。
2.销售和趋势主要是来查看App在各个平台的下载量,收入等方面数据,里面有曲线图等图文结合的方式给我们参考。
3.付款和财务报告显示的是你的收入以及付款等相关信息。
4.iAd主要是跟广告有关,开发者可以登录到Workbench,通过iAd对应用的广告进行控制。
5.用户和职能用于生成相应账号,例如苹果沙河测试账号。
6.协议,税务和银行业务则是你银行相关账户的信息设置。
在这里我们选择第一个选项,我的App, 然后点击左上角的加号,新建一个用来测试用的App。
点新建 App,会出现新建窗口;
在这里有几个需要填写的地方,名称自己取,平台IOS,语言选择了简体中文,套装ID也就是你的Bundle Identifier,需要你在 申请BundleID,SKU可以理解为用户看一看到的唯一标示,会体现在你的app的App Store的链接中。
二.添加内购
App创建好之后,我们打开创建的App,在左上角选择功能,会看到左侧的App 内购买项目。我们点击右下角的加号,为App添加内购项目。
之后我们会看到类型的选项,如下图
官方的注释写的很清楚了,只在这里简单的说下前两种:
- 消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买
- 非消耗型项目 就像你在App Store购买App,买了一次之后就不用再买第二次,你拥有永久使用权。
在我们的app中,是充值会员,所以选择的是第一种,可以无限次购买。
这里有几个选项,需要填写商品名称,产品ID以及价格等级,简单说明一下
1. 商品名称根据你的消费道具的实际意义来说明,比如“100颗宝石”,“100金币”等。
2. 产品ID是比较重要的,由项目自定义,只要唯一即可,因为测试,我在这里随便填写的123,在实际应用中,一定要认真填写。
3. 价格等级的话“查看价格表”中有对应的说明,可以对照着表中每个国家的货币价格与等级来选择
接下来是语言选择,和上传快照如下图
点击添加语言,填写名称和描述,这里我们依然选择简体中文,如下
审核备注,根据实际情况填写,可以不填。而下面的屏幕快照,则是商品图片,以像素为单位,最低尺寸为321,390,尺寸需求如下图,上传即可。
到这里为止, 我们的内购项目则添加完成。接下来则是测试阶段了。
三.申请沙盒测试账号(用来测试购买项目)
这个账号,是利用苹果的沙盒测试环境来模拟AppStore的购买流程,你肯定不会想要用真实RMB去购买测试吧?
首先我们回到iTunes Connect中,在这里我们选择用户和职能。
然后在上面的第三个选项沙箱技术测试员中点击加号,添加测试员。
在信息填写页面只简单说两句。
所有信息都可以随意填写,不用管是否真实。
App Store地区选择,一定要选对,它对应的是你创建的App的地区, 你App是中国的话, 在这里我们依然选择中国。
此账号只能用来测试,不要在正式的appstore上使用
填写完毕,点击保存后,我们则生成一个测试账号,当然这个账号是可以随时删除和添加的。
之后终于到了写代码的时候了,点开你的Xcode创建你的项目!
大部分代码都可以在.m文件中实现。
#import "ViewController.h"
#import &StoreKit/StoreKit.h&
#import "SVProgressHUD.h"
@interface ViewController ()&SKPaymentTransactionObserver,SKProductsRequestDelegate&
@property (nonatomic,copy) NSString *currentProId;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(100, 100, 100, 100);
button.backgroundColor = [UIColor greenColor];
[button setTitle:@"6元" forState:UIControlStateNormal];
[button addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:button];
- (void)btnClick:(UIButton *)button
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
_currentProId = @"123";
if([SKPaymentQueue canMakePayments]){
[self requestProductData:product];
NSLog(@"不允许程序内付费");
- (void)requestProductData:(NSString *)type{
NSLog(@"-------------请求对应的产品信息----------------");
[SVProgressHUD showWithStatus:nil maskType:SVProgressHUDMaskTypeBlack];
NSArray *product = [[NSArray alloc] initWithObjects:type,nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSLog(@"--------------收到产品反馈消息---------------------");
NSArray *product = response.products;
if([product count] == 0){
[SVProgressHUD dismiss];
NSLog(@"--------------没有商品------------------");
NSLog(@"productID:%@", response.invalidProductIdentifiers);
NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
SKProduct *p = nil;
for (SKProduct *pro in product) {
NSLog(@"%@", [pro description]);
NSLog(@"%@", [pro localizedTitle]);
NSLog(@"%@", [pro localizedDescription]);
NSLog(@"%@", [pro price]);
NSLog(@"%@", [pro productIdentifier]);
if([pro.productIdentifier isEqualToString:_currentProId]){
SKPayment *payment = [SKPayment paymentWithProduct:p];
NSLog(@"发送购买请求");
[[SKPaymentQueue defaultQueue] addPayment:payment];
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
[SVProgressHUD showErrorWithStatus:@"支付失败"];
NSLog(@"------------------错误-----------------:%@", error);
- (void)requestDidFinish:(SKRequest *)request{
[SVProgressHUD dismiss];
NSLog(@"------------反馈信息结束-----------------");
#define SANDBOX @"https://sandbox./verifyReceipt"
#define AppStore @"https://buy./verifyReceipt"
-(void)verifyPurchaseWithPaymentTransaction{
NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];
NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];
NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
NSURL *url=[NSURL URLWithString:SANDBOX];
NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
requestM.HTTPBody=bodyD
requestM.HTTPMethod=@"POST";
NSError *error=nil;
NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
if (error) {
NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@",dic);
if([dic[@"status"] intValue]==0){
NSLog(@"购买成功!");
NSDictionary *dicReceipt= dic[@"receipt"];
NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
NSString *productIdentifier= dicInApp[@"product_id"];
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
if ([productIdentifier isEqualToString:@"123"]) {
int purchasedCount=[defaults integerForKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
[defaults setBool:YES forKey:productIdentifier];
NSLog(@"购买失败,未通过验证!");
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction){
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:{
NSLog(@"交易完成");
[self verifyPurchaseWithPaymentTransaction];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加进列表");
case SKPaymentTransactionStateRestored:{
NSLog(@"已经购买过商品");
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
case SKPaymentTransactionStateFailed:{
NSLog(@"交易失败");
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
[SVProgressHUD showErrorWithStatus:@"购买失败"];
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
NSLog(@"交易结束");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
在这里需要注意几点,
1. 代码中的_currentProId所填写的是你的购买项目的的ID,这个和第二步创建的内购的productID要一致;本例中是 123。
2. 在监听购买结果后,一定要调用[[SKPaymentQueue defaultQueue] finishTransaction:tran];来允许你从支付队列中移除交易。
3. 沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。
4. 请务必使用真机来测试,一切以真机为准。
5. 项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。
6. 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号
7. 二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的时候需要判断返回Status Code决定是否去沙盒进行二次验证,为了线上用户的使用,验证的顺序肯定是先验证正式环境,此时若返回值为21007,就需要去沙盒二次验证,因为此购买的是在沙盒进行的。
附:苹果支付错误目录
Status Code
Description
The App Store could not read the JSON object you provided.
The data in the receipt-data property was malformed or missing.
The receipt could not be authenticated.
The shared secret you provided does not match the shared secret on file for your account.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
The receipt server is not currently available.
This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.
我的热门文章
即使是一小步也想与你分享免费内购方法的完整分析
(window.slotbydup=window.slotbydup || []).push({
id: '2611110',
container: s,
size: '240,200',
display: 'inlay-fix'
您当前位置: &
[ 所属分类
| 时间 2015 |
一、安装方法
1.LocalIAPStore
二、原理分析
1.IAPCracker
三、防御建议
1.apple官方
-------------------------------------------
很有人气的iOS 5.1不越狱免费内购方法提出者俄罗斯Alexey V. Borodin(网名ZonD80)于日在网站中申明由于iOS 6更换了服务器并采用了EV证书,该方法不再生效,并由于他要转战android平台,所以不再继续提供对iOS免费内购的支持。
该iOS免费内购服务曾关闭过多次,有apple屏蔽服务器IP的原因,有服务器压力支撑不住的原因,有没人捐赠停服的原因,有iOS新版本修复漏洞的原因,总之是命运多舛,但都继续下来了,并且ZonD80还将如何搭建免费内购服务器的方法公布在了github上。我觉得,这次也不会例外,服务还会继续开放的,并且很快会扩展到Android平台上的。
在之前的文章中,我提及过这种免费内购方法,但有部分情况(如推出的LocalIAPStore插件)没有覆盖到,所以打算补充一下,写得更为完整准确点。
-------------------------------------------
下面介绍的方法是在日发布的(号称在iOS 6 Golden Master版本上测试成功),我试验了一下,对于IAP内购没有第三方服务端验证收据的应用完全有效(对这类应用只需用到LocalIAPStore插件);对运行在iOS 5.1.1并没有采用由第三方服务器与app store服务器直接验证收据措施的应用也是有效的(对这类应用需要连接服务器)。但对于iOS6正式版本,我没有验证。因为我的测试机只有一台,由于iOS6对某些测试工具的不支持所以没有升级到iOS6.期待网友们能帮忙验证一下。我推测是不生效的,因为黑客在日申明停止该服务的部分原因是iOS 6正式版本采用了EV证书,该方法不再生效。
一、安装使用方法
下面先介绍完整的使用方法
第一步: 如果有安装IAPFree与IAPCracker,请先卸载
第二步:安装LocalIAPStore
(1)如果有安装cydia,直接打开cydia添加源 http://system./repo/
搜索LocalIAPStore,安装
添加源后,在iOS设备的/etc/apt/sources.list.d/cydia.list文件中(该文件对应的是cydia中所有的软件源地址),会增加一行文字,如下所示
(2)如果没有安装cydia
首先,SSH到iOS设备上
————————-知道如何SSH,不用看这段——————————————————————-
i.使用wifi ssh连接iOS设备请安装SSH服务端openssh;PC上,如果是,可以使用SSH客户端Puttty或SecureCRT)ssh root@iOS设备IP默认密码是alpineII.没有wifi的,使用USB ssh连接插上USB数据线后,windows上使用itunnel_mux.exeitunnel_mux.exe --lport 1234ssh
——————————————————————————————————————————–
然后,编辑/etc/apt/sources.list/saurik.list,添加一行deb http://system./repo/ ./
vim/etc/apt/sources.list.d/saurik.list (编辑软件源地址列表)
apt-get update (更新软件源)
apt-get install anondev.LocalIAPStore (安装插件)
重启SpringBoard或重启设备
LocalAPStore安装后,在/Library/MobileSubstrate/DynamicLibraries目录下会发现2个文件
LocalAPStore.dylib为Mach-O dynamically linked shared library,可以使用IDA来查看
LocalIAPStore.plist文件为过滤文件,用来限制dylib是否被加载,如下图所示,表示LocalAPStore.dylib只在调用StoreKit功能时加载,即在发生内购操作时加载。
iOS平台上安装的第三方插件,一般采用MobileSubstrate开源框架开发,原理是利用ojective-c reflective lauguage 特性进行dylib注入(类似windows的dll注入),使用动态链接指令DYLD_INSERT_LIBRARIES在运行时调用class_replaceMethod替换系统函数。MobileSubstrate为这一hook封装了更便利得方法。
第三步:有些应用不需要连接到就可以完成免费内购,有些应用需要连接到 服务器,大多数需要连接到该服务器。
连接方法如下所示(这就是很著名的不越狱俄罗斯内购方法):
(1)注销已登录的apple ID
(2)在设备上安装证书文件,使用浏览器打开http://system./
点击First,安装
点击安装,会提示该证书为经过验证
忽略之,点击安装,回到浏览器,点击second
点击安装,会弹出安装该证书会改变iPod上得设置
忽略之,点击现在安装,证书安装成功后,会在设置-通用-描述文件中看到已安装好的证书
(3)修改设备接入wifi的DHCP设置中DNS服务器栏
94.228.221.10(黑客说为了减少每月的维护费用,停用这个)91.224.160.136(目前只有这个可用)
(4)进入游戏,打开购买链接,在如下弹出框中选择like,然后随便输入非真实的apple id,就可以购买成功了。
备注:如果出现预设的”请确认您的软件内购买。。。” ,说明安装失败,你并没有连接到服务器上,需要重复第三步。
-------------------------------------------------方法介绍结束
三、原理分析
(1)IAPCracker插件
对于IAPCracker,能够生效的IAP流程是客户端收到来自App store server的SKPaymentTransaction后,直接根据SKPaymentTransaction的transactionState来判断是否下发购买内容。(这也是IAP built-in内置模式,非常不安全)
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) {case SKPaymentTransactionStatePurchased: if([self putStringToItunes:transaction.transactionReceipt]){ //不少开发不做收据校验,就在这里直接给用户添加钱,装备之类的 } ......代码省略 } }}
对于这种情况,可以在客户端将transactionState修改为SKPaymentTransactionStatePurchased来欺骗应用程序下发购买内容。IAPCracker就是采用的这种办法。
(2)IAP Free插件
经历IAPCracker的洗礼后,IAP流程中开始加入了收据验证。
收据验证的步骤如下:
第一步:从SKPaymentTransaction的transactionReceipt属性种提取收据数据,然后使用base64编码该数据
第二步:创建一个json对象,key名receipt-data,key值为第一步中编码后的收据数据
{ "receipt-data" : "(receipt bytes here)"}
第三步:将该json对象通过HTTP POST的方法发送给app store server,地址为https://buy./verifyReceipt
第四步:app store server会返回一个JSON对象,该对象包括两个key,一个为status表示收据是否合法,0表示合法;一个为receipt表示收据数据。
{ "status" : 0, "receipt" : { (receipt here) }}
receipt数据包括了一些购买信息,用JSON格式表示,具体的key名如下表所示
物品购买数量。
product_id
购买物品的product标示符。
transaction_id
购买物品的transaction标示符,为transaction的transactionIdentifier属性
purchase_date
transaction发生时间,为transaction的transactionDate属性
original_transaction_id
最初的transaction标示符
original_purchase_date
最初的购买时间
app_item_id
用于标识transaction中app的唯一性字符。在sandbox创建的receipt没有该字段
version_external_identifier
用于标识app修订版本的唯一性数字。在sandbox创建的receipt没有该字段
app的bundle identifier
-------------以下是SKPaymentTransaction对象的属性值-----------
Getting Information About the Transaction
error 描述transaction处理中发生的错误,类型为NSError
payment transaction的支付内容,类型为SKPayment
transactionState tranaction当前的状态,类型为SKPaymentTransactionState
transactionIdentifier 成功支付transaction的唯一性标示符,类型为NSString
transactionReceipt 用于纪录成功支付transaction相关信息的签名收据,类型为NSData
transactionDate transaction加入支付队列的时间,类型为NSDate
downloads交易中的可下载内容(即购买内容),类型为NSArray
originalTransaction用于app store transaction恢复类型为SKPaymentTransaction
-------------以下是SKPayment对象的属性值-----------------
productIdentifier 购买物品的product标示符,属性为NSString
quantityproperty 购买物品的数量,属性为NSInteger
-------------以下是SKPaymentTransactionState对象的属性值-----------------
enum { SKPaymentTransactionStatePurchasing, SKPaymentTransactionStatePurchased, SKPaymentTransactionStateFailed, SKPaymentTransactionStateRestored }; typedef NSInteger SKPaymentTransactionS
-------------------------------------------------------
下图是来自app store server的合法receipt示例
再看看IAPFree伪造的receipt
IAPFree之所以生效,主要是由于收据校验的流程于步骤发生了两处错误。
第一处:验证流程漏洞(最致命的)
收据校验由客户端与app store server直接进行验证。
记住一个真理,客户端上一切数据都是可以修改的。
我们可以在客户端上伪造receipt,这正是IAPFree做的事情;
我们可以通过在设备上安装伪造的SSL证书,修改设备的DNS地址,让客户端上的app向伪造的app store server去验证收据,这正是做的事情;
第二处:验证方法漏洞
根据Receipt的status状态是否为0来判断是否为合法的Receipt,而不去校验receipt中的唯一性标识,例如收据签名(receipt signature)
如果APP没有独立的服务器,或者出于用户付费率考虑(由服务器与app store server进行收据验证显然会增加购买等待时间,中国的wifi又不给力),那就必须严格的验证订单,例如transaction_key,收据签名。但只验证是否合法会有个严重缺陷,如果能伪造一个完全合法的订单,该怎么办?就可以实现到这种程度。
从网站说明来看,是利用iOS5证书漏洞,搭建了一个app store服务器的替代品,可以理解为游戏中的私服
下图说明了利用的什么漏洞,SSL证书硬编码与Receipt可以使用自定义证书来签名。意思是可以使用自己伪造的证书来签名收据。
下图说明了该服务是完全模拟了app store server购买算法
下图说明了该服务是完全模拟了app store server购买算法
ZonD80在github上开源了用于模拟app store购买算法的源码,有兴趣的可以下载源码,搭建环境,体验一下。
1.Unix/系统
2.DNSMasq DNS服务器,
dnsmasq.conf配置如下所示如下图所示
3. Nginx/Apache
if (!-e $request_filename) { rewrite ^/(.*)$ /iapcracker.php?URL=$1 }
4.在iOS 5设备安装2个证书,修改设备的DNS IP为你服务器IP
cacert.pem
itcert.pem
四、防御建议
总而言之,对IAP而言,能绕过这些邪恶工具的唯一可靠办法就是由独立的服务器与app store server验证收据是否合法,不要经过万恶的客户端。如果不得不由客户端与app store server验证收据,那就只能采取下面不怎么可靠的办法。
1.确保app用于连接app store server的SSL证书采用的是EV SSL证书2.确保transaction的transaction ID是唯一的3.确保Receipt的签名合法4.确保Receipt中购买信息的合法性
1,确保连接的是真正的app store server;2、3、4确保Receipt是未篡改的Receipt
1. 苹果官方校验代码
// Check the validity of the receipt. If it checks out then also ensure the transaction is something// we haven't seen before and then decode and save the purchaseInfo from the receipt for later receipt validation.- (BOOL)isTransactionAndItsReceiptValid:(SKPaymentTransaction *)transaction{ if (!(transaction && transaction.transactionReceipt && [transaction.transactionReceipt length] & 0)) { // Transaction is not valid. return NO; } // Pull the purchase-info out of the transaction receipt, decode it, and save it for later so // it can be cross checked with the verifyReceipt. NSDictionary *receiptDict = [self dictionaryFromPlistData:transaction.transactionReceipt]; NSString *transactionPurchaseInfo = [receiptDict objectForKey:@"purchase-info"]; NSString *decodedPurchaseInfo = [self decodeBase64:transactionPurchaseInfo length:nil];receiptDict NSDictionary *purchaseInfoDict = [self dictionaryFromPlistData:[decodedPurchaseInfo dataUsingEncoding:NSUTF8StringEncoding]]; NSString *transactionId = [purchaseInfoDict objectForKey:@"transaction-id"]; NSString *purchaseDateString = [purchaseInfoDict objectForKey:@"purchase-date"]; NSString *signature = [objectForKey:@"signature"]; // Convert the string into a date NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; [dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss z"]; NSDate *purchaseDate = [dateFormat dateFromString:[purchaseDateString stringByReplacingOccurrencesOfString:@"Etc/" withString:@""]]; if (![self isTransactionIdUnique:transactionId]) { // We've seen this transaction before. // Had [transactionsReceiptStorageDictionary objectForKey:transactionId] // Got purchaseInfoDict return NO; } // Check the authenticity of the receipt response/signature etc. BOOL result = checkReceiptSecurity(transactionPurchaseInfo, signature, (__bridge CFDateRef)(purchaseDate)); if (!result) { return NO; } // Ensure the transaction itself is legit if (![self doTransactionDetailsMatchPurchaseInfo:transaction withPurchaseInfo:purchaseInfoDict]) { return NO; } // Make a note of the fact that we've seen the transaction id already [self saveTransactionId:transactionId];
// Save the transaction receipt's purchaseInfo in the transactionsReceiptStorageDictionary. [transactionsReceiptStorageDictionary setObject:purchaseInfoDict forKey:transactionId]; return YES;}
网上也有第三方服务来提供receipt验证,例如Beeblex,Beeblex提供IAP收据验证服务,用来防御收据伪造和中间人欺骗共计。他们使用了更有效的加密算法和有时间限制的令牌,使伪造收据变得十分困难。Beeblex非常容易使用,只需要调用他们提供的SDK就行了。
-------------------------------------------
周末两天都泡在和ZonD80博客里,相当喜欢这个人,能发现有趣的事情,能共享有趣的事情,就是有趣的人生。
附上ZonD80博客中的一首歌,,轻摇滚风格的。
参考网站:
http://www./
http://www.chto.su/
http://habrahabr.ru/post/149207/
/library/ios/#documentation/StoreKit/Reference/SKPaymentTransaction_Class/Reference/Reference.html
/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html#//apple_ref/doc/uid/TP-CH104-SW1
/library/ios/#releasenotes/StoreKit/IAP_ReceiptValidation/_index.html#//apple_ref/doc/uid/TP-CH0-SW4
/public/overview
/iphone-cocos2d/673.html
本文网络安全相关术语:网络安全工程师 网络信息安全 网络安全技术 网络安全知识
转载请注明本文标题:本站链接:
分享请点击:
1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
阅读(2302)
CodeSecTeam微信公众号
一切随心意!且行且珍惜!
手机客户端

我要回帖

更多关于 iap transaction id 的文章

 

随机推荐