首页 > 代码库 > 苹果开发——App内购以及验证store的收据(二)

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

原地址: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信息
苹果开发——App内购以及验证store的收据(二) - 过☆客 - 过☆客
 
苹果开发——App内购以及验证store的收据(二) - 过☆客 - 过☆客
 
苹果开发——App内购以及验证store的收据(二) - 过☆客 - 过☆客
 
苹果开发——App内购以及验证store的收据(二) - 过☆客 - 过☆客

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 程序的版本号
 
参考文献:
http://www.himigame.com/iphone-cocos2d/550.html
http://www.cocoachina.com/bbs/read.php?tid=87662
http://blog.csdn.net/zu12jing/article/details/5880654
 
附件为本文测试程序,直接改名为zip解压即可(因网易不支持zip)
 
 
HelloStore.rar(39.45K)   
下载