原地址:http://zengwu3915.blog.163.com/blog/static/2783489720137605156966?suggestedreading

三、 客户端使用StoreKit完成内购
添加Storekit.Framework,编写自己的storeObsever,用于处理交易,代码如下,其中completeTransaction和failedTransaction两个函数是自定义的用来处理交易成功与失败其它的就都是SKPaymentTransactionObserver这个代理要求的。

//
// 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] ----------反馈信息结束--------------

四、验证store收据
交易完成后,服务器需要向Apple验证交易是否成功,不然,自己买的产品给别人了就亏了。

在上面的客户端代码中,我们监听函数中在交易成功时调用了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上去验证。

验证过程:( 原本是在服务器上验证的,为了测试,我们不弄太复杂,直接在当前程序做一个验证测试 )
1. 先取出transactionReceipt属性中的收据数据。并以base64方式编码

NSString* ObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];

// 其中encode就是base64编码转换函数,编码后的数据为类似:

ewoJInNpZ25hdHVyZSIgPSAiQXJjZjZ4bGo0QjNYcW9zWXFGcmhTTmx0VytIWlMxVU9MVGgzMENMYjJBWFRXNUdBQlpSSzUyajFCOGV5T3luWFVxbHFocFpDakNrNTFwV3lpTzU5SkN1a3J6OWt0UWVhVkNoYTJ4Z3FqemFzcXo2eDhVbHNtK1ZlQTRiRHMzZWFrcTV4Y01nb2dSRFVPQmc1c0phdVB3TVFBMzBQbkxWK3J1eXB2YTkxSkFTMkFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NHVVVrVTNaV0FTMU1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEE1TURZeE5USXlNRFUxTmxvWERURTBNRFl4TkRJeU1EVTFObG93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNclJqRjJjdDRJclNkaVRDaGFJMGc4cHd2L2NtSHM4cC9Sd1YvcnQvOTFYS1ZoTmw0WElCaW1LalFRTmZnSHNEczZ5anUrK0RyS0pFN3VLc3BoTWRkS1lmRkU1ckdYc0FkQkVqQndSSXhleFRldngzSExFRkdBdDFtb0t4NTA5ZGh4dGlJZERnSnYyWWFWczQ5QjB1SnZOZHk2U01xTk5MSHNETHpEUzlvWkhBZ01CQUFHamNqQndNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVOaDNvNHAyQzBnRVl0VEpyRHRkREM1RllRem93RGdZRFZSMFBBUUgvQkFRREFnZUFNQjBHQTFVZERnUVdCQlNwZzRQeUdVakZQaEpYQ0JUTXphTittVjhrOVRBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUVhU2JQanRtTjRDL0lCM1FFcEszMlJ4YWNDRFhkVlhBZVZSZVM1RmFaeGMrdDg4cFFQOTNCaUF4dmRXLzNlVFNNR1k1RmJlQVlMM2V0cVA1Z204d3JGb2pYMGlreVZSU3RRKy9BUTBLRWp0cUIwN2tMczlRVWU4Y3pSOFVHZmRNMUV1bVYvVWd2RGQ0TndOWXhMUU1nNFdUUWZna1FRVnk4R1had1ZIZ2JFL1VDNlk3MDUzcEdYQms1MU5QTTN3b3hoZDNnU1JMdlhqK2xvSHNTdGNURXFlOXBCRHBtRzUrc2s0dHcrR0szR01lRU41LytlMVFUOW5wL0tsMW5qK2FCdzdDMHhzeTBiRm5hQWQxY1NTNnhkb3J5L0NVdk02Z3RLc21uT09kcVRlc2JwMGJzOHNuNldxczBDOWRnY3hSSHVPTVoydG04bnBMVW03YXJnT1N6UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREV6TFRBNExUQTBJREEwT2pFMU9qUTFJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluVnVhWEYxWlMxcFpHVnVkR2xtYVdWeUlpQTlJQ0l3TURBd1lqQXpNV00zWWpnaU93b0pJbTl5YVdkcGJtRnNMWFJ5WVc1ellXTjBhVzl1TFdsa0lpQTlJQ0l4TURBd01EQXdNRGd5T0RZNE5qWTBJanNLQ1NKaWRuSnpJaUE5SUNJeExqQWlPd29KSW5SeVlXNXpZV04wYVc5dUxXbGtJaUE5SUNJeE1EQXdNREF3TURneU9EWTROalkwSWpzS0NTSnhkV0Z1ZEdsMGVTSWdQU0FpTVNJN0Nna2liM0pwWjJsdVlXd3RjSFZ5WTJoaGMyVXRaR0YwWlMxdGN5SWdQU0FpTVRNM05UWXhORGswTlRJM05TSTdDZ2tpZFc1cGNYVmxMWFpsYm1SdmNpMXBaR1Z1ZEdsbWFXVnlJaUE5SUNJMlEwWTRPRGcxTmkxRlJqWkdMVFJHTjBVdE9USkdPQzFCTlVFd01qSkZOemRFUlVJaU93b0pJbkJ5YjJSMVkzUXRhV1FpSUQwZ0ltTnZiUzVJWld4c2IxTjBiM0pVWlhOMExsUmxjM1JKZEdWdElqc0tDU0pwZEdWdExXbGtJaUE5SUNJMk9ETTVNVGt4TmpJaU93b0pJbUpwWkNJZ1BTQWlZMjl0TG1kMWIydGxMa2hsYkd4dlUzUnZjbVZVWlhOMElqc0tDU0p3ZFhKamFHRnpaUzFrWVhSbExXMXpJaUE5SUNJeE16YzFOakUwT1RRMU1qYzFJanNLQ1NKd2RYSmphR0Z6WlMxa1lYUmxJaUE5SUNJeU1ERXpMVEE0TFRBMElERXhPakUxT2pRMUlFVjBZeTlIVFZRaU93b0pJbkIxY21Ob1lYTmxMV1JoZEdVdGNITjBJaUE5SUNJeU1ERXpMVEE0TFRBMElEQTBPakUxT2pRMUlFRnRaWEpwWTJFdlRHOXpYMEZ1WjJWc1pYTWlPd29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVWlJRDBnSWpJd01UTXRNRGd0TURRZ01URTZNVFU2TkRVZ1JYUmpMMGROVkNJN0NuMD0iOwoJImVudmlyb25tZW50IiA9ICJTYW5kYm94IjsKCSJwb2QiID0gIjEwMCI7Cgkic2lnbmluZy1zdGF0dXMiID0gIjAiOwp9

2. 创建JSON对象,字典格式,单键值对,键名为”receipt-data“,值为上一步编码的数据

{”recipt-data“:"编码后数据"}

NSString* sendString = [[NSString alloc] initWithFormat:@"{\"receipt-data\":\"%@\"}",ObjectString ];

3. 发送HTTP POS 的请求,将数据发送到App Store,其地址为 https://buy.itunes.apple.com/verifyReceipt

sandbox测试地址为:https://sandbox.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:

{
“status” : 0,
“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}

如果status的值为0, 就说明该receipt为有效的。 否则就是无效的。
 
App Store的收据
发送给App Store的收据数据是通过对transaction中对应的信息编码而创建的。 当App Store验证收据时, 将从其中解码出数据,并以”receipt”的键返回。 返回的响应信息是JSON格式,被包含在SKPaymentTransaction的对象中(transactionReceipt属性)。Server可通过这些值来了解交易的详细信息。 Apple建议只发送receipt数据到服务器并使用receipt数据验证和获得交易详情。 因为App Store可验证收据信息,返回信息,保证信息不被篡改,这种方式比同时提交receipt和transaction的数据要安全。
 
购买信息的键,很多的键都对应SKPaymentTransaction的属性。
键名  描述
quantity  购买商品的数量。对应SKPayment对象中的quantity属性
product_id  商品的标识,对应SKPayment对象的productIdentifier属性。
transaction_id  交易的标识,对应SKPaymentTransaction的transactionIdentifier属性
purchase_date  交易的日期,对应SKPaymentTransaction的transactionDate属性
original_-transaction_id  对于恢复的transaction对象,该键对应了原始的transaction标识
original_purchase_-date  对于恢复的transaction对象,该键对应了原始的交易日期
app_item_id  App Store用来标识程序的字符串。一个服务器可能需要支持多个server的支付功能,可以用这个标识来区分程序。链接sandbox用来测试的程序的不到这个值,因此该键不存在。
version_external_-identifier  用来标识程序修订数。该键在sandbox环境下不存在
bid iPhone 程序的bundle标识
bvrs iPhone 程序的版本号
 
参考文献:
 
附件为本文测试程序,直接改名为zip解压即可(因网易不支持zip)
 
 
HelloStore.rar(39.45K)   
下载

苹果开发——App内购以及验证store的收据(二)的更多相关文章

  1. 苹果开发——App内购以及验证store的收据(一)

    原地址:http://zengwu3915.blog.163.com/blog/static/27834897201375105236580?suggestedreading 发了几天时间在网上折腾了 ...

  2. ios 苹果内购订单验证 --- php实现

    验证函数: function appleVerify($receipt_data,$orderId = 0) { /* * 21000 App Store不能读取你提供的JSON对象 * 21002 ...

  3. 苹果内购服务器验证之receipt返回多组in_app思考

    最近有部分用户反映,苹果内购充值失败,经过测试总结有几个关键点出现问题 1.app购买成功苹果没有返回票据,属于票据遗漏(取决于苹果服务器的响应状况),只能客户端进行监听刷新等处理 2.app连续购买 ...

  4. 苹果APP内购客户付款成功,没收到相应虚拟产品的解决办法

    一.引导用户走申请苹果的退款 1.告知用户新版本可以使用支付宝.微信支付,更划算 2.苹果可申请90天以内的退款,一般情况申请后48小时内就有反馈. 参考链接 https://jingyan.baid ...

  5. iOS开发系列——内购、GameCenter、iCloud、Passbook系统服务开发汇总

    社交 Social 现在很多应用都内置“社交分享”功能,可以将看到的新闻.博客.广告等内容分享到微博.微信.QQ.空间等,其实从iOS6.0开始苹果官方就内置了Social.framework专门来实 ...

  6. iOS APP内购

    看到网上文章一大把,看了这个觉得挺不错的,谢谢 iOS大全 公众平台; 原文:http://mp.weixin.qq.com/s?__biz=MzAxMzE2Mjc2Ng==&mid=2652 ...

  7. php苹果内购订单验证

    /** * 21000 App Store不能读取你提供的JSON对象 * 21002 receipt-data域的数据有问题 * 21003 receipt无法通过验证 * 21004 提供的sha ...

  8. c++ boost 苹果内购 IAP验证

    // 1111.cpp: 定义控制台应用程序的入口点. // #include "stdafx.h" #include <cstdlib> #include <i ...

  9. ios 苹果内购订单验证 --- nodejs实现

    实现代码 function IosPlayVerify(data,orderid,cb) { itunesPost(data,function (error,responseData) { if (e ...

随机推荐

  1. 金山词霸每日一句开放平台 .NET demo

    先附上地址:http://open.iciba.com/?c=api 小金山提供了2种获取数据的方式 1. 通过填入自己的网站名称.网址.邮箱地址 来生成一段javascript脚本,直接将生成的代码 ...

  2. RF-BM-S02(V1.0)蓝牙4.0模块 使用手册

    一.产品概述 图1 RF-BM-S02纯硬件模块 RF-BM-S02是一款采用美国德州仪器TI 蓝牙4.0 CC2540作为核心处理器的高性能.超低功耗(Bluetooth Low Energy)射频 ...

  3. python匿名函数(lambda)

    简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数 当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方 ...

  4. kettle发送邮件

    使用kettle发送邮件是为了更好的监控ETL的加载信息 以下是我通过测试的一个案例 1. JOB示意图 2.邮件发送配置详细信息 2.1地址信息配置 2.2 服务器信息配置 上图中所说的" ...

  5. 一篇文章教你读懂Makefile

    makefile很重要      什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professiona ...

  6. android开发系列之回调函数

    想必对于回调函数大家肯定不陌生,因为这是我们开发里面常用的代码技巧.我也就不废话了,让我们直接来看代码吧! public class TestCallback { public interface I ...

  7. android开发系列之git常用命令

    最近因为跳槽到新公司,然后新公司里面的代码管理工具是gitLab,所以我想在这篇博客里面整理一下git常用的语法. GitLab是利用 Ruby on Rails 一个开源的版本管理系统,实现一个自托 ...

  8. Java动态替换InetAddress中DNS的做法简单分析2

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.i ...

  9. Go字典

    字典(map)是Go语言内置的数据结构,一组键值对的无序集合. 看代码: package main import "fmt" func main() { //使用make申请一个m ...

  10. 66.为什么有时候在ISE软件中,顶层文件不能置顶?

    什么时候回出现顶层文件不能置顶呢?嘿嘿,肯定是工程中有错误啦. 如果你的顶层文件包含了include文件,这个时候就会出现这种情况了.但好像出现在刚新建工程的时候,因为当顶层文件不包括Include文 ...