iOS應用內購買專案(IAP)
阿新 • • 發佈:2019-01-09
iOS應用內購買專案流程,大致分為三個部分:
第一部分:iTunes connect設定,包含建立新的釋出版本號、建立購買專案(商品)、設定協議稅務銀行業務、新增沙盒技術測試賬號;
第二部分:在專案Xcode中設定允許In-App Purchase,然後編寫程式碼;程式碼部分可以使用原生態的,也可以使用第三方整合好的(IAPHelper),需要注意本地儲存交易憑證,用於網路不佳或失敗的情況下,重新提交。程式碼完成好之後,打包提交到APP Strore等待稽核,提交的同時也把之前的內購專案勾選一併提交稽核,待稽核通過就可以測試了(可能稽核通過之後需等待24小時,才能購買成功);
第三部分:測試是否成功購買,這裡需要真機測試,並登出本機的Apple ID,然後在app中點選購買的時候,輸入之前新增的沙盒測試賬號。這部分可能會報各種錯誤,可能是前面一二部分沒有設定完善,也有可能APP Store的問題,需要等待24小時自動就可以了,總之有問題就會有對應的解決方法,網上部落格的解決方法也會幫助你的。
前面的設定部分按照開發文件設定,這裡直接上程式碼:
//去蘋果伺服器請求商品 - (void)requestProductData:(NSString *)productId{ NSSet* dataSet = [[NSSet alloc] initWithArray:@[productId]]; [IAPShare sharedHelper].iap = [[IAPHelper alloc] initWithProductIdentifiers:dataSet]; #if DEBUG==1 [IAPShare sharedHelper].iap.production = NO; #else [IAPShare sharedHelper].iap.production = YES; #endif // 請求商品資訊 [[IAPShare sharedHelper].iap requestProductsWithCompletion:^(SKProductsRequest* request,SKProductsResponse* response){ if(response.products.count > 0 ) { SKProduct *product = response.products[0]; [[IAPShare sharedHelper].iap buyProduct:product onCompletion:^(SKPaymentTransaction* trans){ if(trans.error){ [self.hud hideAnimated:YES]; [MBProgressHUD showError:@"購買失敗,請重試" toView:self.view]; }else if(trans.transactionState == SKPaymentTransactionStatePurchased) { self.receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; [self justifyIAP:self.hud]; }else if(trans.transactionState == SKPaymentTransactionStateFailed) { if (trans.error.code == SKErrorPaymentCancelled) { [self.hud hideAnimated:YES]; }else{ [[[CommonUIAlert alloc] init] showCommonAlertView:self title:@"" message:@"購買失敗,請重試" cancelButtonTitle:@"取消" otherButtonTitle:@"確定" cancle:^{ [self.hud hideAnimated:YES]; [self saveReceipt]; } confirm:^{ [self justifyIAP:self.hud]; }]; } } }]; }else{ [self.hud hideAnimated:YES]; [MBProgressHUD showError:@"購買失敗,請重試" toView:self.view]; } }]; } #pragma mark - ios iap應用內支付二次驗證 - (void)justifyIAP:(MBProgressHUD*)hud{ if(hud==nil){ hud = [MBProgressHUD showMessag:@"支付中..." toView:self.view]; } NSString *receiptBase64 = [NSString base64StringFromData:self.receipt length:[self.receipt length]]; __weak typeof(self) weakSelf = self; NSMutableDictionary *requestDict = [NSMutableDictionary dictionary]; [requestDict setObject:[DataModelInstance shareInstance].userModel.userId forKey:@"userid"]; [requestDict setObject:[CommonMethod paramStringIsNull:receiptBase64] forKey:@"data"]; [self requstType:RequestType_Post apiName:API_NAME_USER_POST_USER_IAPSECONDVALID paramDict:requestDict hud:hud success:^(AFHTTPRequestOperation *operation, id responseObject, MBProgressHUD *hud) { if([CommonMethod isHttpResponseSuccess:responseObject] == HttpResponseTypeSuccess){ weakSelf.receipt = nil; [hud hideAnimated:YES]; [weakSelf.navigationController popViewControllerAnimated:YES]; }else{ [[[CommonUIAlert alloc] init] showCommonAlertView:self title:@"" message:@"購買失敗,請重試" cancelButtonTitle:@"取消" otherButtonTitle:@"確定" cancle:^{ [hud hideAnimated:YES]; [weakSelf saveReceipt]; } confirm:^{ [weakSelf justifyIAP:hud]; }]; } } failure:^(AFHTTPRequestOperation *operation, NSError *error, MBProgressHUD *hud) { [[[CommonUIAlert alloc] init] showCommonAlertView:self title:@"" message:@"購買失敗,請重試" cancelButtonTitle:@"取消" otherButtonTitle:@"確定" cancle:^{ [hud hideAnimated:YES]; [weakSelf saveReceipt]; } confirm:^{ [weakSelf justifyIAP:hud]; }]; }]; } //持久化儲存使用者購買憑證(這裡最好還要儲存當前日期,使用者id等資訊,用於區分不同的憑證) - (void)saveReceipt{ if(self.receipt && self.receipt.length){ NSString *fileName = [NSString genUUID]; NSString *savedPath = [NSString stringWithFormat:@"%@%@.plist", AppStoreInfoLocalFilePath, fileName]; NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: self.receipt, IAP_RECEIPT,[DataModelInstance shareInstance].userModel.userId,IAP_USER_ID,nil]; BOOL result = [dic writeToFile:savedPath atomically:YES]; NSLog(@"%d",result); self.receipt = nil; } }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; //從伺服器驗證receipt失敗之後,在程式再次啟動的時候,使用儲存的receipt再次到伺服器驗證 NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:AppStoreInfoLocalFilePath]) {//如果在改路下不存在檔案,說明就沒有儲存驗證失敗後的購買憑證,也就是說傳送憑證成功。 [fileManager createDirectoryAtPath:AppStoreInfoLocalFilePath//建立目錄 withIntermediateDirectories:YES attributes:nil error:nil]; }else{//存在購買憑證,說明發送憑證失敗,再次發起驗證 [self sendFailedIapFiles]; } return YES; } //驗證receipt失敗,App啟動後再次驗證 - (void)sendFailedIapFiles{ NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; //搜尋該目錄下的所有檔案和目錄 NSArray *cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:AppStoreInfoLocalFilePath error:&error]; if (error == nil){ for (NSString *name in cacheFileNameArray){ if ([name hasSuffix:@".plist"]){//如果有plist字尾的檔案,說明就是儲存的購買憑證 NSString *filePath = [NSString stringWithFormat:@"%@/%@", AppStoreInfoLocalFilePath, name]; [self sendAppStoreRequestBuyPlist:filePath]; } } } } - (void)sendAppStoreRequestBuyPlist:(NSString *)plistPath{ NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:plistPath]; //這裡的引數請根據自己公司後臺伺服器介面定製,但是必須傳送的是持久化儲存購買憑證 NSData *receipt = [dic objectForKey:IAP_RECEIPT]; if(receipt==nil){ [self sendAppStoreRequestSucceededWithData:plistPath]; return; } NSString *receiptBase64 = [NSString base64StringFromData:receipt length:[receipt length]]; NSMutableDictionary *requestDict = [NSMutableDictionary dictionary]; [requestDict setObject:[dic objectForKey:IAP_USER_ID] forKey:@"userid"]; [requestDict setObject:[CommonMethod paramStringIsNull:receiptBase64] forKey:@"data"]; [[[UIViewController alloc] init] requstType:RequestType_Post apiName:API_NAME_USER_POST_USER_IAPSECONDVALID paramDict:requestDict hud:nil success:^(AFHTTPRequestOperation *operation, id responseObject, MBProgressHUD *hud) { if([CommonMethod isHttpResponseSuccess:responseObject] == HttpResponseTypeSuccess){ [self sendAppStoreRequestSucceededWithData:plistPath]; } } failure:^(AFHTTPRequestOperation *operation, NSError *error, MBProgressHUD *hud) { }]; } //驗證成功就從plist中移除憑證 - (void)sendAppStoreRequestSucceededWithData:(NSString *)plistPath{ NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:plistPath]){ [fileManager removeItemAtPath:plistPath error:nil]; } }