Apple 內(nèi)購流程:
1、向蘋果服務(wù)器止毕,發(fā)送請求模蜡,獲取可購買商品信息。
2扁凛、SKProductsRequest 協(xié)議 忍疾。獲取商品數(shù)據(jù)列表。
3谨朝、確定購買商品(或回復(fù)商品)卤妒。
4、監(jiān)測交易過程字币。
5则披、驗證交易收據(jù)(receipt)。
1洗出、向蘋果服務(wù)器士复,發(fā)送請求,獲取可購買商品信息翩活。
//提交商品信息請求
- (void)requestProductWithProductArray:(NSArray *)productIdAry {
NSSet *set = [[NSSet alloc] initWithArray:productIdAry];
//“異步”請求有哪些可售商品
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
//啟動服務(wù)
[request start];
}
2阱洪、SKProductsRequest 協(xié)議 便贵。獲取商品數(shù)據(jù)列表。
#pragma mark - SKProductsRequest Delegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
//獲取商品數(shù)據(jù)
NSArray *productAry = response.products;
if ([productAry count] <= 0) {
//暫無商品信息
return;
}
//初始化本地商品信息個數(shù)
if (self.productDic == nil) {
self.productDic = [NSMutableDictionary dictionaryWithCapacity:response.products.count];
}
//遍歷商品數(shù)據(jù)
for (SKproduct *product in response.products) {
[self.productDis setObject:product forKey:product.productIdentifier];
//打印產(chǎn)品列表信息
NSLog(@"product ID : %@",product.productIdentifier);
NSLog(@"product Description : %@",product.description);
NSLog(@"product Title :%@",product.localizedTitle);
}
#warning - Todo 可以將商品信息列表傳給ViewController以供顯示商品列表冗荸。
#warning - Todo 但不建議使用此信息顯示商品列表承璃,可以用此信息進行產(chǎn)品ID的驗證。
}
#pragma mark - SKProductsRequest Delegate
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(@"---------------- 錯誤 ---------------");
}
#pragma mark - SKProductsRequest Delegate
- (void)requestDidFinish:(SKPRequest *)request {
NSLog(@"--------------- 反饋信息結(jié)束 ---------------");
}
3俏竞、確定購買商品
#pragma mark - Buy Product
- (void)buyProduct:(NSString *)productID {
//購買商品
SKProduct *buyProduct = self.productDic[productID];
SKPayment *payment = [SKPayment paymentWithProduct:buyProduct];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
4绸硕、回復(fù)商品
#pragma mark - Restore Product
- (void)restorePurchase {
//回復(fù)已經(jīng)完成的所有交易(僅限永久有效商品)。
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
5魂毁、監(jiān)測交易過程
#pragma mark - SKPaymentTransaction Observer
- (void)paymentQueue:(SKPaymentQueue *)queue updateTransactions:(NSArray<SKPaymentTransaction *> *)transaction {
for (SKPaymentTransaction *transaction in transactions) {
NSLog(@"交易隊列狀態(tài) :%@",transactions);
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: //購買成功
// 向蘋果服務(wù)器進行驗證
if (self.checkAfterPay) {
//支付成功了玻佩,并開始向蘋果服務(wù)器進行驗證(若CheckAfterPay為NO,則不會經(jīng)過此步驟)
}else {
//商品完全購買成功且驗證成功了席楚。(若CheckAfterPay為NO咬崔,則會在購買成功后直接觸發(fā)此方法)
}
// 目前都是全部進行驗證。
[self verifyPruchaseWithPaymentTransaction:transaction isTestServer:NO];
break;
case SKPaymentTransactionStatePurchasing: // 正在購買
NSLog(@"正在購買...");
break;
case SKPaymentTransactionStateRestored: // 恢復(fù)成功
// 將交易從交易隊列中刪除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed: // 購買取消或失敗
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(@"購買失敺持取垮斯!");
}else{
NSLog(@"購買取消!");
}
// 將交易從交易隊列中刪除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
default:
// 將交易從交易隊列中刪除
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
6只祠、驗證交易收據(jù)(receipt)
#pragma mark - Verify Receipt
- (void)verifyPruchaseWithPaymentTransaction:(SKPaymentTransaction *)transaction isTestServer:(BOOL)flag {
// 驗證憑據(jù)兜蠕,獲取到蘋果返回的交易憑據(jù)
// appStore Receipt URL iOS7.0增加的,購買交易完成后抛寝,會將憑據(jù)存放在該地址
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// 從沙盒中獲取到購買憑據(jù)receipte
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (!receipt) {
// 交易憑證為空熊杨,驗證失敗
// KIAPPurchVerFailed
return;
}else {
// 購買成功將交易憑證發(fā)送給服務(wù)端進行再次校驗
// kIAppurchSuccess
}
// 在網(wǎng)絡(luò)中傳輸數(shù)據(jù),大多情況下是傳輸?shù)淖址皇嵌M制數(shù)據(jù)
// 傳輸?shù)氖荁ASE64編碼的字符串
/**
BASE64 常用的編碼方案盗舰,通常用于數(shù)據(jù)傳輸晶府,以及加密算法的基礎(chǔ)算法,傳輸過程中能夠保證數(shù)據(jù)傳輸?shù)姆€(wěn)定性
BASE64是可以編碼和解碼的
*/
NSString *encodeStr = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSString *encodeLoad = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
NSData *receiptData = [encodeLoad dataUsingEncoding:NSUTF8StringEncoding];
// 發(fā)送網(wǎng)絡(luò)POST請求钻趋,對購買憑證進行驗證
// In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
// In the real environment, use https://buy.itunes.apple.com/verifyReceipt
NSString *serverStr = @"https://buy.itunes.apple.com/verifyReceipt";
if (flag) {
serverStr = @"https://sandbox.itunes.apple.com/verifyReceipt";
}
NSURL *storeURL = [NSURL URLWithString:serverStr];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:receiptData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
// 無法連接服務(wù)器,購買校驗失敗
// KIAPPurchVerFailed
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
NSLog(@"----驗證結(jié)果 %@",jsonResponse);
if (!jsonResponse) {
// 蘋果服務(wù)器校驗數(shù)據(jù)返回為空校驗失敗
// KIAPPurchVerFailed
}
// 先驗證正式服務(wù)器,如果正式服務(wù)器返回21007再去蘋果測試服務(wù)器驗證,沙盒測試環(huán)境蘋果用的是測試服務(wù)器
NSString *status = [NSString stringWithFormat:@"%@",jsonResponse[@"status"]];
if (status && [status isEqualToString:@"21007"]) {
#warning - Todo 給ViewController提示
[self verifyPruchaseWithPaymentTransaction:transaction isTestServer:YES];
}else if(status && [status isEqualToString:@"0"]){
#warning - Todo 給ViewController提示
// kIAPPurchVerSuccwss 購買成功切驗證成功
}
}
}];
// 驗證成功與否都注銷交易,否則會出現(xiàn)虛假憑證信息一直驗證不通過,每次進程序都得輸入蘋果賬號
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
PS:驗證流程
訂單正確性的驗證:
1.iOS客戶端(購買成功)→ 到蘋果服務(wù)器驗證 → 蘋果服務(wù)器返回驗證結(jié)果川陆,做相應(yīng)處理
2.iOS客戶端(購買成功)→ 后臺 → 蘋果服務(wù)器驗證 → 蘋果服務(wù)器返回驗證結(jié)果,做相應(yīng)處理
服務(wù)器要做的是:
1.接收iOS前端發(fā)過來的購買憑證蛮位。
2.判斷憑證是否已經(jīng)存在或驗證過较沪,然后存儲該憑證。
3.將該憑證發(fā)送到對應(yīng)環(huán)境下的蘋果服務(wù)器驗證失仁,并將驗證結(jié)果返回給客戶端购对。
4.根據(jù)需求,是否修改用戶相應(yīng)信息陶因。
注意事項
1.bundleID要與iTunes Connect上你App的相同骡苞,不然是請求不到產(chǎn)品信息的
2.在沙盒環(huán)境進行測試內(nèi)購的時候,要使用沒有越獄的蘋果手機。
3.在沙盒環(huán)境下真機測試內(nèi)購時解幽,請去app store中注銷你的apple ID贴见,不然發(fā)起支付購買請求后會直接case:SKPaymentTransactionStateFailed。使用沙盒測試員的賬號時不需要真正花錢的躲株。
4.如果只添加了一個沙盒測試員賬號片部,當一個真機已經(jīng)使用了這個賬號,另一個真機再使用這個賬號支付也是會發(fā)生錯誤的霜定。那就去多建幾個沙盒測試員賬號使用不同的档悠,反正也是免費的,填寫也很快望浩。
5.監(jiān)聽購買結(jié)果辖所,當失敗和成功時代碼中要調(diào)用:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
該方法通知蘋果支付隊列該交易已完成,不然就會已發(fā)起相同 ID 的商品購買就會有此項目將免費恢復(fù)的提示磨德。
請在本地做一下憑證存儲
現(xiàn)在訂單正確性的驗證是:iOS客戶端(購買成功)→ 后臺→后臺到蘋果服務(wù)器驗證→處理后臺返回結(jié)果做相應(yīng)邏輯處理缘回。
注意:
如果Your App 和 Your Server 中斷了鏈接
當我們前端購買成功后,憑證(receipt)本地保留一份典挑,當與后臺驗證成功后酥宴,再將本地保留的憑證刪除。
否者一直使用本地已經(jīng)保留的憑證與后臺交互您觉。
測試前提條件:
1.在itunesConnect中填寫測試賬號拙寡。
2.在itunesConnect中填寫稅務(wù)單(就是銀行賬號,開戶名琳水,收款機構(gòu)等等的稅務(wù)單)倒庵。
3.交易收據(jù)內(nèi)容(receipt)
"receipt":
{
"original_purchase_date_pst":"2015-06-22 20:56:34 America/Los_Angeles", //購買時間,太平洋標準時間
"purchase_date_ms":"1435031794826", //購買時間毫秒
"unique_identifier":"5bcc5503dbcc886d10d09bef079dc9ab08ac11bb",//唯一標識符
"original_transaction_id":"1000000160390314", //原始交易ID
"bvrs":"1.0",//iPhone程序的版本號
"transaction_id":"1000000160390314", //交易的標識
"quantity":"1", //購買商品的數(shù)量
"unique_vendor_identifier":"AEEC55C0-FA41-426A-B9FC-324128342652", //開發(fā)商交易ID
"item_id":"1008526677",//App Store用來標識程序的字符串
"product_id":"cosmosbox.strikehero.gems60",//商品的標識
"purchase_date":"2015-06-23 03:56:34 Etc/GMT",//購買時間
"original_purchase_date":"2015-06-23 03:56:34 Etc/GMT", //原始購買時間
"purchase_date_pst":"2015-06-22 20:56:34 America/Los_Angeles",//太平洋標準時間
"bid":"com.cosmosbox.StrikeHero",//iPhone程序的bundle標識
"original_purchase_date_ms":"1435031794826"http://毫秒
}