苹果支付
一:绑定银行卡visa卡 参考链接
二:创建内购产品 :app里面的功能 参考链接
三:创建沙盒测试账户:Itunes里面用户和职能。参考链接
四:购买代码块 参考链接
+(instancetype)share{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
applePay = [[AHApplePay alloc]init];
});
if (applePay.myProducts.count == 0) {
[applePay getPurchaseGoodsList];
}
return applePay;
}
//获取商品列表
-(void)getPurchaseGoodsList{
if ([SKPaymentQueue canMakePayments]) {
//此处内购商品编码是虚构的 ?????
NSString *productPath = [[NSBundle mainBundle] pathForResource:@"productIDS" ofType:@"plist"];
NSArray *products = [NSArray arrayWithContentsOfFile:productPath];
[self getProductInfo:products];
}else{
if (_cannotPurchaseBlock) {
_cannotPurchaseBlock(nil,@"失败,用户禁止应用内付费购买");
}
}
}
//购买商品
-(void)purchaseGoods:(id)goods{
[self purchaseGoodsWithApplePurchase:goods];
}
//苹果支付
-(void)purchaseGoodsWithApplePurchase:(id)goods{
NSString *identify = (NSString*)goods;
SKProduct *paymentPay = nil;
for (SKProduct *payment in self.myProducts) {
if ([payment.productIdentifier isEqualToString:identify]) {
paymentPay = payment;
}
}
if (paymentPay == nil) {
return;
}
//1.发起一个购买操作
SKPayment *payment = [SKPayment paymentWithProduct:paymentPay];
if (payment == nil) {
return;
}
// 2.将票据加到到交易队列中
[[SKPaymentQueue defaultQueue] addPayment:payment];
//3.观察交易队列中交易发生的改变
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
//购买 根据在plist文件位置
-(void)applePay:(NSInteger)idx{
NSString *productPath = [[NSBundle mainBundle] pathForResource:@"productIDS" ofType:@"plist"];
NSArray *products = [NSArray arrayWithContentsOfFile:productPath];
if (idx<products.count) {
NSString *identify = products[idx];
[self.myProducts enumerateObjectsUsingBlock:^(SKProduct *payment, NSUInteger idx, BOOL * _Nonnull stop) {
if ([payment.productIdentifier isEqualToString:identify]) {
//1.发起一个购买操作
SKPayment *paymentPro = [SKPayment paymentWithProduct:payment];
// 2.将票据加到到交易队列中
[[SKPaymentQueue defaultQueue] addPayment:paymentPro];
//3.观察交易队列中交易发生的改变
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
}];
}
}
//通过该IAP的Product ID向App Store查询,获取SKPayment实例,接着通过SKPaymentQueue的addPayment方法发起一个购买的操作
//下面的ProductId应该是事先在itunesConnect中添加好的,已存在的付费项目,否则会查询失败
-(void)getProductInfo:(NSArray *)productIds{
NSSet *set = [NSSet setWithArray:productIds];
_applePurchaseRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
_applePurchaseRequest.delegate = self;
[_applePurchaseRequest start];
}
#pragma mark - SKProductsRequestDelegate
//查询的回调函数 -----获取可销售的商品,并且排序
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
//获取到的所有内购商品
NSArray *myProducts = response.products;
//判断个数
if (myProducts.count==0) {
if (_failureBlock) {
_failureBlock(nil,@"无法获取产品信息,购买失败。");
}
NSLog(@"无法获取产品信息,购买失败。");
return;
}else{
self.myProducts = myProducts;
}
//刷新、配置UI ???
#warning 发起一个购买操作
// //1.发起一个购买操作
// SKPayment *payment = [SKPayment paymentWithProduct:myProducts[self.purchaseGoodsNumber]];
// // 2.将票据加到到交易队列中
// [[SKPaymentQueue defaultQueue] addPayment:payment];
// //3.观察交易队列中交易发生的改变
// [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
#pragma mark - SKPaymentTransactionObserver
//当用户购买的操作有结果时,就会触发下面的回调函数,相应进行处理
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: //交易完成
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed: //交易失败
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored: //已经购买过该商品
[self restoreTransaction:transaction];
break;
case SKPaymentTransactionStatePurchasing: //商品添加进列表
LOG(@"商品添加进列表");
break;
default:
break;
}
}
}
//交易完成后的操作
-(void)completeTransaction:(SKPaymentTransaction *)transaction{
NSString * productIdentifier = transaction.payment.productIdentifier;
// NSURL *url = [[NSBundle mainBundle] appStoreReceiptURL];
// NSData *transactionReceiptData = [NSData dataWithContentsOfURL:url];
// NSString *receipt = [transactionReceiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSData *data = transaction.transactionReceipt;
NSString * receipt = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
[[NSUserDefaults standardUserDefaults]setObject:receipt forKey:@"receipt"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([productIdentifier length]>0 && !TestStringIsBlank(receipt)) {
//向自己的服务器验证购买凭证????
AHPersonInfoModel *infoModel = [AHPersonInfoManager share].infoModel;
NSDictionary *dic = @{@"uid":@(infoModel.uid),@"token":infoModel.token,@"orderid":receipt};
[AHRequestTool applepay:dic succussBlock:^(AHBaseModel *baseModel) {
LOG(@"%@",baseModel);
if (self.successfulBlock) {
self.successfulBlock(nil, nil);
self.successfulBlock = nil;
}
} failureBlock:^(NSString *errorStr) {
if (self.failureBlock) {
self.failureBlock(nil, nil);
self.failureBlock = nil;
}
}];
//创建描述请求的JSON对象
NSError *error;
NSDictionary * requestContents = @{@"receipt-data":receipt,@"password":@"7ad47dcb6de94b08aa3ac3b7dd0d55f8",@"exclude-old-transactions":@(YES)};
NSData * requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0 error:&error];
if(!requestData){
//错误处理
}
//创建带有收据数据的POST请求。
NSURL * storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
NSMutableURLRequest * storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];
//在后台队列上连接到iTunes Store。
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse * response,NSData * data,NSError * connectionError){
if(connectionError){
//错误处理
} else {
NSError *error;
NSDictionary * jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if(jsonResponse){
//验证
LOG(@"%@",jsonResponse);
}
}}];
}
//移除transaction购买操作
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
//交易失败后的操作
-(void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) {
if (_failureBlock) {
_failureBlock(nil,@"购买失败");
}
LOG(@"购买失败");
}else{
if (_failureBlock) {
_failureBlock(nil,@"用户取消交易");
}
LOG(@"用户取消交易");
}
//移除transaction购买操作
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
//已经购买过该商品
-(void)restoreTransaction:(SKPaymentTransaction *)transaction{
//对于已购买商品,处理恢复购买的逻辑
//移除transaction购买操作
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
五:苹果支付验证代码块,建议后台验证。
NSString *recept = [[NSUserDefaults standardUserDefaults]objectForKey:@"receipt"];
if (!recept) {
return;
}
//创建描述请求的JSON对象 只有订阅型才有密码设置。
NSError *error;
NSDictionary * requestContents = @{@"receipt-data":recept,@"password":@"7ad47dcb6de94b08aa3ac3b7dd0d55f8"};
NSData * requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0 error:&error];
if(!requestData){
//错误处理
}
//创建带有收据数据的POST请求。
NSURL * storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
NSMutableURLRequest * storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];
//在后台队列上连接到iTunes Store。
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse * response,NSData * data,NSError * connectionError){
if(connectionError){
//错误处理
} else {
NSError *error;
NSDictionary * jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if(jsonResponse){
//验证
LOG(@"%@",jsonResponse);
}
}}];
内购沙盒测试续订周期时间不一样,且一天测试次数大概6次,且自动续费一般失败。
六 :上线注意事项(这里是以订购为例)
购买协议具体内容:
一定要有:自动续费,可随时取消。提前24续费,下个周期第一天可取消。app设置里面可取消订购。协议里最好加上自己的公司。
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:13],NSForegroundColorAttributeName:[UIColor blackColor]};
NSString *contentStr = @"* The cost of subscription for Super Powers is 2.99 USD for 7 days, 7.99 USD for 1 month, 19.99 USD for 3 months, and 49.99 USD for 1 year.\n\n* If you choose to purchase Super Powers, the payment will be charged to your iTunes account at confirmation of purchase.\n\n* Your subscription automatically renews unless auto-renew is turned off at least 24-hours before the end of the current period.\n\n* Account will be charged for renewal within 24-hours prior to the end of the current period, and the cost of the renewal would be the same as your first payment.\n\n* You can manage your auto-renewal and auto-renewal may be turned off by going to your Account Settings after purchase.\n\n* No cancellation of the current subscription is allowed during the active subscription period.\n\n* If you don't want to purchase Super Powers, you can simply continue using Dittor for free.\n\n* Any unused portion of a free trial period, if offered, will be forfeited when you purchase a subscription to that publication.\n\nhttp://godittor.com/terms.html\n\nhttp://godittor.com/privacy.html";
NSMutableAttributedString *contentAttr = [[NSMutableAttributedString alloc]initWithString:contentStr attributes:dic];
NSRange range = [contentStr rangeOfString:@"http://godittor.com/terms.html\n\nhttp://godittor.com/privacy.html"];
NSString *lin1 = @"http://godittor.com/terms.html";
NSString *lin2 = @"http://godittor.com/privacy.html";
NSRange range1 = [contentStr rangeOfString:lin1];
NSRange range2 = [contentStr rangeOfString:lin2];
self.range1 = range1;
self.range2 = range2;
[contentAttr addAttributes:@{NSForegroundColorAttributeName:[UIColor blueColor]} range:range];
[contentAttr addAttributes:@{NSLinkAttributeName:lin1}
range:range1];
[contentAttr addAttributes:@{NSLinkAttributeName:lin2}
range:range2];
self.textView.attributedText = contentAttr;
self.textView.delegate =self;
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
if (characterRange.location == self.range1.location && characterRange.length == self.range1.length) {
[self bt_pushTrermAction:nil];
}else if(characterRange.location == self.range2.location && characterRange.length == self.range2.length){
[self bt_privacyAction:nil];
}
return NO;
}
app上线app描述包含商品名字,价格,最好加购买协议。
可参考爱奇艺,等有内购功能app在appstore上的描述。
七 :深坑记录。
- 第一次加内购或者有新商品,发布成功后,服务器并没有立刻同步,必须要等上一段时间(12小时左右)才能拉取到产品列表。
- 点击发布后等待时间较久,大概12小时才能在appstore上搜索到。(官方说24之内可以看到)
- 元数据需要,绑定银行卡,设置报税表,设置内购项目(完整的需要截图和描述),添加沙盒测试账号(沙盒账号不一定需要验证,沙盒账号iOS13在个人资料里有专门登陆的地方,iOS12在支付的时候会弹出登陆框登陆即可)。
-
绑定银行卡的时候需要设置报税表,一般选美国就好,后面选择NO,然后同意条款,输入生日,签名就好。如下图。