zoukankan      html  css  js  c++  java
  • 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
    
  • 相关阅读:
    取球游戏
    初来乍到
    大臣的旅费
    【转载】.NET Core微服务架构学习与实践系列文章索引目录
    【转载】直接拿来用,最火的.NET开源项目
    C# For Demo
    【转载】快速序列化组件MessagePack介绍
    【转载】C# 网上收集的一些所谓的开源项目
    【转载】为了拿捏 Redis 数据结构,我画了 40 张图(完整版)
    【转载】Identity Server 4 从入门到落地(七)—— 控制台客户端
  • 原文地址:https://www.cnblogs.com/damiao/p/4394610.html
Copyright © 2011-2022 走看看