IAP内购——精选推荐

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

IAP内购IAPHelper.h
//
// IAPHelper.h
// airplay
//
// Created by apple on 13-10-23.
// Copyright (c) 2013年 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef void (^myBlock)();
typedef void(^buyCompletionBlock)(NSString *identifier);
typedef void(^restoreCompletionBlock)(NSArray *products);
typedef void(^failedBlock)(NSString *reason);
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
@interface IAPHelper : NSObject {
NSUserDefaults *defaults;
}
/**
包装后的IAPHelper的使⽤⽅法
1. 调⽤requestProducts去服务器验证可⽤的商品列表
2. 调⽤buyProduct⽅法,传⼊要购买的产品标⽰,并在completion块代码中做后续处理即可
3. 调⽤restorePurchase⽅法,并在completion块代码中做后续处理即可
所谓后续处理,就是根据购买情况,调整界⾯UI或者设置⽤户属性
提⽰:在使⽤IAPHelper的同时,需要导⼊Base64的两个分类⽅法。

*/
@property(nonatomic,assign)int money;
//充值的⾦额
@property(strong,nonatomic)myBlock block;
+ (IAPHelper *)sharedIAPHelper;
#pragma mark 请求有效产品(使⽤⾃定义的产品集合去iTunes服务器确认哪些商品可以销售)
- (void)requestProducts:(NSSet *)products;
#pragma mark 购买商品(使⽤指定的产品标⽰符购买商品)
- (void)buyProduct:(NSString *)identifier
completion:(buyCompletionBlock)completion
failed:(failedBlock)failed;
#pragma mark 恢复购买(仅针对⾮消耗品可⽤)
- (void)restorePurchase:(restoreCompletionBlock)completion
failed:(failedBlock)failed;
- (void)buyProduct:(NSString *)identifier;
@end
IAPHelper.m
//
// IAPHelper.m
// airplay
//
// Created by apple on 13-10-23.
// Copyright (c) 2013年 itcast. All rights reserved.
//
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
#import "NSData+Base64.h"
static IAPHelper *sharedInstance;
/**
为了防⽌越狱⼿机插件的拦截,在完成购买之后,需要做购买的验证!
*/
// ⽤来真机验证的服务器地址
#define ITMS_PROD_VERIFY_RECEIPT_URL @"https:///verifyReceipt"
// 开发时模拟器使⽤的验证服务器地址
#define ITMS_SANDBOX_VERIFY_RECEIPT_URL @"https:///verifyReceipt" @interface IAPHelper() <SKProductsRequestDelegate, SKPaymentTransactionObserver>
{
// 从服务器返回的有效商品字典,以备⽤户购买是使⽤
NSMutableDictionary *_productDict;
// 回调块代码
buyCompletionBlock _buyCompletion;
restoreCompletionBlock _restoreCompletion;
failedBlock _failedBlock;
}
@end
@implementation IAPHelper
#pragma mark - 单例⽅法
+ (id)allocWithZone:(NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
// 为共享实例添加交易观察者对象
[[SKPaymentQueue defaultQueue]addTransactionObserver:sharedInstance];
});
return sharedInstance;
}
+ (IAPHelper *)sharedIAPHelper
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[IAPHelper alloc]init];
});
return sharedInstance;
}
#pragma mark - 内购⽅法
#pragma mark 请求有效产品(使⽤⾃定义的产品集合去iTunes服务器确认哪些商品可以销售)
- (void)requestProducts:(NSSet *)products
{
// 实例化请求
SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:products];
//NSLog(@"%@",products);
// 设置代理
[request setDelegate:self];
// 启动请求
[request start];
}
#pragma mark请求错误信息
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"请求错误信息: %@",error);
}
#pragma mark请求成功
-(void)requestDidFinish:(SKRequest *)request{
//[activityView stopAnimating];
NSLog(@"success request = %@",request);
}
#pragma mark 购买商品(使⽤指定的产品标⽰符购买商品)
- (void)buyProduct:(NSString *)identifier
// completion:(buyCompletionBlock)completion
// failed:(failedBlock)failed
{
// 记录回调块代码
// _buyCompletion = completion;
// _failedBlock = failed;
// 从商品字典中提取商品对象,如果有才购买
// 如果没有,提⽰⽤户
SKProduct *product = _productDict[identifier];
if (product) {
// 购买
// 1. 实例化付款对象
SKPayment *payment = [SKPayment paymentWithProduct:product];
// 2. 将付款对象添加到付款队列,付款就启动,将购买请求提交给iTunes服务器,等待服务器的相应
[[SKPaymentQueue defaultQueue]addPayment:payment];
} else {
// 这种情况会在定义了购买商品,但是苹果没有审批通过,或者苹果服务器不可⽤时出现
NSLog(@"当前商品不可购买,请稍后再试");
UIAlertView *alterview = [[UIAlertView alloc] initWithTitle:@"充值失败!请稍后再试!"
message:nil
delegate:self
cancelButtonTitle:nil
otherButtonTitles:@"确定", nil];
[alterview show];
}
}
#pragma mark 验证购买
// 验证购买,在每⼀次购买完成之后,需要对购买的交易进⾏验证
// 所谓验证,是将交易的凭证进⾏"加密",POST请求传递给苹果的服务器,苹果服务器对"加密"数据进⾏验证之后,
// 会返回⼀个json数据,供开发者判断凭据是否有效
// 有些“内购助⼿”同样会拦截验证凭据,返回⼀个伪造的验证结果
// 所以在开发时,对凭据的检验要格外⼩⼼
- (void)verifyPurchase:(SKPaymentTransaction *)transaction
{
// 使⽤base64的加密算法,对凭据进⾏加密处理
// 1. 使⽤base64加密交易凭据
NSString *encodeStr = [transaction.transactionReceipt base64EncodedString];
// 2. 建⽴验证请求
// 1) 测试的URL ITMS_PROD_VERIFY_RECEIPT_URL
NSURL *url = [NSURL URLWithString:ITMS_PROD_VERIFY_RECEIPT_URL];
// 2) 建⽴请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f]; // 1> 请求数据体
NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
// 2> 设置数据体
[request setHTTPBody:payloadData];
// 3> 设置请求⽅法
[request setHTTPMethod:@"POST"];
// 3) 建⽴连接,发送同步请求!
// 不能发送异步请求!后续还要对服务器返回结果做进⼀步的确认,以保证⽤户真的是在购买!
// 所谓真的购买,不是插件模拟的校验数据
NSURLResponse *response = nil;
// 此请求返回的是⼀个json结果
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
// 将数据反序列化为数据字典
if (data == nil) {
return;
}
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
if (jsonDict != nil) {
[[NSNotificationCenter defaultCenter]postNotificationName:KJOINMEMBERNOTIFICATIONCENTER object:nil];
}
// 针对服务器返回数据进⾏校验
// 通常需要校验:bid,product_id,purchase_date,status
// if ([jsonDict[@"status"]integerValue] == 0) {
// _buyCompletion(transaction.payment.productIdentifier);
// } else {
// _buyCompletion(@"验证失败,检查你的机器是否越狱");
// }
}
#pragma mark 恢复购买(仅针对⾮消耗品可⽤)
// 恢复购买的应⽤场景
// 1) ⽤户在其他设备上恢复⾮消耗品的购买
// 2) ⽤户的⼿机恢复出⼚设置,或者重新安装软件之后,可以使⽤恢复购买
// 提⽰:恢复购买本质上和采购⾮常像,对于⾮消耗品⽽⾔,即便是再次采购,也不会让⽤户付费
// 恢复购买相对更加⼈性化⼀些,因此,在实际开发中,两个按钮⼀个都不能少
// 使⽤恢复购买,可以恢复⽤户已经购买的所有⾮消耗品类型的商品
- (void)restorePurchase:(restoreCompletionBlock)completion
failed:(failedBlock)failed
{
// 记录回调块代码
_restoreCompletion = completion;
_failedBlock = failed;
// 恢复购买的⼯作原理,使⽤⽤户的appleID连接到itunes服务器,检查⽤户曾经购买的所有商品
// 将商品集合返回给⽤户
[[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
}
#pragma mark SKProductsRequest Delegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
// 懒加载产品字典
if (_productDict == nil) {
_productDict = [NSMutableDictionary dictionaryWithCapacity:response.products.count];
} else {
[_productDict removeAllObjects];
}
NSLog(@"有效的产品列表 %@",response.products);
NSLog(@"⽆效的商品:%@",response.invalidProductIdentifiers);
// 遍历服务器返回的产品列表
for (SKProduct *product in response.products) {
// 输出有效产品(当前可以购买的产品)唯⼀标⽰符
// NSLog(@"////%@", product.productIdentifier);
// 需要记录服务器返回的有效商品,以便后续的购买
// 提⽰:不要直接使⽤⾃定义的商品标⽰符开始购买,购买前,⼀定要从服务器查询可⽤商品
// 以免服务器调整或其他原因,⽤户⽆法正常采购,同时造成⾦钱的损失
[_productDict setObject:product forKey:product.productIdentifier];
}
}
#pragma mark - 交易观察者⽅法
// 付款队列中的交易变化的回调⽅法
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
// 针对恢复操作,定义⼀个临时数组
//NSMutableArray *restoreArray = [NSMutableArray arrayWithCapacity:transactions.count];
// 判断是否是恢复的操作
//BOOL isRestore = NO;
for (SKPaymentTransaction *transaction in transactions)
{
//NSLog(@"transaction.State = %@",transaction);
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased://交易完成
break;
case SKPaymentTransactionStateFailed://交易失败
//[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored://已经购买过该商品
// [self restoreTransaction:transaction];
break;
case SKPaymentTransactionStatePurchasing: //商品添加进列表
NSLog(@"商品添加进列表");
break;
default:
break;
}
}
for (SKPaymentTransaction *transction in transactions) {
// 如果交易的状态是购买完成,说明商品购买成功
if (SKPaymentTransactionStatePurchased == transction.transactionState) {
NSLog(@"购买成功 %@", transction.payment.productIdentifier);
// 验证凭据
if (CurrentSystemVersion >= 7) {
[self verifyPruchaseIOS7];
}else {
[self verifyPurchase:transction];
}
//[self verifyFinishedTransaction:transction];
// 通知队列结束交易
[queue finishTransaction:transction];
}
// else if (SKPaymentTransactionStateRestored == transction.transactionState) {
// isRestore = YES;
//
// // 恢复购买
// [restoreArray addObject:transction.payment.productIdentifier];
//
// // 通知队列结束交易
// [queue finishTransaction:transction];
// } else if (SKPaymentTransactionStateFailed == transction.transactionState) {
// // 判断是否因为⽤户点击取消,产⽣的请求失败
// if (SKErrorPaymentCancelled != transction.error.code) {
// // 出错块代码回调,调⽤回调⽅法之前,需要判断回调⽅法是否设置
// // 如此设置之后,可以给回调⽅法设置为nil,否则会报错!
// if (_failedBlock) {
// _failedBlock(transction.error.localizedDescription);
// }
// }
// }
}
// 如果是恢复的交易
// if (isRestore) {
// // 调⽤块代码回传整个恢复的产品标⽰数组
// _restoreCompletion(restoreArray);
// }
}
- (void)verifyPruchaseIOS7 {
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
// 发送⽹络POST请求,对购买凭据进⾏验证
NSURL *url = [NSURL URLWithString:ITMS_PROD_VERIFY_RECEIPT_URL];
// 国内访问苹果服务器⽐较慢,timeoutInterval需要长⼀点
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f]; request.HTTPMethod = @"POST";
// 在⽹络中传输数据,⼤多情况下是传输的字符串⽽不是⼆进制数据
// 传输的是BASE64编码的字符串
/**
BASE64 常⽤的编码⽅案,通常⽤于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
BASE64是可以编码和解码的
*/
NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = payloadData;
// 提交验证请求,并获得官⽅的验证JSON结果
NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// 官⽅验证结果为空
if (result == nil) {
NSLog(@"验证失败");
}
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@", dict);
if (dict != nil) {
// ⽐对字典中以下信息基本上可以保证数据安全
// bundle_id&application_version&product_id&transaction_id
[[NSNotificationCenter defaultCenter]postNotificationName:KJOINMEMBERNOTIFICATIONCENTER object:nil]; NSLog(@"验证成功");
}
}
@end。

相关文档
最新文档