苹果开发——App内购以及验证store的收据(二)
原地址:http://zengwu3915.blog.163.com/blog/static/2783489720137605156966?suggestedreading
//
// StoreObserver.h
// HelloStore
//
// Created by guoke on 13-8-4.
// Copyright (c) 2013年 guoke. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
#import <StoreKit/SKPaymentTransaction.h>
@interface StoreObserver : NSObject <SKPaymentTransactionObserver, SKProductsRequestDelegate>
{
int buyType;
}
-(void)Create;
-(void)Destroy;
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
-(void)PurchasedTransaction: (SKPaymentTransaction *)transaction;
-(void)completeTransaction: (SKPaymentTransaction *)transaction;
-(void)failedTransaction: (SKPaymentTransaction *)transaction;
-(void)paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction;
-(void)paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error;
-(void)Buy:(int)type;
-(bool)CanMakePay;
-(void)RequestProductData;
-(void)restoreTransaction: (SKPaymentTransaction *)transaction;
@end
//
// StoreObserver.m
// HelloStore
//
// Created by guoke on 13-8-4.
// Copyright (c) 2013年 guoke. All rights reserved.
//
#import "StoreObserver.h"
#define ProductID @"com.HelloStorTest.TestItem" // 产品ID,对应iTunes Connect中设置的产品ID
@implementation StoreObserver
-(void)Create
{
//---------------------
//----监听购买结果
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
-(void)Destroy
{
//解除监听
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
-(void)Buy:(int)type
{
buyType = type;
if ([self CanMakePay])
{
NSLog(@"允许程序内付费购买");
[self RequestProductData];
}
else
{
NSLog(@"不允许程序内付费购买");
}
}
-(bool)CanMakePay
{
return [SKPaymentQueue canMakePayments];
}
-(void)RequestProductData
{
NSLog(@"---------请求对应的产品信息------------");
NSArray *product = nil;
// 这时可以根据buyType不同选择不同的商品,这个只是测试,只用了一种
product=[[NSArray alloc] initWithObjects:ProductID,nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
request.delegate=self;
[request start];
[product release];
}
//<SKProductsRequestDelegate> 请求协议
//收到的产品信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(@"-----------收到产品反馈信息--------------");
NSArray *myProduct = response.products;
NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
NSLog(@"产品付费数量: %d", [myProduct count]);
// populate UI
for(SKProduct *product in myProduct){
NSLog(@"product info");
NSLog(@"SKProduct 描述信息%@", [product description]);
NSLog(@"产品标题 %@" , product.localizedTitle);
NSLog(@"产品描述信息: %@" , product.localizedDescription);
NSLog(@"价格: %@" , product.price);
NSLog(@"Product id: %@" , product.productIdentifier);
}
SKPayment *payment = nil;
payment = [SKPayment paymentWithProductIdentifier:ProductID];
NSLog(@"---------发送购买请求------------");
[[SKPaymentQueue defaultQueue] addPayment:payment];
[request autorelease];
}
- (void)requestProUpgradeProductData
{
NSLog(@"------请求升级数据---------");
NSSet *productIdentifiers = [NSSet setWithObject:@"com.productid"];
SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
}
//弹出错误信息
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(@"-------弹出错误信息----------");
}
-(void) requestDidFinish:(SKRequest *)request
{
NSLog(@"----------反馈信息结束--------------");
}
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction
{
NSLog(@"-----PurchasedTransaction----");
NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
[self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
[transactions release];
}
//<SKPaymentTransactionObserver> 千万不要忘记绑定,代码如下:
//----监听购买结果
//[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray*)transactions//交易结果
{
NSLog(@"-----paymentQueue--------");
for(SKPaymentTransaction*transaction in transactions)
{
switch(transaction.transactionState)
{
caseSKPaymentTransactionStatePurchased://交易完成
[self completeTransaction:transaction];
NSLog(@"-----交易完成 --------");
break;
caseSKPaymentTransactionStateFailed://交易失败
[self failedTransaction:transaction];
NSLog(@"-----交易失败 --------");
break;
caseSKPaymentTransactionStateRestored://已经购买过该商品
[self restoreTransaction:transaction];
NSLog(@"-----已经购买过该商品 --------");
caseSKPaymentTransactionStatePurchasing://商品添加进列表
NSLog(@"-----商品添加进列表 --------");
break;
default:
break;
}
}
}
-(void) completeTransaction:(SKPaymentTransaction*)transaction
{
NSLog(@"-----completeTransaction--------");
// 接受到的App Store验证字符串,这里需要经过JSON编码
NSString* jsonObjectString =[self encode:(uint8_t*)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];
// 将jsonObjectString字符串发给服务器,由服务器POST到iTunes上验证
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
NSLog(@"失败");
if (transaction.error.code != SKErrorPaymentCancelled)
{
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction
{
}
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
NSLog(@" 交易恢复处理");
}
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
NSLog(@"-------paymentQueue----");
}
// base64编码
- (NSString *)encode:(const uint8_t *)input length:(NSInteger)length
{
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t *output = (uint8_t *)data.mutableBytes;
for (NSInteger i = 0; i < length; i += 3) {
NSInteger value = 0;
for (NSInteger j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger index = (i / 3) * 4;
output[index + 0] = table[(value >> 18) & 0x3F];
output[index + 1] = table[(value >> 12) & 0x3F];
output[index + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[index + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}
return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}
@end
在ViewController.m中创建和释放
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.observer = [[StoreObserver alloc] init];
[self.observer Create];
// test
[self.observer Buy:0];
}
- (void)viewDidUnload
{
[self.observer Destroy];
[self.observer release];
}
编译运行,将会看到以下Log信息
2013-08-04 19:15:39.776 HelloStoreTest[9839:c07] 允许程序内付费购买
2013-08-04 19:15:39.777 HelloStoreTest[9839:c07] ---------请求对应的产品信息------------
2013-08-04 19:15:39.780 HelloStoreTest[9839:c07] -----paymentQueue--------
2013-08-04 19:15:39.781 HelloStoreTest[9839:c07] -----商品添加进列表 --------
2013-08-04 19:15:39.989 HelloStoreTest[9839:c07] -----------收到产品反馈信息--------------
2013-08-04 19:15:39.990 HelloStoreTest[9839:c07] 产品Product ID:(
)
2013-08-04 19:15:39.990 HelloStoreTest[9839:c07] 产品付费数量: 1
2013-08-04 19:15:39.990 HelloStoreTest[9839:c07] product info
2013-08-04 19:15:39.991 HelloStoreTest[9839:c07] SKProduct 描述信息<SKProduct: 0x8b4e5a0>
2013-08-04 19:15:39.991 HelloStoreTest[9839:c07] 产品标题 测试商品
2013-08-04 19:15:39.991 HelloStoreTest[9839:c07] 产品描述信息: 这里是一个store测试商品
2013-08-04 19:15:39.992 HelloStoreTest[9839:c07] 价格: 6
2013-08-04 19:15:39.992 HelloStoreTest[9839:c07] Product id: com.HelloStorTest.TestItem
2013-08-04 19:15:39.992 HelloStoreTest[9839:c07] ---------发送购买请求------------
2013-08-04 19:15:39.993 HelloStoreTest[9839:c07] -----paymentQueue--------
2013-08-04 19:15:39.993 HelloStoreTest[9839:c07] -----商品添加进列表 --------
2013-08-04 19:15:39.994 HelloStoreTest[9839:c07] ----------反馈信息结束--------------
在上面的客户端代码中,我们监听函数中在交易成功时调用了completeTransaction函数,购买成功之后,我们会收到反馈的收据,其中在transactionReceipt中存验证信息。将字打印出来格式为:
{
"signature" = "Arcf6xlj4B3XqosYqFrhSNltW+HZS1UOLTh30CLb2AXTW5GABZRK52j1B8eyOynXUqlqhpZCjCk51pWyiO59JCukrz9ktQeaVCha2xgqjzasqz6x8Ulsm+VeA4bDs3eakq5xcMgogRDUOBg5sJauPwMQA30PnLV+ruypva91JAS2AAADVzCCA1MwggI7oAMCAQICCGUUkU3ZWAS1MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MDYxNTIyMDU1NloXDTE0MDYxNDIyMDU1NlowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrRjF2ct4IrSdiTChaI0g8pwv/cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ==";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDEzLTA4LTA0IDA0OjE1OjQ1IEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICIwMDAwYjAzMWM3YjgiOwoJIm9yaWdpbmFsLXRyYW5zYWN0aW9uLWlkIiA9ICIxMDAwMDAwMDgyODY4NjY0IjsKCSJidnJzIiA9ICIxLjAiOwoJInRyYW5zYWN0aW9uLWlkIiA9ICIxMDAwMDAwMDgyODY4NjY0IjsKCSJxdWFudGl0eSIgPSAiMSI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZS1tcyIgPSAiMTM3NTYxNDk0NTI3NSI7CgkidW5pcXVlLXZlbmRvci1pZGVudGlmaWVyIiA9ICI2Q0Y4ODg1Ni1FRjZGLTRGN0UtOTJGOC1BNUEwMjJFNzdERUIiOwoJInByb2R1Y3QtaWQiID0gImNvbS5IZWxsb1N0b3JUZXN0LlRlc3RJdGVtIjsKCSJpdGVtLWlkIiA9ICI2ODM5MTkxNjIiOwoJImJpZCIgPSAiY29tLmd1b2tlLkhlbGxvU3RvcmVUZXN0IjsKCSJwdXJjaGFzZS1kYXRlLW1zIiA9ICIxMzc1NjE0OTQ1Mjc1IjsKCSJwdXJjaGFzZS1kYXRlIiA9ICIyMDEzLTA4LTA0IDExOjE1OjQ1IEV0Yy9HTVQiOwoJInB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDEzLTA4LTA0IDA0OjE1OjQ1IEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUiID0gIjIwMTMtMDgtMDQgMTE6MTU6NDUgRXRjL0dNVCI7Cn0=";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}
将这个字符串发给服务器,然后用服务器去POST到App上去验证。
NSString* ObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];
// 其中encode就是base64编码转换函数,编码后的数据为类似:
ewoJInNpZ25hdHVyZSIgPSAiQXJjZjZ4bGo0QjNYcW9zWXFGcmhTTmx0VytIWlMxVU9MVGgzMENMYjJBWFRXNUdBQlpSSzUyajFCOGV5T3luWFVxbHFocFpDakNrNTFwV3lpTzU5SkN1a3J6OWt0UWVhVkNoYTJ4Z3FqemFzcXo2eDhVbHNtK1ZlQTRiRHMzZWFrcTV4Y01nb2dSRFVPQmc1c0phdVB3TVFBMzBQbkxWK3J1eXB2YTkxSkFTMkFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NHVVVrVTNaV0FTMU1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEE1TURZeE5USXlNRFUxTmxvWERURTBNRFl4TkRJeU1EVTFObG93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNclJqRjJjdDRJclNkaVRDaGFJMGc4cHd2L2NtSHM4cC9Sd1YvcnQvOTFYS1ZoTmw0WElCaW1LalFRTmZnSHNEczZ5anUrK0RyS0pFN3VLc3BoTWRkS1lmRkU1ckdYc0FkQkVqQndSSXhleFRldngzSExFRkdBdDFtb0t4NTA5ZGh4dGlJZERnSnYyWWFWczQ5QjB1SnZOZHk2U01xTk5MSHNETHpEUzlvWkhBZ01CQUFHamNqQndNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVOaDNvNHAyQzBnRVl0VEpyRHRkREM1RllRem93RGdZRFZSMFBBUUgvQkFRREFnZUFNQjBHQTFVZERnUVdCQlNwZzRQeUdVakZQaEpYQ0JUTXphTittVjhrOVRBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUVhU2JQanRtTjRDL0lCM1FFcEszMlJ4YWNDRFhkVlhBZVZSZVM1RmFaeGMrdDg4cFFQOTNCaUF4dmRXLzNlVFNNR1k1RmJlQVlMM2V0cVA1Z204d3JGb2pYMGlreVZSU3RRKy9BUTBLRWp0cUIwN2tMczlRVWU4Y3pSOFVHZmRNMUV1bVYvVWd2RGQ0TndOWXhMUU1nNFdUUWZna1FRVnk4R1had1ZIZ2JFL1VDNlk3MDUzcEdYQms1MU5QTTN3b3hoZDNnU1JMdlhqK2xvSHNTdGNURXFlOXBCRHBtRzUrc2s0dHcrR0szR01lRU41LytlMVFUOW5wL0tsMW5qK2FCdzdDMHhzeTBiRm5hQWQxY1NTNnhkb3J5L0NVdk02Z3RLc21uT09kcVRlc2JwMGJzOHNuNldxczBDOWRnY3hSSHVPTVoydG04bnBMVW03YXJnT1N6UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREV6TFRBNExUQTBJREEwT2pFMU9qUTFJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluVnVhWEYxWlMxcFpHVnVkR2xtYVdWeUlpQTlJQ0l3TURBd1lqQXpNV00zWWpnaU93b0pJbTl5YVdkcGJtRnNMWFJ5WVc1ellXTjBhVzl1TFdsa0lpQTlJQ0l4TURBd01EQXdNRGd5T0RZNE5qWTBJanNLQ1NKaWRuSnpJaUE5SUNJeExqQWlPd29KSW5SeVlXNXpZV04wYVc5dUxXbGtJaUE5SUNJeE1EQXdNREF3TURneU9EWTROalkwSWpzS0NTSnhkV0Z1ZEdsMGVTSWdQU0FpTVNJN0Nna2liM0pwWjJsdVlXd3RjSFZ5WTJoaGMyVXRaR0YwWlMxdGN5SWdQU0FpTVRNM05UWXhORGswTlRJM05TSTdDZ2tpZFc1cGNYVmxMWFpsYm1SdmNpMXBaR1Z1ZEdsbWFXVnlJaUE5SUNJMlEwWTRPRGcxTmkxRlJqWkdMVFJHTjBVdE9USkdPQzFCTlVFd01qSkZOemRFUlVJaU93b0pJbkJ5YjJSMVkzUXRhV1FpSUQwZ0ltTnZiUzVJWld4c2IxTjBiM0pVWlhOMExsUmxjM1JKZEdWdElqc0tDU0pwZEdWdExXbGtJaUE5SUNJMk9ETTVNVGt4TmpJaU93b0pJbUpwWkNJZ1BTQWlZMjl0TG1kMWIydGxMa2hsYkd4dlUzUnZjbVZVWlhOMElqc0tDU0p3ZFhKamFHRnpaUzFrWVhSbExXMXpJaUE5SUNJeE16YzFOakUwT1RRMU1qYzFJanNLQ1NKd2RYSmphR0Z6WlMxa1lYUmxJaUE5SUNJeU1ERXpMVEE0TFRBMElERXhPakUxT2pRMUlFVjBZeTlIVFZRaU93b0pJbkIxY21Ob1lYTmxMV1JoZEdVdGNITjBJaUE5SUNJeU1ERXpMVEE0TFRBMElEQTBPakUxT2pRMUlFRnRaWEpwWTJFdlRHOXpYMEZ1WjJWc1pYTWlPd29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVWlJRDBnSWpJd01UTXRNRGd0TURRZ01URTZNVFU2TkRVZ1JYUmpMMGROVkNJN0NuMD0iOwoJImVudmlyb25tZW50IiA9ICJTYW5kYm94IjsKCSJwb2QiID0gIjEwMCI7Cgkic2lnbmluZy1zdGF0dXMiID0gIjAiOwp9
2. 创建JSON对象,字典格式,单键值对,键名为”receipt-data“,值为上一步编码的数据
NSString* sendString = [[NSString alloc] initWithFormat:@"{\"receipt-data\":\"%@\"}",ObjectString ];
3. 发送HTTP POS 的请求,将数据发送到App Store,其地址为 https://buy.itunes.apple.com/verifyReceipt
NSURL *sandboxStoreURL = [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];
NSData *postData = [NSData dataWithBytes:[sendString UTF8String] length:[sendString length]];
NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:sandboxStoreURL];
[connectionRequest setHTTPMethod:@"POST"];
[connectionRequest setTimeoutInterval:120.0];
[connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
[connectionRequest setHTTPBody:postData];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
NSURLConnection *connection = [[NSURLConnection alloc]
initWithRequest:connectionRequest
delegate:self];
[connection release];
4. App Store的返回值也是一个JSON格式的对象,包含两个键值对, status和receipt:
{"receipt":{"original_purchase_date_pst":"2013-08-04 04:15:45 America/Los_Angeles", "purchase_date_ms":"1375614945275", "unique_identifier":"0000b031c7b8", "original_transaction_id":"1000000082868664", "bvrs":"1.0", "transaction_id":"1000000082868664", "quantity":"1", "unique_vendor_identifier":"6CF88856-EF6F-4F7E-92F8-A5A022E77DEB", "item_id":"683919162", "product_id":"com.HelloStorTest.TestItem", "purchase_date":"2013-08-04 11:15:45 Etc/GMT", "original_purchase_date":"2013-08-04 11:15:45 Etc/GMT", "purchase_date_pst":"2013-08-04 04:15:45 America/Los_Angeles", "bid":"com.guoke.HelloStoreTest", "original_purchase_date_ms":"1375614945275"}, "status":0}
下载
苹果开发——App内购以及验证store的收据(二)的更多相关文章
- 苹果开发——App内购以及验证store的收据(一)
原地址:http://zengwu3915.blog.163.com/blog/static/27834897201375105236580?suggestedreading 发了几天时间在网上折腾了 ...
- ios 苹果内购订单验证 --- php实现
验证函数: function appleVerify($receipt_data,$orderId = 0) { /* * 21000 App Store不能读取你提供的JSON对象 * 21002 ...
- 苹果内购服务器验证之receipt返回多组in_app思考
最近有部分用户反映,苹果内购充值失败,经过测试总结有几个关键点出现问题 1.app购买成功苹果没有返回票据,属于票据遗漏(取决于苹果服务器的响应状况),只能客户端进行监听刷新等处理 2.app连续购买 ...
- 苹果APP内购客户付款成功,没收到相应虚拟产品的解决办法
一.引导用户走申请苹果的退款 1.告知用户新版本可以使用支付宝.微信支付,更划算 2.苹果可申请90天以内的退款,一般情况申请后48小时内就有反馈. 参考链接 https://jingyan.baid ...
- iOS开发系列——内购、GameCenter、iCloud、Passbook系统服务开发汇总
社交 Social 现在很多应用都内置“社交分享”功能,可以将看到的新闻.博客.广告等内容分享到微博.微信.QQ.空间等,其实从iOS6.0开始苹果官方就内置了Social.framework专门来实 ...
- iOS APP内购
看到网上文章一大把,看了这个觉得挺不错的,谢谢 iOS大全 公众平台; 原文:http://mp.weixin.qq.com/s?__biz=MzAxMzE2Mjc2Ng==&mid=2652 ...
- php苹果内购订单验证
/** * 21000 App Store不能读取你提供的JSON对象 * 21002 receipt-data域的数据有问题 * 21003 receipt无法通过验证 * 21004 提供的sha ...
- c++ boost 苹果内购 IAP验证
// 1111.cpp: 定义控制台应用程序的入口点. // #include "stdafx.h" #include <cstdlib> #include <i ...
- ios 苹果内购订单验证 --- nodejs实现
实现代码 function IosPlayVerify(data,orderid,cb) { itunesPost(data,function (error,responseData) { if (e ...
随机推荐
- 金山词霸每日一句开放平台 .NET demo
先附上地址:http://open.iciba.com/?c=api 小金山提供了2种获取数据的方式 1. 通过填入自己的网站名称.网址.邮箱地址 来生成一段javascript脚本,直接将生成的代码 ...
- RF-BM-S02(V1.0)蓝牙4.0模块 使用手册
一.产品概述 图1 RF-BM-S02纯硬件模块 RF-BM-S02是一款采用美国德州仪器TI 蓝牙4.0 CC2540作为核心处理器的高性能.超低功耗(Bluetooth Low Energy)射频 ...
- python匿名函数(lambda)
简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数 当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方 ...
- kettle发送邮件
使用kettle发送邮件是为了更好的监控ETL的加载信息 以下是我通过测试的一个案例 1. JOB示意图 2.邮件发送配置详细信息 2.1地址信息配置 2.2 服务器信息配置 上图中所说的" ...
- 一篇文章教你读懂Makefile
makefile很重要 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professiona ...
- android开发系列之回调函数
想必对于回调函数大家肯定不陌生,因为这是我们开发里面常用的代码技巧.我也就不废话了,让我们直接来看代码吧! public class TestCallback { public interface I ...
- android开发系列之git常用命令
最近因为跳槽到新公司,然后新公司里面的代码管理工具是gitLab,所以我想在这篇博客里面整理一下git常用的语法. GitLab是利用 Ruby on Rails 一个开源的版本管理系统,实现一个自托 ...
- Java动态替换InetAddress中DNS的做法简单分析2
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.i ...
- Go字典
字典(map)是Go语言内置的数据结构,一组键值对的无序集合. 看代码: package main import "fmt" func main() { //使用make申请一个m ...
- 66.为什么有时候在ISE软件中,顶层文件不能置顶?
什么时候回出现顶层文件不能置顶呢?嘿嘿,肯定是工程中有错误啦. 如果你的顶层文件包含了include文件,这个时候就会出现这种情况了.但好像出现在刚新建工程的时候,因为当顶层文件不包括Include文 ...