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://buy.itunes.apple.com/verifyReceipt"

// 开发时模拟器使用的验证服务器地址

#define ITMS_SANDBOX_VERIFY_RECEIPT_URL     @"https://sandbox.itunes.apple.com/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

 

你可能感兴趣的:(a)