一、创建苹果内购支付管理工具 SIAPManager
// SIAPManager.h
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
//沙盒验证地址
//#define checkURL @"https://sandbox.itunes.apple.com/verifyReceipt"
//正式验证地址
#define checkURL @"https://buy.itunes.apple.com/verifyReceipt"
// 套装id: com.bnqc.mlt
// apple id: 1481163429
// sku: mlt
// 12个月 产品id mlt_book_year
#pragma mark - SIAPManagerDelegate内购代理
/**
* 内购工具的代理
*/
@protocol SIAPManagerDelegate <NSObject>
/**
* 代理:系统错误
*/
- (void)IAPToolSysWrong;
/**
* 代理:购买成功
* @param productID 购买成功的商品ID
*/
- (void)IAPToolBoughtProductSuccessedWithProductID:(NSString *)productID
andInfo:(NSDictionary *)infoDic;
/**
* 代理:取消购买
* @param productID 商品ID
*/
- (void)IAPToolCanceldWithProductID:(NSString *)productID;
/**
* 代理:购买成功,开始验证购买
* @param productID 商品ID
*/
- (void)IAPToolBeginCheckingdWithProductID:(NSString *)productID;
/**
* 代理:验证失败
* @param productID 商品ID
*/
- (void)IAPToolCheckFailedWithProductID:(NSString *)productID
andInfo:(NSData *)infoData;
/**
* 代理:重复验证
* @param productID 商品ID
*/
- (void)IAPToolCheckRedundantWithProductID:(NSString *)productID;
/**
* 代理:已刷新可购买商品
* @param products 商品数组
*/
- (void)IAPToolGotProducts:(NSMutableArray *)products;
/**
* 恢复了已购买的商品(永久性商品)
* @param productID 商品ID
*/
- (void)IAPToolRestoredProductID:(NSString *)productID;
@end
#pragma mark - SIAPManager内购工具
/**
* 内购工具
*/
@interface SIAPManager : NSObject
typedef void(^BoolBlock)(BOOL successed, BOOL result);
typedef void(^DicBlock)(BOOL successed, NSDictionary *result);
/**
* 代理
*/
@property(nonatomic,weak)id<SIAPManagerDelegate> delegate;
/**
* 购买完后是否在iOS端向服务器验证一次,默认为YES
*/
@property(nonatomic)BOOL CheckAfterPay;
/**
* 单例
* @return SIAPManager
*/
+ (SIAPManager *)defaultTool;
/**
* 询问苹果的服务器能够销售哪些商品
* @param products 商品ID的数组
*/
- (void)requestProductsWithProductArray:(NSArray *)products;
/**
* 用户决定购买商品
* @param productID 商品ID
*/
- (void)buyProduct:(NSString *)productID;
/**
* 恢复商品(仅限永久有效商品)
*/
- (void)restorePurchase;
@end
// SIAPManager.m
#import "SIAPManager.h"
@interface SIAPManager ()<SKPaymentTransactionObserver, SKProductsRequestDelegate>
// 商品字典
@property(nonatomic,strong)NSMutableDictionary *productDict;
@end
@implementation SIAPManager
//单例
static SIAPManager *storeTool;
//单例
+ (SIAPManager *)defaultTool {
if (!storeTool){
storeTool = [SIAPManager new];
[storeTool setup];
}
return storeTool;
}
#pragma mark 初始化
- (void)setup {
self.CheckAfterPay = YES;
// 设置购买队列的监听器
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
#pragma mark 询问苹果的服务器能够销售哪些商品
- (void)requestProductsWithProductArray:(NSArray *)products {
NSLog(@"开始请求可销售商品");
// 能够销售的商品
NSSet *set = [[NSSet alloc] initWithArray:products];
// "异步"询问苹果能否销售
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
// 启动请求
[request start];
}
#pragma mark 获取询问结果,成功采取操作把商品加入可售商品字典里
/**
* 获取询问结果,成功采取操作把商品加入可售商品字典里
* @param request 请求内容
* @param response 返回的结果
*/
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
if (self.productDict == nil) {
self.productDict = [NSMutableDictionary dictionaryWithCapacity:response.products.count];
}
NSMutableArray *productArray = [NSMutableArray array];
for (SKProduct *product in response.products) {
//NSLog(@"%@", product.productIdentifier);
// 填充商品字典
[self.productDict setObject:product forKey:product.productIdentifier];
[productArray addObject:product];
}
//通知代理
[self.delegate IAPToolGotProducts:productArray];
}
#pragma mark - 用户决定购买商品
- (void)buyProduct:(NSString *)productID {
SKProduct *product = self.productDict[productID];
// 要购买产品(店员给用户开了个小票)
SKPayment *payment = [SKPayment paymentWithProduct:product];
// 去收银台排队,准备购买(异步网络)
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark - SKPaymentTransaction Observer
#pragma mark 购买队列状态变化,,判断购买状态是否成功
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
NSLog(@"队列状态变化 %@", transaction);
if (SKPaymentTransactionStatePurchased == transaction.transactionState) { // 状态是购买完成
NSLog(@"购买完成 %@", transaction.payment.productIdentifier);
if (self.CheckAfterPay) { // 需要向苹果服务器验证一下
//通知代理
[self.delegate IAPToolBeginCheckingdWithProductID:transaction.payment.productIdentifier];
// 验证购买凭据
[self verifyPruchaseWithID:transaction.payment.productIdentifier];
} else { // 不需要向苹果服务器验证
//通知代理
[self.delegate IAPToolBoughtProductSuccessedWithProductID:transaction.payment.productIdentifier
andInfo:nil];
}
// 将交易从交易队列中删除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
} else if (SKPaymentTransactionStateRestored == transaction.transactionState) { // 状态是恢复成功
NSLog(@"恢复成功 :%@", transaction.payment.productIdentifier);
// 通知代理
[self.delegate IAPToolRestoredProductID:transaction.payment.productIdentifier];
// 将交易从交易队列中删除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
} else if (SKPaymentTransactionStateFailed == transaction.transactionState) { // 状态是交易失败
NSLog(@"交易失败");
// 将交易从交易队列中删除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// 通知代理
[self.delegate IAPToolCanceldWithProductID:transaction.payment.productIdentifier];
} else if (SKPaymentTransactionStatePurchasing == transaction.transactionState) { // 状态是购买中ing....
NSLog(@"正在购买");
} else {
NSLog(@"state:%ld",(long)transaction.transactionState); // 状态是已经购买
NSLog(@"已经购买");
// 将交易从交易队列中删除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
}
#pragma mark - 恢复商品
- (void)restorePurchase {
// 恢复已经完成的所有交易.(仅限永久有效商品)
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#pragma mark 验证购买凭据
- (void)verifyPruchaseWithID:(NSString *)ProductID {
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
// 发送网络POST请求,对购买凭据进行验证
//In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
//In the real environment, use https://buy.itunes.apple.com/verifyReceipt
// Create a POST request with the receipt data.
NSURL *url = [NSURL URLWithString:checkURL];
// 国内访问苹果服务器比较慢,timeoutInterval需要长一点
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f];
request.HTTPMethod = @"POST";
// 在网络中传输数据,大多情况下是传输的字符串而不是二进制数据
// 传输的是BASE64编码的字符串
/**
BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
BASE64是可以编码和解码的
*/
NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = payloadData;
// 提交验证请求,并获得官方的验证JSON结果
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
#pragma clang diagnostic pop
// NSData *result = [self sendSynchronousRequest:request returningResponse:nil error:nil];
// 官方验证结果为空
if (result == nil) {
NSLog(@"凭据验证失败");
//验证失败,通知代理
[self.delegate IAPToolCheckFailedWithProductID:ProductID
andInfo:result];
}
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result
options:NSJSONReadingAllowFragments error:nil];
NSLog(@"凭据-RecivedVerifyPruchaseDict:%@", dict);
if (dict != nil) {
// 验证成功,通知代理
// bundle_id&application_version&product_id&transaction_id
[self.delegate IAPToolBoughtProductSuccessedWithProductID:ProductID
andInfo:dict];
} else {
//验证失败,通知代理
[self.delegate IAPToolCheckFailedWithProductID:ProductID
andInfo:result];
}
}
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error {
NSError __block *err = NULL;
NSData __block *data;
BOOL __block reqProcessed = false;
NSURLResponse __block *resp;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) {
resp = _response;
err = _error;
data = _data;
reqProcessed = true;
}] resume];
while (!reqProcessed) {
[NSThread sleepForTimeInterval:0];
}
*response = resp;
*error = err;
return data;
}
@end
二、调用内购支付工具
- 导入内购工具
#import "SIAPManager.h"
- 遵守内购协议
@interface QuestionPayAlertView() <SIAPManagerDelegate>
- 在支付时,创建支付单例
- (void)okButtonAction {
SIAPManager *IAPManager = [SIAPManager defaultTool];
IAPManager.delegate = self;
// 支付前首先调用接口,获取商品ID数组,也就是内购列表
[IAPManager requestProductsWithProductArray:self.productsIDArray];
}
- 实现内购方法
#pragma mark - 内购回调方法
// 系统错误
- (void)IAPToolSysWrong {
[SVProgressHUD showInfoWithStatus:@"系统错误"];
[SVProgressHUD dismissWithDelay:1.5];
}
// 购买成功
- (void)IAPToolBoughtProductSuccessedWithProductID:(NSString *)productID andInfo:(NSDictionary *)infoDic {
// 验证凭据,获取到苹果返回的交易凭据
// appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 从沙盒中获取到购买凭据
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
// 发送网络POST请求,对购买凭据进行验证
//In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
//In the real environment, use https://buy.itunes.apple.com/verifyReceipt
NSURL *url = [NSURL URLWithString:checkURL];
// 国内访问苹果服务器比较慢,timeoutInterval需要长一点
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0f];
request.HTTPMethod = @"POST";
// 在网络中传输数据,大多情况下是传输的字符串而不是二进制数据
// 传输的是BASE64编码的字符串
/**
BASE64 常用的编码方案,通常用于数据传输,以及加密算法的基础算法,传输过程中能够保证数据传输的稳定性
BASE64是可以编码和解码的
*/
NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
// NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
// 创建订单
[HttpManager creatOrderWithAmount:@(1) refer_id:@(self.refer_id) refer_type:@([self.referType integerValue]) course_price_id:@([self.coursePriceIDArray[self.selectIndex] integerValue]) receipt:encodeStr andHandle:^(NSString *error, NSDictionary *result) {
[self.loadingHUD removeFromSuperview];
if (error == nil) {
[SVProgressHUD showSuccessWithStatus:@"购买成功!"];
[SVProgressHUD dismissWithDelay:3.0 completion:^{
// 购买成功后刷新列表,回调
// [self dismissAlertViewAction];
self.backgroundColor = [UIColor clearColor];
self.cancleButton.alpha = 0;
self.mainView.alpha = 0;
[self removeFromSuperview];
// 回调
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(questionPaySuccess)]) {
[self.delegate questionPaySuccess];
}
}];
} else {
[self.loadingHUD removeFromSuperview];
[SVProgressHUD showErrorWithStatus:@"购买失败"];
[SVProgressHUD dismissWithDelay:1.5];
}
}];
}
// 取消购买
- (void)IAPToolCanceldWithProductID:(NSString *)productID {
[self.loadingHUD removeFromSuperview];
[SVProgressHUD showInfoWithStatus:@"取消购买"];
[SVProgressHUD dismissWithDelay:1.5];
}
// 购买成功,开始验证购买
- (void)IAPToolBeginCheckingdWithProductID:(NSString *)productID {
//
}
// 验证失败
- (void)IAPToolCheckFailedWithProductID:(NSString *)productID andInfo:(NSData *)infoData {
[self.loadingHUD removeFromSuperview];
[SVProgressHUD showInfoWithStatus:@"购买失败"];
[SVProgressHUD dismissWithDelay:1.5];
}
// 重复验证
- (void)IAPToolCheckRedundantWithProductID:(NSString *)productID {
[self.loadingHUD removeFromSuperview];
[SVProgressHUD showInfoWithStatus:@"购买失败"];
[SVProgressHUD dismissWithDelay:1.5];
}
// 已刷新可购买商品
- (void)IAPToolGotProducts:(NSMutableArray *)products {
[SIAPManager defaultTool].CheckAfterPay = NO;
// mkj00006
// [[SIAPManager defaultTool] buyProduct:@"mlt_book_year"];
[[SIAPManager defaultTool] buyProduct:self.productsIDArray[self.selectIndex]];
}
// 恢复了已购买的商品(永久性商品)
- (void)IAPToolRestoredProductID:(NSString *)productID {
[self.loadingHUD removeFromSuperview];
}
三、补充,Xcode开启内购权限
开启内购权限.jpg
网友评论