1.概念介绍
<1>苹果内购:
App内购买是指在苹果的 App Store 中购买应用程序的方式。
在玩一些游戏类应用软件,这些应用都可以免费下载,但是在玩的过程中就会需要在应用内付费,否则就只能玩几关之类的。有时候会不小心点击到一些内购按钮,或是小孩乱点导致扣费的情况。
另外苹果手机系统默认的 Apple ID 的密码时效为 15 分钟,意思就是当输入了一次 Apple ID 之后,在接下来的 15 分钟内,若是点击一些付费项目的话,系统就会直接使用 Apple ID 来购买了。
2.登录iTunes Connect,进入具体的app,点击右上角的功能,创建以供测试的内购产品
3.登录iTunes Connect,进入用户职能,点击沙箱技术测试员,创建测试员账号
注意:
<1>将要创建的测试员账号必须是之前没有注册过App Store的,所以要谨慎注册,谨慎删除。
<2>创建成功之后,我们必须得登录这个邮箱,完成App Store的邮箱验证。
<3>退出当前iPhone的iCloud,App Store的所有账号。
完成以上的操作,即可开始苹果内购的测试。
4.打开Xcode运行项目,开始代码层的编写;
我们先理一下内购的逻辑:
<1>定义好正式和测试的环境;
#if DEBUG
//沙盒测试环境验证
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
#else
//正式环境验证
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
#endif
<2>检查当前iPhone是否支持苹果内购;
-(void)startUserIAP{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
_currentProId = @"yearmoneytest";
if([SKPaymentQueue canMakePayments]){
[self requestProductData:@"yearmoneytest"];
}else{
NSLog(@"不允许程序内付费");
}
}
<3>去苹果服务器请求当前app里面的内购产品,如果请求成功,找到自己要购买的产品,并开始支付;
//去苹果服务器请求商品
- (void)requestProductData:(NSString *)type{
NSLog(@"-------------请求对应的产品信息----------------");
NSArray *product = [[NSArray alloc] initWithObjects:type,nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
}
//收到产品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSLog(@"--------------收到产品反馈消息---------------------");
NSArray *product = response.products;
if([product count] == 0){
NSLog(@"--------------没有商品------------------");
return;
}
NSLog(@"productID:%@", response.invalidProductIdentifiers);
NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
SKProduct *p = nil;
for (SKProduct *pro in product) {
NSLog(@"%@", [pro description]);
NSLog(@"%@", [pro localizedTitle]);
NSLog(@"%@", [pro localizedDescription]);
NSLog(@"%@", [pro price]);
NSLog(@"%@", [pro productIdentifier]);
if([pro.productIdentifier isEqualToString:_currentProId]){
p = pro;
}
}
SKPayment *payment = [SKPayment paymentWithProduct:p];
NSLog(@"发送购买请求");
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"------------------错误-----------------:%@", error);
}
- (void)requestDidFinish:(SKRequest *)request{
NSLog(@"------------反馈信息结束-----------------");
}
<4>监听购买的结果,如果购买成功,我们再去苹果服务器验证凭证;
//监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction){
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:{
NSLog(@"交易完成");
// 发送到苹果服务器验证凭证
[self verifyPurchaseWithPaymentTransaction];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加进列表");
break;
case SKPaymentTransactionStateRestored:{
NSLog(@"已经购买过商品");
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStateFailed:{
NSLog(@"交易失败");
[[NSNotificationCenter defaultCenter] postNotificationName:@"PaymentFailed" object:nil];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
default:
break;
}
}
}
//交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
NSLog(@"交易结束");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
/**
* 验证购买,避免越狱软件模拟苹果请求达到非法购买问题
*
*/
-(void)verifyPurchaseWithPaymentTransaction{
//从沙盒中获取交易凭证并且拼接成请求体数据
NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];
NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串
NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接请求数据
NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
//创建请求到苹果官方进行购买验证
NSURL *url=[NSURL URLWithString:SANDBOX];
NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
requestM.HTTPBody=bodyData;
requestM.HTTPMethod=@"POST";
//创建连接并发送同步请求
[[NSURLSession sharedSession] dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
return;
}
NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@",dic);
if([dic[@"status"] intValue]==0){
NSLog(@"购买成功!");
[[NSNotificationCenter defaultCenter] postNotificationName:@"PaymentSuccess" object:nil];
NSDictionary *dicReceipt= dic[@"receipt"];
NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识
//如果是消耗品则记录购买数量,非消耗品则记录是否购买过
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
if ([productIdentifier isEqualToString:@"yearmoneytest"]) {
NSInteger purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量
[defaults setInteger:(purchasedCount+1) forKey:productIdentifier];
}else{
[defaults setBool:YES forKey:productIdentifier];
}
[defaults synchronize];
//在此处对购买记录进行存储,可以存储到开发商的服务器端
}else{
[[NSNotificationCenter defaultCenter] postNotificationName:@"PaymentFailed" object:nil];
NSLog(@"购买失败,未通过验证!");
}
}];
}
以上即是我分享的苹果内购总结,感谢阅读,欢迎指正。为方便大家使用,封装好了一套代码,一句话调用即可,下面是文件代码。
//
// IAPManager.h
// traffic
//
// Created by Tommy on 2017/6/10.
// Copyright © 2017年 Mqch295. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface IAPManager : NSObject
/***该类的对象必须在内存中一直存在,才可以执行完整个内购的流程***/
-(void)startUserIAP;
@end
//
// IAPManager.m
// traffic
//
// Created by Tommy on 2017/6/10.
// Copyright © 2017年 Mqch295. All rights reserved.
//
#if DEBUG
//沙盒测试环境验证
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
#else
//正式环境验证
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
#endif
#import "IAPManager.h"
#import <StoreKit/StoreKit.h>
@interface IAPManager()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
@property (nonatomic,copy) NSString *currentProId;
@end
@implementation IAPManager
#pragma mark 公有方法
-(void)startUserIAP{
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
_currentProId = @"yearmoneytest";
if([SKPaymentQueue canMakePayments]){
[self requestProductData:@"yearmoneytest"];
}else{
NSLog(@"不允许程序内付费");
}
}
#pragma mark 私有方法
//去苹果服务器请求商品
- (void)requestProductData:(NSString *)type{
NSLog(@"-------------请求对应的产品信息----------------");
NSArray *product = [[NSArray alloc] initWithObjects:type,nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
}
//收到产品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSLog(@"--------------收到产品反馈消息---------------------");
NSArray *product = response.products;
if([product count] == 0){
NSLog(@"--------------没有商品------------------");
return;
}
NSLog(@"productID:%@", response.invalidProductIdentifiers);
NSLog(@"产品付费数量:%lu",(unsigned long)[product count]);
SKProduct *p = nil;
for (SKProduct *pro in product) {
NSLog(@"%@", [pro description]);
NSLog(@"%@", [pro localizedTitle]);
NSLog(@"%@", [pro localizedDescription]);
NSLog(@"%@", [pro price]);
NSLog(@"%@", [pro productIdentifier]);
if([pro.productIdentifier isEqualToString:_currentProId]){
p = pro;
}
}
SKPayment *payment = [SKPayment paymentWithProduct:p];
NSLog(@"发送购买请求");
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"------------------错误-----------------:%@", error);
}
- (void)requestDidFinish:(SKRequest *)request{
NSLog(@"------------反馈信息结束-----------------");
}
/**
* 验证购买,避免越狱软件模拟苹果请求达到非法购买问题
*
*/
-(void)verifyPurchaseWithPaymentTransaction{
//从沙盒中获取交易凭证并且拼接成请求体数据
NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];
NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串
NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接请求数据
NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
//创建请求到苹果官方进行购买验证
NSURL *url=[NSURL URLWithString:SANDBOX];
NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
requestM.HTTPBody=bodyData;
requestM.HTTPMethod=@"POST";
//创建连接并发送同步请求
[[NSURLSession sharedSession] dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
return;
}
NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@",dic);
if([dic[@"status"] intValue]==0){
NSLog(@"购买成功!");
[[NSNotificationCenter defaultCenter] postNotificationName:@"PaymentSuccess" object:nil];
NSDictionary *dicReceipt= dic[@"receipt"];
NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
NSString *productIdentifier= dicInApp[@"product_id"];//读取产品标识
//如果是消耗品则记录购买数量,非消耗品则记录是否购买过
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
if ([productIdentifier isEqualToString:@"yearmoneytest"]) {
NSInteger purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量
[defaults setInteger:(purchasedCount+1) forKey:productIdentifier];
}else{
[defaults setBool:YES forKey:productIdentifier];
}
[defaults synchronize];
//在此处对购买记录进行存储,可以存储到开发商的服务器端
}else{
[[NSNotificationCenter defaultCenter] postNotificationName:@"PaymentFailed" object:nil];
NSLog(@"购买失败,未通过验证!");
}
}];
}
//监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction){
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:{
NSLog(@"交易完成");
// 发送到苹果服务器验证凭证
[self verifyPurchaseWithPaymentTransaction];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加进列表");
break;
case SKPaymentTransactionStateRestored:{
NSLog(@"已经购买过商品");
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStateFailed:{
NSLog(@"交易失败");
[[NSNotificationCenter defaultCenter] postNotificationName:@"PaymentFailed" object:nil];
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
default:
break;
}
}
}
//交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
NSLog(@"交易结束");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
@end