关闭

iOS开发 app内购In-App Purchase(二)

标签: ios开发app内购app
567人阅读 评论(0) 收藏 举报
分类:

上篇的内购准备工作已完成,接下来就只需集成代码咯~

part 2. 代码

1.在项目中引入“storekit.framework”,并加入头文件#import <StoreKit/StoreKit.h>

2.在.h文件中加入“SKPaymentTransactionObserver ,SKProductsRequestDelegate”监听

//
//  LHYInAppPurchaseViewController.h
//  LHY
//
//  Created by scx on 16/2/28.
//  Copyright © 2016年 scx. All rights reserved.
//

#import "LHYDetailBaseViewController.h"
#import <StoreKit/StoreKit.h>


@interface LHYInAppPurchaseViewController : LHYDetailBaseViewController<SKPaymentTransactionObserver , SKProductsRequestDelegate>

@end

3.在.m文件

@implementation LHYInAppPurchaseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"购买航币";
    
    //添加监听
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    self.productID = @"com.myTest.inappPurchase_prodect1";   
}

// app内购
- (void)toPay:(id)sender
{
    NSLog(@"____app内购 , %@" , self.productID);
    NSString *product = self.productID;
    if([SKPaymentQueue canMakePayments]){
        [self requestProductData:product];
    }else{
        [LHYAlertView showAlert:@"您的手机没有打开程序内付费购买"];
    }
    
}

//请求商品
- (void)requestProductData:(NSString *)type{
    
    NSLog(@"-------------请求对应的产品信息----------------");
    NSArray *product = [[NSArray alloc] initWithObjects:type, nil];
    
    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];
    
}
//收到产品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    NSLog(@"--------------收到产品反馈消息---------------------");
    NSArray *product = response.products;
    if([product count] == 0){
        
        NSLog(@"--------------没有商品------------------");
        return;
    }
    
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
    
    SKProduct *p = nil;
    for (SKProduct *pro in product) {
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);
        if([pro.productIdentifier isEqualToString:self.productID]){
            p = pro;
        }
    }
    
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    
    LHYLog(@"发送购买请求");
    [[SKPaymentQueue defaultQueue] addPayment:payment];

}
//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    
    NSLog(@"------------------错误-----------------:%@", error);
}

- (void)requestDidFinish:(SKRequest *)request{
    NSLog(@"------------反馈信息结束-----------------");
}


//监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
//    [ZZZWaitingView hideInView:LHY_APPDELEGATE.window];
    for(SKPaymentTransaction *tran in transaction){
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:{
                [ZZZWaitingView hideInView:LHY_APPDELEGATE.window];
                LHYLog(@"___%@" , tran.transactionIdentifier )
                [self completeTransaction:tran];
                LHYLog(@"交易完成");

            }
                break;
            case SKPaymentTransactionStatePurchasing:
                LHYLog(@"商品添加进列表");
    
                break;
            case SKPaymentTransactionStateRestored: {
                [ZZZWaitingView hideInView:LHY_APPDELEGATE.window];
                LHYLog(@"已经购买过商品");
                [self restoreTransaction:tran];
            }
                break;
            case SKPaymentTransactionStateFailed: {
                [ZZZWaitingView hideInView:LHY_APPDELEGATE.window];
                LHYLog(@"交易失败");
                [self failedTransaction:tran];
                UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"提示"
                                                                     message:@"购买失败,请重新尝试购买"
                                                                    delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
                
                [alerView2 show];
            }
                break;
            default:
                break;
        }
    }
}


//交易结束
- (void)completeTransaction: (SKPaymentTransaction *)transaction

{
    NSLog(@"-----completeTransaction--------");
   
    //系统IOS7.0以上获取支付验证凭证的方式应该改变,切验证返回的数据结构也不一样了。
    NSString *transactionReceiptString= nil;
    NSURLRequest *appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];
    NSError *error = nil;
    NSData *receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];
    transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    LHYLog(@"-----:%@" , transactionReceiptString);
    
    // NSString *str = [transactionReceiptString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    // 客户端验证
    [self verifyPruchase:transactionReceiptString];
    
    //向自己的服务器验证购买凭证
    [self verifyTransactionReceiptWithTRString:transactionReceiptString transaction:transaction];

    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    
}


当我们的交易完成后还要去appstore 上面去验证票据信息是否正确,这样我们才可以发放道具

关于验证 ---> apple官方文档


验证示例:

- (void)verifyPruchase:(NSString *)receiptStr
 {     
     // 发送网络POST请求,对购买凭据进行验证
     NSURL *url = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
     // 国内访问苹果服务器比较慢,timeoutInterval需要长一点
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
     request.HTTPMethod = @"POST";
     
     // 在网络中传输数据,大多情况下是传输的字符串而不是二进制数据
     // 传输的是BASE64编码的字符串
     /**
      BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
      BASE64是可以编码和解码的
      */
     NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptStr];
     
     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
         NSString *status = LHY_OBJECT_STRING(dict[@"status"]);
         if ([status isEqualToString:@"0"]) {
             NSLog(@"验证成功");
         }else {
             NSLog(@"验证失败");
         }
     }
}


注意事项:

1. 沙盒环境测试app内购流程时,请使用未越狱的设备。

2. 请务必使用真机来测试,结果以真机为准。

3. 项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,否则无法请求到商品信息。

4. 关于验证,苹果提供了两个验证链接,在审核时,苹果使用的是沙盒验证链接,沙盒验证链接,沙盒验证链接(重要的事情说三遍!),千万不要在程序中写死验证链接。

通常在验证时采用 可先验证沙盒,再验证正式 的方式,且最终验证放在服务端来完成。

沙盒验证:https://sandbox.itunes.apple.com/verifyReceipt

正式验证:https://buy.itunes.apple.com/verifyReceipt


- (void) failedTransaction: (SKPaymentTransaction *)transaction{
    NSLog(@"失败");
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        
    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
-(void)paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{
    
}

- (void)restoreTransaction: (SKPaymentTransaction *)transaction
{
    NSLog(@" 交易恢复处理");
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    
}

-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{
    NSLog(@"-------paymentQueue----");
}

- (void)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}


补充:

在项目中测试中有遇到过当还未购买成功,用户就退出了界面的情况,这时客户端已经向苹果发出了购买请求,当再次进入会出现购买问题。

只需检测一下是否有未完成的交易即可。

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;
    if (transactions.count > 0) {
        //检测是否有未完成的交易
        SKPaymentTransaction *transaction = [transactions firstObject];
        LHYToastView *toastView = [[LHYToastView alloc]init];
        if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
            [toastView setupWithText:[NSString stringWithFormat:@"您有一笔未完成的订单"]];
            [self completeTransaction:transaction];
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            
        }
        if (transaction.transactionState == SKPaymentTransactionStatePurchasing) {
            
        }
        if (transaction.transactionState == SKPaymentTransactionStateRestored) {
            [self restoreTransaction:transaction];
        }
        if (transaction.transactionState == SKPaymentTransactionStateFailed) {
            [self failedTransaction:transaction];
        }
        [LHY_APPDELEGATE.window addSubview:toastView];
    }
}

至此,app内购集成大功告成~

文章整理可能有疏漏,欢迎大家补充~

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1550次
    • 积分:77
    • 等级:
    • 排名:千里之外
    • 原创:6篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档