/*注意事項:
1.沙盒環境測試appStore內購流程的時候,請使用沒越獄的設備。
2.請務必使用真機來測試,一切以真機為准。
3.項目的Bundle identifier需要與您申請AppID時填寫的bundleID一致,不然會無法請求到商品信息。
4.如果是你自己的設備上已經綁定了自己的AppleID賬號請先注銷掉,否則你哭爹喊娘都不知道是怎么回事。
5.訂單校驗 蘋果審核app時,仍然在沙盒環境下測試,所以需要先進行正式環境驗證,如果發現是沙盒環境則轉到沙盒驗證。
識別沙盒環境訂單方法:
1.根據字段 environment = sandbox。
2.根據驗證接口返回的狀態碼,如果status=21007,則表示當前為沙盒環境。
蘋果反饋的狀態碼:
21000App Store無法讀取你提供的JSON數據
21002 訂單數據不符合格式
21003 訂單無法被驗證
21004 你提供的共享密鑰和賬戶的共享密鑰不一致
21005 訂單服務器當前不可用
21006 訂單是有效的,但訂閱服務已經過期。當收到這個信息時,解碼后的收據信息也包含在返回內容中
21007 訂單信息是測試用(sandbox),但卻被發送到產品環境中驗證
21008 訂單信息是產品環境中使用,但卻被發送到測試環境中驗證
*/
開發內購功能,首先需要一個開發者賬號,在 App store connect -> 我的App -> 功能 中申請 如圖:
有四種類型,主要看項目的需求而決定(自動續期訂閱這個最為麻煩,要簽署協議,而已在App 啟動是請求蘋果服務器,是否存在自動續期訂單)
這個圖是新增一項內購產品
參考名稱:介紹這個內購產品
產品ID(主要用到的):支付的時候用到
其他的都是在app store 中看到的介紹
首先引用 #import <StoreKit/StoreKit.h>
封裝一個內購功能,這個在app 中可能多個地方用到
+ (instancetype)shareIAPManager;
//添加內購產品
- (void)addPurchWithProductID:(NSString *)product_id completeHandle:(IAPCompletionHandleBlock)handle;
復制代碼
SKProductsRequestDelegate代理方法
交易結束后用到
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSString * productIdentifier = transaction.payment.productIdentifier;
NSData *data = [productIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSString *receipt = [data base64EncodedStringWithOptions:0];
YMLog(@"%@",receipt); if ([productIdentifier length] > 0) { // 向自己的服務器驗證購買憑證 NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) { // 取 receipt 的時候要判空,如果文件不存在,就要從蘋果服務器重新刷新下載 receipt 了 // SKReceiptRefreshRequest 刷新的時候,需要用戶輸入 Apple ID,同時需要網絡狀態良好 SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil]; receiptRefreshRequest.delegate = self; [receiptRefreshRequest start]; return; } NSData *data = [NSData dataWithContentsOfURL:receiptURL]; /** 交易憑證*/ NSString *receipt_data = [data base64EncodedStringWithOptions:0]; /** 事務標識符(交易編號) 交易編號(必傳:防止越獄下內購被破解,校驗 in_app 參數)*/ NSString *transaction_id = transaction.transactionIdentifier; NSString *goodID = transaction.payment.productIdentifier; //這里緩存receipt_data,transaction_id 因為后端做校驗的時候需要用到這兩個字段 YMLog(@"%@",receipt_data); YMLog(@"%@",transaction_id); [self retquestApplePay:receipt_data transaction_id:transaction_id goodsID:goodID]; } [self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO]; } 復制代碼
- (void)completeTransaction:(SKPaymentTransaction *)transaction ;
中 獲取
NSString * productIdentifier = transaction.payment.productIdentifier;
NSData *data = [productIdentifier
dataUsingEncoding:NSUTF8StringEncoding];NSString *receipt = [data base64EncodedStringWithOptions:0];
NSString *receipt_data = [data base64EncodedStringWithOptions:0];
/** 事務標識符(交易編號) 交易編號(必傳:防止越獄下內購被破解,校驗 in_app 參數)*/
NSString *transaction_id = transaction.transactionIdentifier;
NSString *goodID = transaction.payment.productIdentifier;
得到的 transaction_id receipt_data goodID需要上傳到app 服務器校驗(具體看后端的需求)
//private 提交訂單數據到app 服務器校驗
- (void)retquestApplePay:(NSString *)receipt_data transaction_id:(NSString *)transaction_id goodsID:(NSString *)goodsId;
復制代碼
// 交易失敗
- (void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) { [self handleActionWithType:IAPPurchFailed data:nil]; }else{ [self handleActionWithType:IAPPurchCancel data:nil]; } [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } #pragma mark -- 結束上次未完成的交易 防止串單 -(void)removeAllUncompleteTransactionBeforeStartNewTransaction{ NSArray* transactions = [SKPaymentQueue defaultQueue].transactions; if (transactions.count > 0) { //檢測是否有未完成的交易 SKPaymentTransaction* transaction = [transactions firstObject]; if (transaction.transactionState == SKPaymentTransactionStatePurchased) { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; return; } } }復制代碼
IAPDemo鏈接:Demo