iOS 应用内购买(In-App Purchase)之开发

iOS 应用内购买(In-App Purchase)之协议、税务和银行业务

 使用IAP之前,需要签订协议,查看上面的链接。 



IAP开发

添加App内购项目

登录 iTunes Connect ,选择我的app,点击要使用IAP的应用。

iOS 应用内购买(In-App Purchase)之开发_第1张图片



添加app内购买项目,这里有几种类型可选,根据你的产品选择需要的类型,比如Q币是消耗类型,QQ会员是非续订订阅等等。

iOS 应用内购买(In-App Purchase)之开发_第2张图片


iOS 应用内购买(In-App Purchase)之开发_第3张图片


iOS 应用内购买(In-App Purchase)之开发_第4张图片


虚拟货币/充值模式引入

之所以要引入虚拟货币或充值,有两个重要的原因:

1. 每一个商品都需要在 ITunes Connect 中创建一个对应的内购项目,而内购项目还需要提交审核,考虑到商品后期增删改,这是一个坑。

2. 苹果的内购项目的定价是固定的,参看这里,在列表中选择一个作为售价,这样就不一定契合我们实际商品定价,也不方便我们对商品改价促销之类。

iOS 应用内购买(In-App Purchase)之开发_第5张图片


引入充值或虚拟货币则可以解决这个问题

以虚拟货币(金币)为例,流程大致如下:

应用内购商品提供 100金币,200金币,500金币,1000金币4中类型的付费购买。

用户付费购买对应的商品(金币),用户账户里增加对应的金币数量,用户再使用金币购买应用内的实际商品(比如A课程,耗费288金币)

这样一来,ITunes Connect  中的内购项目只需要创建一次,而实际商品(课程)也可以按需增删改。


IPA购买流程

1. IOS客户端发起购买请求,购买指定产品ID对应的商品。

2. 支付成功/失败/取消后,苹果服务器返回支付结果,支付成功返回 receipt 。

3. IOS客户端获取到 receipt 后,提交到应用服务器(我们自己的服务器)验证。

4. 应用服务器将验证结果发给客户端。


IOS代码部分

// 这里产品id就是上面添加的内购项目产品id
static NSString *productID = @"100RMB";

#import 
@interface TestViewControler() 
....

- (void)viewDidLoad {
    // 添加购买监听
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void) dealloc {
    // 移除监听
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

....

#pragma mark - 苹果 IAP 支付

// 开始 IAP 流程
- (void) startIAPPay {
    // 检测是否允许内购
    if([SKPaymentQueue canMakePayments]){
        
        [self.navigationController.view makeToastActivity:CSToastPositionCenter];
        self.navigationController.view.userInteractionEnabled = NO;
        [btnBottom setTitle:NSLocalizedString(@"请稍等...", nil) forState:UIControlStateNormal];
        btnBottom.userInteractionEnabled = NO;
        
        NSSet *nsset = [NSSet setWithArray:@[productID]];
        
        // 请求商品
        SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
        request.delegate = self;
        [request start];
        
    }else{
        [[[UIAlertView alloc]initWithTitle:nil message:NSLocalizedString(@"用户禁止应用内付费购买", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"确定", nil) otherButtonTitles:nil, nil] show];
    }
}

//收到产品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    if(response.products.count == 0){ // 无法获取产品信息,购买失败
        [self.navigationController.view hideToastActivity];
        self.navigationController.view.userInteractionEnabled = YES;
        
        [[[UIAlertView alloc]initWithTitle:nil message:NSLocalizedString(@"无法获取产品信息,请重试", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"确定", nil) otherButtonTitles:nil, nil] show];
        return;
    }
    
    SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:response.products[0]];
    payment.quantity = (NSInteger)_coursesDetailInfo.price;//购买次数=价钱
    if (payment.quantity == 0) {
        payment.quantity = 1;
    }
    payment.applicationUsername = [NSString stringWithFormat:@"%ld", self.orderId];
    
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    NSLog(@"商品信息请求错误:%@", error); 
    
    [self.navigationController.view hideToastActivity];
    self.navigationController.view.userInteractionEnabled = YES;
    [btnBottom setTitle:NSLocalizedString(@"立即支付", nil) forState:UIControlStateNormal];
    btnBottom.userInteractionEnabled = YES;
    
    [[[UIAlertView alloc]initWithTitle:nil message:NSLocalizedString(@"无法获取产品信息,请重试", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"确定", nil) otherButtonTitles:nil, nil] show];
    
}

- (void)requestDidFinish:(SKRequest *)request {
//    [self.navigationController.view hideToastActivity];
//    self.navigationController.view.userInteractionEnabled = YES;
}

//监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction {
    
    for(SKPaymentTransaction *tran in transaction){
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:
                NSLog(@"交易完成");
                [self completeTransaction:tran];
                
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品添加进列表");
                
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"已经购买过商品");
                [self restoreTransaction:tran];
                
                break;
            case SKPaymentTransactionStateFailed:
                NSLog(@"交易失败");
                [self failedTransaction:tran];
                
                break;
            default:

                break;
        }
    }
}

// 交易完成
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"交易完成,走验证通道");
    NSLog(@"transaction=%@", transaction);
    
    
    NSString *productIdentifier = transaction.payment.productIdentifier;
    NSLog(@"productIdentifier=%@", productIdentifier);
    
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
    NSString *strReceipt = [receipt base64EncodedStringWithOptions:0];
    
    NSLog(@"strReceipt=%@", strReceipt);
    
    NSString *orderId = transaction.payment.applicationUsername;
    NSLog(@"orderId=%@", orderId);
    

    if ([productIdentifier length] > 0) {
        // 向自己的服务器验证购买凭证
    }
    
    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    
    [self.navigationController.view hideToastActivity];
    self.navigationController.view.userInteractionEnabled = YES;
    
    [self payCompleted];
    
}

// 交易失败
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    
    [self.navigationController.view hideToastActivity];
    self.navigationController.view.userInteractionEnabled = YES;

    [btnBottom setTitle:NSLocalizedString(@"立即支付", nil) forState:UIControlStateNormal];
    btnBottom.userInteractionEnabled = YES;
    
}

// 已经购买过该商品
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    // 对于已购商品,处理恢复购买的逻辑
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    
    [self.navigationController.view hideToastActivity];
    self.navigationController.view.userInteractionEnabled = YES;
    
    [btnBottom setTitle:NSLocalizedString(@"立即支付", nil) forState:UIControlStateNormal];
    btnBottom.userInteractionEnabled = YES;
}



服务器验证

验证方法查看这里 

测试服务器地址:https://sandbox.itunes.apple.com/verifyReceipt

正式服务器地址:https://buy.itunes.apple.com/verifyReceipt 

验证时先向正式环境发起,如果返回码是 21007,说明是测试购买,再转向测试环境验证。


测试购买

进入 ITunes Connect 》用户和职能 》添加 沙箱技术测试员,然后就可以用这个测试账号发起购买了。

iOS 应用内购买(In-App Purchase)之开发_第6张图片



其它

记得开启 Capabilities > In-App Purchase 

iOS 应用内购买(In-App Purchase)之开发_第7张图片












你可能感兴趣的:(ios)