iOS 应用内购(IAP)

一、去App Store Connect协议、税务和银行业务添加银行卡
二、去App Store Connect创建一个应用或者之前App就行
三、在当前要增加内购的App下选择功能选项卡增加内购项目
四、去App Store Connect用户和职能添加沙盒测试账号
五、代码编写、测试、上线

关于步骤 一、二、三、四

直接去开发者后台设置完就行了,都有提示的,一看就明白

几个注意点:
0、添加银行卡的时候建议填写大银行的,小银行的CNAPS Code查不到或者难查,打相应银行客服也可以
1、在创建内购项目的时候信息要填全,否则会显示元数据丢失
2、在创建完之后,别忘了,去当前添加的App版本增加内购项目
3、创建沙盒账号的时候,密码要复杂点,否则会报一些莫名奇妙的错误
4、必须用真机测试
5、测试的时候要退出当前账号,测试App购买的时候弹窗直接选择“使用现有的Apple ID”输入创建的沙盒账号

附上比较好的链接
https://www.jianshu.com/p/f7bff61e0b31

五、示例代码

#import "ViewController.h"
#import 

@interface ViewController () 

@end

@implementation ViewController

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

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

- (IBAction)bugAction:(id)sender {
    
    if ([SKPaymentQueue canMakePayments]) {
        [self getProductInfo];
    } else {
        NSLog(@"不允许程序内付费");
    }
}

- (void)getProductInfo {
    NSSet *set = [NSSet setWithArray:@[@"com.czchat.01"]];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    request.delegate = self;
    [request start];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
    NSArray *myProduct = response.products;
    if (myProduct.count == 0) {
        NSLog(@"无法获取产品信息,购买失败");
        return;
    }
    
    SKProduct *requestProduct = nil;
    for (SKProduct *pro in myProduct) {
        
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);
        
        // 11.如果后台消费条目的ID与我这里需要请求的一样(用于确保订单的正确性)
        if([pro.productIdentifier isEqualToString:@"com.czchat.01"]){
            requestProduct = pro;
        }
    }
    
    
    
    
    SKPayment *payment = [SKPayment paymentWithProduct:requestProduct];
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}
// 请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"请求失败,错误信息%@", [error localizedDescription]);
}

// 反馈请求的产品信息结束后
-(void)requestDidFinish:(SKRequest *)request {
    NSLog(@"反馈信息结束");
}

#pragma mark - SKPaymentTransactionObserver
// 监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品添加进列表");
                break;
            case SKPaymentTransactionStatePurchased:
                NSLog(@"交易完成 transactionIdentifier = %@", transaction.transactionIdentifier);
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                NSLog(@"交易失败");
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"重复购买");
                [self restoreTransaction:transaction];
                break;
            case SKPaymentTransactionStateDeferred:
                NSLog(@"SKPaymentTransactionStateDeferred");
                break;
            default:
                break;
        }
    }
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    // Your application should implement these two methods.
    NSString * productIdentifier = transaction.payment.productIdentifier;
//    NSString * receipt = [transaction.transactionReceipt base64EncodedString];
    if ([productIdentifier length] > 0) {
        // 向自己的服务器验证购买凭证
    }
    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    
}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    if(transaction.error.code != SKErrorPaymentCancelled) {
        NSLog(@"购买失败");
    } else {
        NSLog(@"用户取消交易");
    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    // 对于已购商品,处理恢复购买的逻辑
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

一般情况下后台需要去校验

前端校验示例代码如下


#pragma mark 客户端验证购买凭据
- (void)verifyTransactionResult:(SKPaymentTransaction *)transaction
{
    
    NSString * str=[[NSString alloc]initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
    
    NSString *environment=[self environmentForReceipt:str];
    
    // 验证凭据,获取到苹果返回的交易凭据
    // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 从沙盒中获取到购买凭据
    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
    // 传输的是BASE64编码的字符串
    /**
     BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
     BASE64是可以编码和解码的
     */
    NSDictionary *requestContents = @{
                                      @"receipt-data": [receipt base64EncodedStringWithOptions:0]
                                      };
    NSError *error;
    // 转换为 JSON 格式
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                          options:0
                                                            error:&error];
    // 不存在
    if (!requestData) { /* ... Handle error ... */ }
    
    // 发送网络POST请求,对购买凭据进行验证
    NSString *verifyUrlString;
    
    if ([environment isEqualToString:@"environment=Sandbox"]) {
        verifyUrlString= @"https://sandbox.itunes.apple.com/verifyReceipt";
    } else {
        verifyUrlString= @"https://buy.itunes.apple.com/verifyReceipt";
    }
    
    
    // 国内访问苹果服务器比较慢,timeoutInterval 需要长一点
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:verifyUrlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
    
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];
    
    // 在后台对列中提交验证请求,并获得官方的验证JSON结果
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                               if (connectionError) {
                                   
                                   NSLog(@"验证购买过程中发生错误, 错误信息:%@", error.localizedDescription);
                                   return ;
                               } else {
                                   NSError *error;
                                   NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                   
                                   if ([jsonResponse[@"status"] intValue] == 0) {
                                       NSLog(@"购买成功");
                                       
                                       NSDictionary *dicReceipt= jsonResponse[@"receipt"];
                                       
                                       /*
                                        PS.如果你的商品比较多的话,最好给dicReceipt[@"in_app"]按照最新时间做下排序再取firstObject,由于网络延迟难免出现延迟等现象导致当前firstObject可能并不是我们现在所买商品的凭证。
                                        */
                                       NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
                                       
                                       NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识
                                       //如果是消耗品则记录购买数量,非消耗品则记录是否购买过
                                       NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
                                       if ([productIdentifier isEqualToString:@"123"]) {
                                           int purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量
                                           [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
                                       }else{
                                           [defaults setBool:YES forKey:productIdentifier];
                                       }
                                       
                                       //在此处对购买记录进行存储,可以存储到开发商的服务器端
                                       
                                   } else {
                                       NSLog(@"验证失败");
                                   }
                                   
                                   // 比对 jsonResponse 中以下信息基本上可以保证数据安全
                                   /*
                                    bundle_id
                                    application_version
                                    product_id
                                    transaction_id
                                    */
                                   
                                   
                               }
                           }];
    
}
-(NSString *)environmentForReceipt:(NSString * )str
{
    str= [str stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
    
    str = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    
    str = [str stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    
    str=[str stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    str=[str stringByReplacingOccurrencesOfString:@"\"" withString:@""];
    
    NSArray * arr=[str componentsSeparatedByString:@";"];
    
    //存储收据环境的变量
    NSString * environment=arr[2];
    return environment;
}
附:

CocoaChina IAP专题

你可能感兴趣的:(iOS 应用内购(IAP))