一、去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专题