我上一篇博客《为iOS应用/游戏内建购买项目(IAP)》对于CP有自己游戏服务器的从安全性来讲并不适用,有自己服务器则需要进一步对数据进行校验。
如果CP具有自己的游戏服务器, Apple建议在服务器端存储产品ID,而不要将其存储在客户端本地。 这样就可以在不升级程序的前提下添加新的产品,这只需要我们向客户端发送哪个产品的ID。
如果要申请商品ID,可参考《为iOS应用/游戏内建购买项目(IAP)》
更多详细流程可参考这篇文章:《Store Kit Guide(In App Purchase)翻译》
IAP充值数据校验流程如图:
而以下代码就是要实现上图数据校验流程的:
//
// ViewController.m
//
//
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
#pragma mark 数据初始化
- (void)viewDidLoad {
[super viewDidLoad];
//1
[self setOnListner];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(100, 300, self.view.bounds.size.width/2, 50)];
[btn setTitle:@"buy" forState:UIControlStateNormal];
btn.backgroundColor = [UIColor purpleColor];
[btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:btn];
}
//1,对交易进行监听和设置可购买
-(void)setOnListner{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
NSString * des;
if([SKPaymentQueue canMakePayments]){
des = @"可购买内置项目!";
}else{
des = @"不可购买内置项目!";
}
NSLog(@"%@",des);
}
-(void)click{
[self reqBuyData];
}
//2,请求商品信息
-(void)reqBuyData{
// 所注册的商品ID数组
NSArray * arr = [[NSArray alloc] initWithObjects:@"com.xx.gold100",@"com.xx.gold200", nil];
NSSet *set = [NSSet setWithArray:arr];
SKProductsRequest * req = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
req.delegate = self;
[req start];
}
#pragma mark SKProductsRequestDelegate代理实现
//3, 得到商品信息,并自动购买第一个
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray * myProducts = response.products;
//如果是无效的商品将会出现在这里,如AppleID账号没完善银行信息之类也会导致
NSArray * dArr = response.invalidProductIdentifiers;
NSLog(@"接受到产品返回信息%lu", (unsigned long)myProducts.count);
NSLog(@"接受到无效的产品返回信息%lu", (unsigned long)dArr.count);
if(myProducts.count < 1){
NSLog(@"无有效商品信息!");
return;
}
SKProduct * buyProduct;
for (SKProduct * product in myProducts) {
NSLog(@"---pay : %@", product.localizedDescription);
NSLog(@"product title:%@", product.localizedTitle);
NSLog(@"price:%@", product.price);
buyProduct = product;
}
//这里我直接对最后一个商品进行下单购买,
// 游戏中应该把商品列表列出来给玩家选择,然后把要买的add到[SKPaymentQueue defaultQueue],到add这里开始发送购买请求
SKPayment * pay = [SKPayment paymentWithProduct:buyProduct];
[[SKPaymentQueue defaultQueue] addPayment:pay];
}
//SKProductsRequestDelegate代理实现,请求商品有错log
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"---------error log----%@", error.localizedDescription);
}
//SKProductsRequestDelegate代理实现
-(void)requestDidFinish:(SKRequest *)request{
NSLog(@"信息请求结束");
}
#pragma mark SKPaymentTransactionObserver代理实现
//4,SKPaymentTransactionObserver代理实现,也就是购买的结果
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
NSString * des = @"";
for(SKPaymentTransaction * trans in transactions){
switch (trans.transactionState) {
case SKPaymentTransactionStatePurchased:
des = @"客户端本地购买完成!将向服务器验证数据!";
//购买成功,那么将要向cp游戏服务器发送订单,cp游戏服务器再去通过iap服务器验证数据,再返回结果到客户端
//5,
[self completeTransaction:trans];
//pop出用过的交易
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStateFailed:
des = @"购买失败!";
[self failedTransaction:trans];
//pop出用过的交易
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStateRestored:
des = @"购买重复!";
//pop出用过的交易
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
break;
case SKPaymentTransactionStatePurchasing:
//des = @"购买ing!";
break;
default:
break;
}
UIAlertView * a = [[UIAlertView alloc] initWithTitle:@"result" message:des delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
if (![des isEqual: @""]) {
[a show];
}
}
}
#pragma mark 自定义的函数,由本地购买成功后验证数据和打印信息用
//5,购买成功后调用,
-(void)completeTransaction:(SKPaymentTransaction*) transaction{
//模拟向游戏服务器验证数据
[self verifyTransaction:transaction];
}
//6,
//假设以下函数是游戏服务器的
//这里应该交给游戏服务器去验证,然后把结果返回客户端,直接现在这里模拟游戏服务器吧
-(void)verifyTransaction:(SKPaymentTransaction*) transaction{
//沙盒测试用url
NSString * urlStr = @"https://sandbox.itunes.apple.com/verifyReceipt";
NSURL * url = [NSURL URLWithString:urlStr];
NSMutableURLRequest * req = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:5000];
req.HTTPMethod = @"POST";
//要求Base64编码
NSString * transactionReceipt = [transaction.transactionReceipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString * jsonStr = [NSString stringWithFormat:@"{\"receipt-data\":\"%@\"}", transactionReceipt];
NSData * jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
[req setHTTPBody:jsonData];
NSData * result = [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];
//这个验证函数里应该是服务器的,得到下面结果后返回给本客户端就OK啦
if (result == nil) {
NSLog(@"验证到有误数据");
}else{
NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
/*应该返回的json
receipt = {
.......
};
status = 0;
*/
//status == 0 才算成功
if (dic!= nil && [dic[@"status"] == 0]) {
NSLog(@"数据验证成功!购买成功!");
NSLog(@"返回数据:");
NSLog(@"%@",dic);
}else{
NSLog(@"验证到有误数据");
if (dic != nil) {
NSLog(@"%@", dic);
}
}
}
}
//给交易失败后调用
-(void)failedTransaction:(SKPaymentTransaction*)transaction{
NSString * errorDes;
NSInteger errorCode = transaction.error.code;
NSLog(@"payment error ; %@", transaction.error.localizedDescription);
switch (errorCode) {
case SKErrorUnknown:
errorDes = @"unknown";
break;
case SKErrorPaymentCancelled:
errorDes = @"payment cancel";
break;
case SKErrorPaymentInvalid:
errorDes = @"payment invalid";
break;
case SKErrorPaymentNotAllowed:
errorDes = @"not allowed";
break;
case SKErrorClientInvalid:
errorDes = @"client invalid";
break;
default:
break;
}
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"title" message:errorDes delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
[alert show];
}
@end
运行就可以得到结果了:
查看打印数据: