iOS之苹果内购最新2017年教程
公司有个功能叫付费加速制作模型。这属于服务费,之前用的支付宝和微信支付,后来微信通过其官方公众号发出通知,称因为苹果对iOS应用的新规定,将关闭微信公众号在iOS上的打赏功能。所以为了不被苹果拒绝。支付换做内购了。今天学习下如何使用苹果支付。
协议,税务和银行业务
1242012-69099549f1c19d98.png点击
申请合同.png点击request申请合同
1242012-d240abc2ad52cf23.png添加联系人
1242012-e4b46eebc037c1c9.png同意协议然后提交
1242012-447905cbad7e757b.png
设置相关的信息如联系人,银行,税务信息。
1242012-56d222c1b8dd3aa8.png联系人信息
先点击蓝色的add New Contact 写完后save,注意在role(角色)中,为联系人选择身份(高级管理人员 ,金融,技术,法律,营销),点击Done保存
银行信息
添加银行信息
1242012-f0975f1235ee8cfa.png选择国家
1242012-7222d2b96d4c8463.png
填写CNAPS
CNAPS 银行网点联行号.我们可以直接到银行客服电话查询 CNAPS, 快速便捷.还可以通过下图的Look up CNAPS Code 查询. 不过有的查询不到
1242012-52e42c1d0678e05a.png 1242012-f32b0ee23bb4eb3c.png
确认银行信息
1242012-407a7da27f6730a1.png
.填写公司银行账号信息
1242012-efc941edf1b339fc.png
税务信息
税务信息有三个选项:美国税务、澳大利亚税务、加拿大税务。我们在这里选择美国税务就可以
1242012-2eec17fd00430f79.png 1242012-e7cbd0888d850872.png选No
1242012-391d636a208ade83.png选No
填写税务相关信息
9b.Foreign TIN 是填写公司税务识别码.公司营业执照上同意社会信用代码去掉前两位和最后一位就是公司税务识别码(15位)
1242012-eeb0455ed5164c73.png
点击提交之后会让你确认信息,确认无误后再次点击提交(提交后无法修改)
添加内购
点击功能,然后点加号。
WX20170503-155202@2x.png
根据项目需求,选择内购项目
1242012-ee6d6740f12728a9.png填写内购项目的相关信息
商品名称根据你的消费道具的实际意义来说明
产品ID是比较重要的,只要唯一即可,在实际应用中,一定要认真填写。
选择价格
本地化版本
将在 App Store 上可见的 App 内购买项目名称。可以参考App Store中其他应用的内购项目描述
描述不得少于10个字符
WX20170503-155755@2x.png
审核信息
1.只会在审核中使用屏幕快照,不会将其显示在 App Store 上。屏幕快照必须具有适合您 App 平台的有效尺寸。
2.能够有助于我们进行审核的关于您的 App 内购买项目的其他信息,如测试帐户(包括用户名、密码等)。审核备注不得超过 4000 个字符。
WX20170503-155814@2x.png
内购项目则添加完成
申请沙盒测试账号(用来测试购买项目)
我们不必用真实的人民币测试购买内购项目
首先我们回到iTunes Connect中,在这里我们选择用户和职能。
添加沙箱技术测试员
添加测试员详细信息
所有信息都可以随意填写,不用管是否真实。但是App Store地区必须对应
注意: 邮件地址不能填写与 Apple ID 关联的, 密码保护问题必须在 6 到 35 个字符之间。密码必须包含至少一个大写字母。
内购原理
20150723155327761.jpeg- 先学习下内购的流程吧。了解了流程代码就简单了。首先app先去苹果服务器请求产品信息,然后拿着请求下来的产品信息去发送内购请求就是步骤1。
- 然后苹果服务器返回支付的结果receipt_data给app,这receipt_data是交易凭据这是步骤2.为了防止黑客盗刷。所以得拿receipt_data来做验证,看看到底是不是支付了。
- app把receipt_data传给咱们自己的服务器这是步骤3,
- 服务器把data转码后这是步骤4。
- 咱们的服务器去苹果那里验证是不是付款了。这是步骤5.
- 苹果服务器返回验证的结果给咱们的服务器。这是步骤6.
- 咱们的服务器把最后的验证结果给app,app做出一些动作。这是步骤7.
在项目中实现购买
#import "PurchaseService.h"
#import <StoreKit/StoreKit.h>
#import "MaskView.h"
@interface PurchaseService()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
//productId是在开发者平台填写的产品id,排队加速
@property(nonatomic,copy)NSString *productId;
@property(nonatomic,strong)MaskView *maskView;
@property(nonatomic,assign)BOOL isAccelerate;
@property(nonatomic,strong)NSString *filePath;
@end
@implementation PurchaseService
- (instancetype)initWithAccelerate:(BOOL)isAccelerate;
{
self = [super init];
if (self) {
self.isAccelerate = isAccelerate;
}
return self;
}
- (void)setIsAccelerate:(BOOL)isAccelerate
{
_isAccelerate = isAccelerate;
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//槽位的路径
NSString *receiptPath = [docPath stringByAppendingPathComponent:@"slot_receitp.plist"];
if (_isAccelerate == YES) {
receiptPath = [docPath stringByAppendingPathComponent:@"accelerate_receitp.plist"];
}
self.filePath = receiptPath;
}
- (void)pay
{
NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:self.filePath];
if (dic != nil) {
DLog(@"沙盒中拿");
NSString *receiptEncodeStr = dic[@"receiptEncodeStr"];
//去服务器验证
//配置服务器需要的参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"xxx"] = xxx;
//params[@"xxx"] = @(1x);
params[@"xxx"] = xxxx;
params[@"xxx"] = @(false);
[self verifyPurchaseWithDic:params];
return;
}
//添加一个交易队列观察者
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
//判断是否允许程序内购买
if ([SKPaymentQueue canMakePayments]) {
//获取商品列表
WS(weakSelf)
[SWAYNetWorking get:IapProductInfoUrl parameters:nil success:^(id responseObject) {
if ([responseObject[@"code"] integerValue] == 100) {
NSDictionary *dic = [responseObject[@"data"] objectAtIndex:0];
if (self.isAccelerate == NO) {
dic = [responseObject[@"data"] objectAtIndex:1];
}
NSInteger p_id = [dic[@"productId"] integerValue];
weakSelf.productId = [NSString stringWithFormat:@"%ld",p_id];
//去苹果服务器请求产品信息
[weakSelf requestProductData:weakSelf.productId];
}
} failure:^(NSError *error) {
}];
}else{
[SVProgressHUD showImage:nil status:@"用户禁止应用内购买,请到设置->通用->访问限制里打开App内购买项目"];
}
}
// 去苹果服务器请求产品信息
- (void)requestProductData:(NSString *)productId {
self.maskView = [[MaskView alloc]init];
[KWindow addSubview:self.maskView];
[self.maskView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.top.mas_equalTo(0);
}];
NSArray *productArr = [[NSArray alloc]initWithObjects:productId, nil];
NSSet *productSet = [NSSet setWithArray:productArr];
SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
request.delegate = self;
[request start];
}
// SKProductsRequestDelegate 收到产品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *productArr = response.products;
if ([productArr count] == 0) {
DLog(@"没有该商品");
[self.maskView removeFromSuperview];
return;
}
DLog(@"productId = %@",response.invalidProductIdentifiers);
DLog(@"产品付费数量 = %zd",productArr.count);
SKProduct *p = nil;
for (SKProduct *pro in productArr) {
DLog(@"description:%@",[pro description]);
DLog(@"localizedTitle:%@",[pro localizedTitle]);
DLog(@"localizedDescription:%@",[pro localizedDescription]);
DLog(@"price:%@",[pro price]);
DLog(@"productIdentifier:%@",[pro productIdentifier]);
if ([pro.productIdentifier isEqualToString:self.productId]) {
p = pro;
}
}
SKPayment *payment = [SKPayment paymentWithProduct:p];
//发送内购请求
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)requestDidFinish:(SKRequest *)request {
NSLog(@"完成");
[self.maskView removeFromSuperview];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSString *errMessage = [error.userInfo objectForKey:@"NSLocalizedDescription"];
[SVProgressHUD showErrorWithStatus:errMessage];
[self.maskView removeFromSuperview];
}
// SKPaymentTransactionObserver监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
for (SKPaymentTransaction *tran in transactions) {
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased: //交易完成
// 发送到服务器验证凭证
[self completeTransaction:tran];
[[SKPaymentQueue defaultQueue]finishTransaction:tran];
break;
case SKPaymentTransactionStatePurchasing: //商品添加进列表
break;
case SKPaymentTransactionStateRestored: //购买过
// 发送到苹果服务器验证凭证
[[SKPaymentQueue defaultQueue]finishTransaction:tran];
break;
case SKPaymentTransactionStateFailed: //交易失败
[[SKPaymentQueue defaultQueue]finishTransaction:tran];
[SVProgressHUD showErrorWithStatus:@"购买失败"];
break;
default:
break;
}
}
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
//商品id
NSString *produckID = transaction.payment.productIdentifier;
//订单号
NSString *transactionIdentifier = transaction.transactionIdentifier;
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
NSString *receiptEncodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//存储收据
NSMutableDictionary *receiptDic = [NSMutableDictionary dictionary];
[receiptDic setObject:produckID forKey:@"produckID"];
[receiptDic setObject:transactionIdentifier forKey:@"transactionIdentifier"];
[receiptDic setObject:receiptEncodeStr forKey:@"receiptEncodeStr"];
[receiptDic writeToFile:self.filePath atomically:YES];
//配置服务器需要的参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"receipt"] = receiptEncodeStr;
params[@"chooseEnv"] = @(false);
[self verifyPurchaseWithDic:params];
}
#pragma mark- 服务器端做验证
- (void)verifyPurchaseWithDic:(NSDictionary *)dic
{
[NetWorking post:IapCertificateUrl parameters:dic success:^(id responseObject) {
if ([responseObject[@"code"] integerValue] == 100) {
//刷新首页
[[NSNotificationCenter defaultCenter] postNotificationName:@"reloadHomeNotification" object:nil];
[SVProgressHUD showImage:nil status:responseObject[@"message"]];
[[NSFileManager defaultManager] removeItemAtPath:self.filePath error:nil];
}else{
[SVProgressHUD showImage:nil status:responseObject[@"message"]];
}
} failure:^(NSError *error) {
}];
}
//沙盒测试环境验证
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
//正式环境验证
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
// 验证购买
- (void)verifyPurchaseWithPaymentTrasaction {
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
// 发送网络POST请求,对购买凭据进行验证
//测试验证地址:https://sandbox.itunes.apple.com/verifyReceipt
//正式验证地址:https://buy.itunes.apple.com/verifyReceipt
NSURL *url = [NSURL URLWithString:SANDBOX];
NSMutableURLRequest *urlRequest =
[NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
urlRequest.HTTPMethod = @"POST";
NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
urlRequest.HTTPBody = payloadData;
// 提交验证请求,并获得官方的验证JSON结果 iOS9后更改了另外的一个方法
NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
// 官方验证结果为空
if (result == nil) {
NSLog(@"验证失败");
return;
}
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
if (dict != nil) {
// 比对字典中以下信息基本上可以保证数据安全
// bundle_id , application_version , product_id , transaction_id
// NSLog(@"验证成功!购买的商品是:%@", @"_productName");
NSLog(@"验证成功%@",dict);
}
}
- (void)dealloc {
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
@end