最近在更新版本,发现好多用户来进行应用内的服务退款,理由频多,但是还不能确定用户是否支付成功,所以看了一下苹果内购的官方文档,发现有验证服务器的方法,于是就加上了。
具体内购怎么做在这里就不说了,网上有详细的讲解。
首先内购有几种类别呢,这个一定要搞清楚,否则会遇到很多问题。在游戏中我们经常用到的主要由分两种:
非消耗品 (Nonconsumable)买了就有,头衔,功能
指的是在游戏中一次性购买并拥有永久访问权的物品或服务。非消耗品物品可以被用户再次下载,并且能够在用户的所有设备上使用
消耗品 (Consumable),买了就用,用了就没
专为支持可消耗的物品或服务设计的,消耗品购买不可被再次下载,根据其特点,消耗品不能在用户的设备之间跨设备使用,除非自定义服务在用户的账号之间共享这些信息。
接下来我们就开始应用内购,需要导入StoreKit框架。定义好的商品
#define kIAPBomb @ "airplay.10bombs"
#define kIAPBullet @"airplay.laserBullet"
1. 实例化请求时,必须指定有效的identifiers集合,之所以如此处理,主要是为了确保提交的内购商品真的通过了苹果的审批,处于可用状态!
2. 要想获取到准确的可用产品集合,需要通过代理方法实现
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
3. 越狱用户无法测试内购,但是可以购买
1@interfaceITViewController() <SKProductsRequestDelegate,SKPaymentTransactionObserver>
2{
3// 产品字典
4NSMutableDictionary*_productDict;
5}
1-(void)viewDidLoad
2{
3[super viewDidLoad];
4
5[self requestProducts];
6
7// 设置购买队列的监听器
8[[SKPaymentQueue defaultQueue]addTransactionObserver:self];
9}
3.4.询问苹果的服务器能够销售哪些商品
#pragma mark 询问苹果的服务器能够销售哪些商品
- (void)requestProducts
{
// 能够销售的商品
NSSet*set=[[NSSetalloc] initWithObjects:kIAPBomb, kIAPBullet,nil];
// "异步"询问苹果能否销售
SKProductsRequest*request = [[SKProductsRequestalloc] initWithProductIdentifiers:set];
request.delegate=self;
// 启动请求
[request start];
}
3.5.获取询问结果,成功采取操作把商品加入可售商品字典里
- (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProductsResponse*)response
{
if(_productDict ==nil) {
_productDict=[NSMutableDictionarydictionaryWithCapacity:response.products.count];
}
for(SKProduct*productinresponse.products) {
// 激活了对应的销售操作按钮,相当于商店的商品上架允许销售
NSLog(@"%@", product.productIdentifier);
if([product.productIdentifier isEqualToString:kIAPBullet]) {
_bulletButton.enabled=YES;
}
if([product.productIdentifier isEqualToString:kIAPBomb]) {
_bombButton.enabled=YES;
}
// 填充商品字典
[_productDict setObject:product forKey:product.productIdentifier];
}
}
3.6.用户决定购买商品
1#pragma mark - 用户决定购买商品
2- (void)buyProduct:(SKProduct*)product
3{
4// 要购买产品(店员给用户开了个小票)
5SKPayment*payment =[SKPaymentpaymentWithProduct:product];
6
7//// 设置购买队列的监听器
8//[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
9
10// 去收银台排队,准备购买(异步网络)
11[[SKPaymentQueuedefaultQueue] addPayment:payment];
12}
1-(IBAction)purchaseProducts
2{
3[self buyProduct:_productDict[kIAPBullet]];
4}
5
6-(IBAction)purchaseBomb:(id)sender
7{
8[self buyProduct:_productDict[kIAPBomb]];
9}
3.7.判断购买状态是否成功
#pragma mark - SKPaymentTransaction Observer
#pragma mark 购买队列状态变化
- (void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*)transactions
{
// 调试
for(SKPaymentTransaction*transactionintransactions) {
NSLog(@"队列状态变化 %@", transaction);
// 如果小票状态是购买完成
if(SKPaymentTransactionStatePurchased==transaction.transactionState) {
NSLog(@"购买完成 %@", transaction.payment.productIdentifier);
// 更新界面或者数据,把用户购买得商品交给用户
// ...
// 验证购买凭据
[selfverifyPruchase];
// 将交易从交易队列中删除
[[SKPaymentQueuedefaultQueue] finishTransaction:transaction];
}elseif(SKPaymentTransactionStateRestored==transaction.transactionState) {
NSLog(@"恢复成功 %@", transaction.payment.productIdentifier);
// 更新界面或者数据,把用户购买得商品交给用户
// ...
// 将交易从交易队列中删除
[[SKPaymentQueuedefaultQueue] finishTransaction:transaction];
}
}
}
3.8.给用户提供恢复功能(因为在不同设备上永久性商品可能会出现需要恢复购买的情况)
#pragmamark-恢复商品
-(void)restorePurchase
{
// 恢复已经完成的所有交易.(仅限永久有效商品)
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
}
3.9.验证购买(防止第三方插件漏洞)iOS7新特性
提示:虽然苹果在iOS7提升了购买凭据的安全性,但是处于金钱考虑,购买完成后,一定要做凭据的验证工作。
#pragma mark 验证购买凭据
- (void)verifyPruchase
{
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL*receiptURL =[[NSBundlemainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData*receiptData =[NSDatadataWithContentsOfURL:receiptURL];
// 发送网络POST请求,对购买凭据进行验证
NSURL*url =[NSURLURLWithString:ITMS_SANDBOX_VERIFY_RECEIPT_URL];
// 国内访问苹果服务器比较慢,timeoutInterval需要长一点
13NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicytimeoutInterval:10.0f];
request.HTTPMethod =@"POST";
// 在网络中传输数据,大多情况下是传输的字符串而不是二进制数据
/ 传输的是BASE64编码的字符串
/**
BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
BASE64是可以编码和解码的
*/
NSString*encodeStr =[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString*payload = [NSStringstringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
NSData*payloadData =[payload dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody =payloadData;
// 提交验证请求,并获得官方的验证JSON结果
NSData*result =[NSURLConnectionsendSynchronousRequest:request returningResponse:nilerror:nil];
// 官方验证结果为空
if(result ==nil) {
NSLog(@"验证失败");
}
NSDictionary*dict =[NSJSONSerializationJSONObjectWithData:result options:NSJSONReadingAllowFragmentserror:nil];
NSLog(@"%@", dict);
if(dict !=nil) {
// 比对字典中以下信息基本上可以保证数据安全
// bundle_id&application_version&product_id&transaction_id
NSLog(@"验证成功");
}
}
3.9.说说整个购买流程结构
1.苹果APP(商家) ——— 2.告诉苹果Store服务器要卖的商品 ——— 3.苹果审核完(告诉你是否可以卖)
4.用户(买商品) ——— 5.苹果APP(商家) ——— 6.开发票给(用户) ————
7.用户(拿着发票去苹果Store服务器付款) —— 8.付款成功(用户在APP里获得服务商品)
(注意:如果要模拟测试内购,需要用真机才可以测试)