zoukankan      html  css  js  c++  java
  • iOS 关于内购

    最近项目的第三方支付导致项目被拒,记录一下关于内购

      1 #import <StoreKit/StoreKit.h>
      2 
      3 //沙盒测试环境验证
      4 #define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
      5 //正式环境验证
      6 #define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
      7 
      8 #define ProductID_GPSTC01  @"xmws_tc1"  //xmws_tc1是在itunes上注册的物品,这个要保持一致
      9 #define ProductID_GPSTC02   @"xmws_tc2"  
     10 #define ProductID_GPSTC03    @"xmws_tc3" 
     11 
     12 @interface ViewController ()<SKPaymentTransactionObserver>
     13 
     14 - (void)viewDidLoad {
     15     [super viewDidLoad];
     16     [self createViews];
     17     // 01监听购买结果
     18     [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
     19 }
     20 
     21 - (void)dealloc
     22 {
     23     [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
     24 }
     25 
     26 - (void)createViews
     27 {
     28     NSArray * buttonNames = @[@"GPS套餐01", @"GPS套餐02", @"GPS套餐03"];
     29     __weak typeof(self) weakSelf = self;
     30     [buttonNames enumerateObjectsUsingBlock:^(NSString * buttonName, NSUInteger idx, BOOL * stop) {
     31         UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem];
     32         [weakSelf.view addSubview:button];
     33         button.frame = CGRectMake(100, 100 + idx   * 60, 150, 50);
     34         button.titleLabel.font = [UIFont systemFontOfSize:18];
     35         [button setTitle:buttonName forState:UIControlStateNormal];
     36         
     37         // 设置tag值
     38         button.tag = 1000 + idx;
     39         [button addTarget:self action:@selector(buyProduct:) forControlEvents:UIControlEventTouchUpInside];
     40     }];
     41 }
     42 
     43 
     44 - (void)buyProduct:(UIButton *) sender
     45 {
     46     self.buyType = sender.tag - 1000;
     47     if ([SKPaymentQueue canMakePayments]) {
     48         // 02判断是否允许程序内付费购买
     49         [self requestProductData];
     50         NSLog(@"允许程序内付费购买");
     51     }
     52     else
     53     {
     54         NSLog(@"不允许程序内付费购买");
     55         UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"提示"
     56                                                             message:@"您的手机没有打开程序内付费购买"
     57                                                            delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
     58         [alerView show];
     59         
     60     }
     61 }
     62 
     63 
     64 // 03查询。下面的ProductId应该是事先在itunesConnect中添加好的,已存在的付费项目。否则查询会失败。
     65 - (void)requestProductData {
     66     NSLog(@"---------请求对应的产品信息------------");
     67     NSArray *product = nil;
     68     switch (self.buyType) {
     69         case 0:
     70             product = [NSArray arrayWithObject:ProductID_GPSTC01];
     71             break;
     72         case 1:
     73             product = [NSArray arrayWithObject:ProductID_GPSTC02];
     74             break;
     75         case 2:
     76             product = [NSArray arrayWithObject:ProductID_GPSTC03];
     77             break;
     78     }
     79     NSSet *nsset = [NSSet setWithArray:product];
     80     SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
     81     request.delegate = self;
     82     [request start];
     83 }
     84 
     85 #pragma mark - SKProductsRequestDelegate
     86 // 05收到的产品信息回调
     87 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
     88     
     89     NSArray *myProduct = response.products;
     90     NSLog(@"----收到产品反馈信息%@-----",myProduct);
     91     if (myProduct.count == 0) {
     92         NSLog(@"无法获取产品信息,购买失败。");
     93         return;
     94     }
     95     NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
     96     NSLog(@"产品付费数量: %d", (int)[myProduct count]);
     97     // populate UI
     98     for(SKProduct *product in myProduct){
     99         NSLog(@"product info");
    100         NSLog(@"SKProduct 描述信息%@", [product description]);
    101         NSLog(@"产品标题 %@" , product.localizedTitle);
    102         NSLog(@"产品描述信息: %@" , product.localizedDescription);
    103         NSLog(@"价格: %@" , product.price);
    104         NSLog(@"Product id: %@" , product.productIdentifier);
    105     }
    106     SKPayment * payment = [SKPayment paymentWithProduct:myProduct[0]];
    107     NSLog(@"---------发送购买请求------------");
    108     [[SKPaymentQueue defaultQueue] addPayment:payment];
    109     
    110 }
    111 
    112 //弹出错误信息
    113 - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    114     NSLog(@"-------弹出错误信息----------");
    115     UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]
    116                                                        delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];
    117     [alerView show];
    118     
    119 }
    120 
    121 -(void) requestDidFinish:(SKRequest *)request
    122 {
    123     NSLog(@"----------反馈信息结束--------------");
    124     
    125 }
    126 
    127 #pragma mark - SKPaymentTransactionObserver
    128 // 处理交易结果
    129 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    130     for (SKPaymentTransaction *transaction in transactions)
    131     {
    132         switch (transaction.transactionState)
    133         {
    134             case SKPaymentTransactionStatePurchased://交易完成
    135                 NSLog(@"transactionIdentifier = %@", transaction.transactionIdentifier);
    136                 [self completeTransaction:transaction];
    137                 break;
    138             case SKPaymentTransactionStateFailed://交易失败
    139                 [self failedTransaction:transaction];
    140                 break;
    141             case SKPaymentTransactionStateRestored://已经购买过该商品
    142                 [self restoreTransaction:transaction];
    143                 break;
    144             case SKPaymentTransactionStatePurchasing:      //商品添加进列表
    145                 NSLog(@"商品添加进列表");
    146                 break;
    147             default:
    148                 break;
    149         }
    150     }
    151     
    152 }
    153 
    154 // 交易完成
    155 - (void)completeTransaction:(SKPaymentTransaction *)transaction {
    156     NSString * productIdentifier = transaction.payment.productIdentifier;
    157     //    NSString * receipt = [transaction.transactionReceipt base64EncodedString];
    158     if ([productIdentifier length] > 0) {
    159         // 向自己的服务器验证购买凭证
    160         [self verifyPurchaseWithPaymentTransaction];
    161     }
    162     
    163     // Remove the transaction from the payment queue.
    164     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    165     
    166 }
    167 
    168 // 交易失败
    169 - (void)failedTransaction:(SKPaymentTransaction *)transaction {
    170     if(transaction.error.code != SKErrorPaymentCancelled) {
    171         NSLog(@"购买失败");
    172     } else {
    173         NSLog(@"用户取消交易");
    174     }
    175     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    176 }
    177 
    178 /**
    179  *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
    180  *
    181  */
    182 -(void)verifyPurchaseWithPaymentTransaction{
    183     //从沙盒中获取交易凭证并且拼接成请求体数据
    184     NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
    185     NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];
    186 
    187     NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串
    188 
    189     NSString *bodyString = [NSString stringWithFormat:@"{"receipt-data" : "%@"}", receiptString];//拼接请求数据
    190     NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
    191 
    192 
    193     //创建请求到苹果官方进行购买验证
    194     NSURL *url=[NSURL URLWithString:SANDBOX];
    195     NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
    196     requestM.HTTPBody=bodyData;
    197     requestM.HTTPMethod=@"POST";
    198     //创建连接并发送同步请求
    199     NSError *error=nil;
    200     NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
    201     if (error) {
    202         NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
    203         return;
    204     }
    205     NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
    206     NSLog(@"苹果返回的购买凭证 == %@",dic);  //苹果返回的购买凭证
    207     if([dic[@"status"] intValue]==0){
    208         NSLog(@"购买成功!");
    209         NSDictionary *dicReceipt= dic[@"receipt"];
    210         NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
    211         NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识
    212         //如果是消耗品则记录购买数量,非消耗品则记录是否购买过
    213         NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
    214 //        if ([productIdentifier isEqualToString:ProductID_IAP_XYJ]) {
    215 //            int purchasedCount = [defaults integerForKey:productIdentifier];//已购买数量
    216 //            [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
    217 //        }else{
    218             [defaults setBool:YES forKey:productIdentifier];
    219 //        }
    220         //在此处对购买记录进行存储,可以存储到开发商的服务器端
    221     }else{
    222         NSLog(@"购买失败,未通过验证!");
    223     }
    224 }
    225 
    226 // 已购商品
    227 - (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    228     // 对于已购商品,处理恢复购买的逻辑
    229     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    230 }

    补充:01-测试时一定要在真机上测试,并且要先去设置中退出当前的appleID账号

    02-沙盒的测试账号是没有金额限制的,可以无数次测试,刚开始的时候还担心会扣我的钱,实测不会的,请放心测试。

    03-提交审核的时候不需要提供沙盒技术测试账号的,苹果有自己的测试账号。

  • 相关阅读:
    IO多路复用 IO异步
    你没听说过的协程
    事件驱动和IO操作
    堡垒机前戏——paramiko
    听说过的多进程,多线程到底是什么鬼
    socket套接字
    看见就烦的异常
    struts2值栈内部数据结构详解
    hibernate一级缓存的源码初窥
    使用自定义标签模拟jstl的<c:for each>标签
  • 原文地址:https://www.cnblogs.com/somethingWithiOS/p/6688042.html
Copyright © 2011-2022 走看看