親測可用,這里做個記錄,內購完整流程參考原文
調用方式
// 支付結果監(jiān)聽
[YWPayHepler shareHelper].payResultBlock = ^(BOOL result, NSString * _Nonnull resultMsg) {
NSLog(@"結果回調:%@ ---> %@", (result ? @"支付成功" : @"支付失敗"), resultMsg);
};
YWIPAPayHepler .h
#import <Foundation/Foundation.h>
typedef enum {
SIAPPurchSuccess = 0, // 購買成功
SIAPPurchFailed = 1, // 購買失敗
SIAPPurchCancle = 2, // 取消購買
SIAPPurchVerFailed = 3, // 訂單校驗失敗
SIAPPurchVerSuccess = 4, // 訂單校驗成功
SIAPPurchNotArrow = 5, // 不允許內購
}SIAPPurchType;
typedef void (^IAPCompletionHandle)(SIAPPurchType type, NSData *data);
@interface YWIPAPayHepler : NSObject
/**
* 內購單例
*/
+ (instancetype)shareIAPPayHepler;
/**
開始內購
@param purchID 產品id
@param handle 回調結果
*/
- (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle;
@end
YWIPAPayHepler .m
#import "YWIPAPayHepler.h"
#import <StoreKit/StoreKit.h>
// 日志打印
#ifdef DEBUG
# define YWLog(fmt, ...) NSLog((@"\n??????: %s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
# define YWLog(...)
#endif
@interface YWIPAPayHepler() <SKPaymentTransactionObserver,SKProductsRequestDelegate> {
NSString *_purchID;
IAPCompletionHandle _handle;
}
@end
@implementation YWIPAPayHepler
#pragma mark - ??life cycle
+ (instancetype)shareIAPPayHepler {
static YWIPAPayHepler *IAPPayHepler = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
IAPPayHepler = [[YWIPAPayHepler alloc] init];
});
return IAPPayHepler;
}
- (instancetype)init{
self = [super init];
if (self) {
// 購買監(jiān)聽寫在程序入口,程序掛起時移除監(jiān)聽,這樣如果有未完成的訂單將會自動執(zhí)行并回調 paymentQueue:updatedTransactions:方法
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
#pragma mark - * * * * * ??public * * * * *
- (void)startPurchWithID:(NSString *)purchID completeHandle:(IAPCompletionHandle)handle {
if (purchID) {
if ([SKPaymentQueue canMakePayments]) {
// 開始購買服務
_purchID = purchID;
_handle = handle;
NSSet *nsset = [NSSet setWithArray:@[purchID]];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
}else{
[self handleActionWithType:SIAPPurchNotArrow data:nil];
}
}
}
#pragma mark - * * * * * ??private * * * * *
- (void)handleActionWithType:(SIAPPurchType)type data:(NSData *)data {
#if DEBUG
switch (type) {
case SIAPPurchSuccess:
YWLog(@"購買成功");
break;
case SIAPPurchFailed:
YWLog(@"購買失敗");
break;
case SIAPPurchCancle:
YWLog(@"用戶取消購買");
break;
case SIAPPurchVerFailed:
YWLog(@"訂單校驗失敗");
break;
case SIAPPurchVerSuccess:
YWLog(@"訂單校驗成功");
break;
case SIAPPurchNotArrow:
YWLog(@"不允許程序內付費");
break;
default:
break;
}
#endif
if(_handle){
// 回調憑證數據
_handle(type, data);
}
}
#pragma mark - * * * * * ??delegate * * * * *
// 交易結束
- (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) {
// 如果是真正需要內購胆剧,則在此處向自己的服務器驗證購買憑證
YWLog(@"購買憑證為:%@", receipt);
// 驗證成功與否都注銷交易,否則會出現虛假憑證信息一直驗證不通過,每次進程序都得輸入蘋果賬號
//[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
YWLog(@"支付交易結束房维,App向蘋果發(fā)起驗證");
// App向蘋果發(fā)起驗證(這里因為蘋果服務器不穩(wěn)定,所以真正的內購 不是App向蘋果發(fā)起驗證疯坤,而是上面的向自己的應用服務器發(fā)起驗證肢簿,由服務器向蘋果驗證)
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}
// 交易失敗
- (void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) {
[self handleActionWithType:SIAPPurchFailed data:nil];
}else{
[self handleActionWithType:SIAPPurchCancle data:nil];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)verifyPurchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag{
//交易驗證
NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
if(!receipt){
// 交易憑證為空驗證失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
return;
}
// 購買成功將交易憑證發(fā)送給服務端進行再次校驗
[self handleActionWithType:SIAPPurchSuccess data:receipt];
NSError *error;
NSDictionary *requestContents = @{
@"receipt-data": [receipt base64EncodedStringWithOptions:0]
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { // 交易憑證為空驗證失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
return;
}
//In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
//In the real environment, use https://buy.itunes.apple.com/verifyReceipt
// 正式服務器環(huán)境驗證地址
NSString *serverString = @"https://buy.itunes.apple.com/verifyReceipt";
if (flag) {
// 沙盒測試環(huán)境驗證地址
serverString = @"https://sandbox.itunes.apple.com/verifyReceipt";
}
NSURL *storeURL = [NSURL URLWithString:serverString];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:requestData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
// 無法連接服務器,購買校驗失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (!jsonResponse) {
// 蘋果服務器校驗數據返回為空校驗失敗
[self handleActionWithType:SIAPPurchVerFailed data:nil];
}
// 先驗證正式服務器,如果正式服務器返回21007再去蘋果測試服務器驗證,沙盒測試環(huán)境蘋果用的是測試服務器
NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
if (status && [status isEqualToString:@"21007"]) {
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:YES];
}else if(status && [status isEqualToString:@"0"]){
[self handleActionWithType:SIAPPurchVerSuccess data:nil];
}
YWLog(@"----驗證結果 %@",jsonResponse);
}
}];
// 驗證成功與否都注銷交易,否則會出現虛假憑證信息一直驗證不通過,每次進程序都得輸入蘋果賬號
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
#pragma mark - * * * * * SKProductsRequestDelegate * * * * *
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray *product = response.products;
if([product count] <= 0){
YWLog(@"--------------沒有商品------------------");
return;
}
SKProduct *p = nil;
for(SKProduct *pro in product){
if([pro.productIdentifier isEqualToString:_purchID]){
p = pro;
break;
}
}
YWLog(@"沙盒測試時為空productID:%@", response.invalidProductIdentifiers);
YWLog(@"產品付費數量:%lu",(unsigned long)[product count]);
YWLog(@"產品描述:%@",[p description]);
YWLog(@"產品localizedTitle:%@",[p localizedTitle]);
YWLog(@"產品localizedDescription:%@",[p localizedDescription]);
YWLog(@"產品價格:%@",[p price]);
YWLog(@"產品ID標志:%@",[p productIdentifier]);
YWLog(@"向蘋果發(fā)送購買請求");
SKPayment *payment = [SKPayment paymentWithProduct:p];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//請求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
YWLog(@"------------------請求錯誤-----------------:%@", error);
}
//請求完成
- (void)requestDidFinish:(SKRequest *)request{
YWLog(@"------------請求完成靶剑,反饋信息結束-----------------");
}
#pragma mark - * * * * * SKPaymentTransactionObserver * * * * *
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
for (SKPaymentTransaction *tran in transactions) {
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:
[self completeTransaction:tran];
break;
case SKPaymentTransactionStatePurchasing:
YWLog(@"商品添加進列表");
break;
case SKPaymentTransactionStateRestored:
YWLog(@"已經購買過商品");
// 消耗型不支持恢復購買
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:tran];
break;
default:
break;
}
}
}
@end