蘋果IAP總結(jié)

最近公司項目要增加內(nèi)購功能,摸索了幾天,遇到不少問題,先記錄下來,有圖有真相,我盡量多貼些圖片。

1、首先你要有一個app,無論上架與否都可以,如果沒有,就新建一個,怎么新建APP就不多說了,有一條要注意,APP的APPID信息里一定要支持In-App Purchases。

2、進(jìn)入iTunes Connect 選擇我的APP,在APP的詳情頁,選擇功能選項,添加APP內(nèi)要出售的物品

添加物品的時候會讓你選擇物品類型,按你的需求選擇就行了,有幾個商品就添加幾個,

注意,物品的資料要填寫完整,必填項一定要填,

審核信息里的圖片要上傳,備注也要寫上,

3、當(dāng)你的物品狀態(tài)是這樣的,就說明可以在代碼里獲取物品了,

當(dāng)然,這時候你要直接寫代碼獲取,肯定是獲取不到的,因為還有一些非APP信息需要補充,想當(dāng)初我就是沒逼著產(chǎn)品完善信息,自己一直找錯誤,浪費了許多時間。

協(xié)議、稅務(wù)和銀行業(yè)務(wù),這里面的信息怎么填寫,這篇文章寫的很詳細(xì),就不多說了,讓產(chǎn)品參考著寫吧。

4、為了測試支付情況,我們還需要新建一個測試用的Apple id,在iTunes Connect里選擇用戶和職能

選擇沙箱技術(shù)測試員,添加測試賬號,可以參考這篇文章。

5,以上都完成之后,終于可以打開編譯器了,對,打開xcode,

打開Xcode 的這個功能。

6,寫代碼吧,

引入#import <StoreKit/StoreKit.h>

遵守協(xié)議

添加監(jiān)聽

[[SKPaymentQueue defaultQueue]addTransactionObserver:self];

(別忘了移除監(jiān)聽)

請求物品列表可以從自己的服務(wù)器,也可以從蘋果的服務(wù)器請求,但是從蘋果請求的話,慢慢等吧,很慢的,而且不穩(wěn)定。

下面的代碼是從點擊物品開始,直到完成付款并通過自己的服務(wù)器驗證。

//點擊購買按鈕- (void)clickPurcaseBtnAction{? ?

?//點擊按鈕的時候判斷app是否允許apple支付??

? if ([SKPaymentQueue canMakePayments]) {? ? ?

?? //請求蘋果后臺商品? ? ?

?? [self requestProductData:_productId];? ?

?}??

? else? ? {? ? ??

? NSLog(@"用戶不允許應(yīng)用內(nèi)購買");??

? }}

//去蘋果服務(wù)器請求商品

-(void)requestProductData:(NSString *)type {??

? //根據(jù)商品ID查找商品信息??

? NSArray *product = [[NSArray alloc] initWithObjects:type, nil];? ?

?NSSet *nsset = [NSSet setWithArray:product];? ?

?//創(chuàng)建SKProductsRequest對象,用想要出售的商品的標(biāo)識來初始化,然后附加上對應(yīng)的委托對象。? ?

?//該請求的響應(yīng)包含了可用商品的本地化信息。??

? SKProductsRequest *request = [[SKProductsRequest alloc]? initWithProductIdentifiers:nsset];? ??

request.delegate = self;? ?

?[request start];

}

// SKProductsRequestDelegate

//查詢成功的回調(diào)

-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:? (SKProductsResponse *)response {? ??

NSArray *product = response.products;? ??

//invalidProductIdentifiers是不被App Store所識別的產(chǎn)品id字符串?dāng)?shù)組,通常為空? ?

?NSLog(@"產(chǎn)品Product ID:%@",response.invalidProductIdentifiers);? ?

?//如果沒應(yīng)用的銀行信息沒填寫完整,你添加的商品會出現(xiàn)在無效商品列表里? ?

?NSLog(@"無效商品列表 :%@",response.invalidProductIdentifiers); ??

?if(product == nil) {??

? ? ? return;??

? }? ??

//數(shù)組的count代表回調(diào)的產(chǎn)品ID數(shù)組的長度? ?

?if (product.count == 0) {? ? ??

? NSLog(@"無法獲得產(chǎn)品信息,購買失敗"); ??

? ? ? ? return; ??

?}? ?

?//SKProduct對象包含了在App Store上注冊的商品的本地化信息。? ?

?SKProduct *storeProduct = nil;??

? for (SKProduct *pro in product) {? ? ?

?? if ([pro.productIdentifier isEqualToString:_productId]) {? ? ? ??

? ? storeProduct = pro;? ? ?

?? }? ?

?}? ?

?if(storeProduct == nil) {? ? ?

?? return;??

? }? ??

//創(chuàng)建一個支付對象,并放到隊列中? ??

self.g_payment = [SKMutablePayment paymentWithProduct:storeProduct];? ?

?//設(shè)置購買的數(shù)量? ?

?self.g_payment.quantity = 1;??

? [[SKPaymentQueue defaultQueue] addPayment:self.g_payment];

}

//查詢失敗的回調(diào)

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {? ?

?NSLog(@"請求商品失敗%@", error);

}

//交易有結(jié)果時會調(diào)用此回調(diào)函數(shù),SKPaymentTransactionObserver - 交易的回調(diào)

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray*)transactions {

for (SKPaymentTransaction *transaction in transactions) {

switch (transaction.transactionState) {

case SKPaymentTransactionStatePurchasing: //正在購買

break;

case SKPaymentTransactionStatePurchased:? //購買成功

[self completedTransaction:transaction Product:self.productId];

break;

case SKPaymentTransactionStateFailed://購買失敗

{

UIAlertView *alt3 = [[UIAlertView alloc]initWithTitle:@"提示" message:@"購買失敗" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];

[alt3 show];

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

}

break;

case SKPaymentTransactionStateRestored:? //恢復(fù)購買

{

//當(dāng)上次購買過程中,因某種原因以外中斷而沒能調(diào)用完成購買的API時,再次進(jìn)入APP蘋果會提醒恢復(fù)購買,可以在這里繼續(xù)處理

}

break;

case SKPaymentTransactionStateDeferred:? //最終狀態(tài)未確認(rèn)

{

UIAlertView *alt5 = [[UIAlertView alloc]initWithTitle:@"提示" message:@"無法獲得產(chǎn)品信息,購買失敗" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];

[alt5 show];

}

break;

default:

break;

}

}

}

-(void)completedTransaction:(SKPaymentTransaction*)transaction Product:(NSString *)productId{

//NSString * productIdentifier = transaction.payment.productIdentifier;

NSString *transactionReceiptString= nil;

NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];

NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];

transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

把transactionReceiptString發(fā)送給自己的服務(wù)器讓后臺驗證就行了,還有一點就是對于sandbox環(huán)境和生產(chǎn)環(huán)境,校驗的地址是不同的,后臺校驗的時候要區(qū)分測試和生產(chǎn)環(huán)境,注意:提交蘋果審核后,審核的時候蘋果用的也是測試環(huán)境,后臺去驗證的時候需要在測試環(huán)境驗證,過審后再去生產(chǎn)環(huán)境驗證。

https://buy.itunes.apple.com/verifyReceipt (生產(chǎn))

https://sandbox.itunes.apple.com/verifyReceipt (sandbox測試)

/* 下面的代碼是客戶端自己驗證的方法

NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", transactionReceiptString];//拼接請求數(shù)據(jù)

NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];

NSURL *url=[NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];

NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];

requestM.HTTPBody = bodyData;

requestM.HTTPMethod=@"POST";

//創(chuàng)建連接并發(fā)送同步請求

NSError *error=nil;

NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];

if (error) {

NSLog(@"驗證購買過程中發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);

return;

}

NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];

NSLog(@"%@",dic);

if([dic[@"status"] intValue]==0){

[[UIApplication sharedApplication].keyWindow makeToast:@"購買成功" duration:1.5 position:CSToastPositionCenter];

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

NSDictionary *dicReceipt= dic[@"receipt"];

NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];

NSString *productIdentifier= dicInApp[@"product_id"];//讀取產(chǎn)品標(biāo)識

//如果是消耗品則記錄購買數(shù)量,非消耗品則記錄是否購買過

//NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];

if ([productIdentifier isEqualToString:@"123"]) {

//? ? ? ? ? ? NSInteger purchasedCount=[defaults integerForKey:productIdentifier];//已購買數(shù)量

//? ? ? ? ? ? [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];

}else{

//? [defaults setBool:YES forKey:productIdentifier];

}

//在此處對購買記錄進(jìn)行存儲,可以存儲到開發(fā)商的服務(wù)器端

}else{

NSLog(@"購買失敗,未通過驗證");

NSString *str = [NSString stringWithFormat:@"失敗了%@",dic[@"status"]];

[[UIApplication sharedApplication].keyWindow makeToast:str duration:1.5 position:CSToastPositionCenter];

}

*/

}

遇到的問題

1、銀行信息不完整造成的無法獲取商品列表

2、蘋果服務(wù)器慢,網(wǎng)絡(luò)問題等造成的購買失敗

3、交給服務(wù)器驗證的字符串,服務(wù)器需要再進(jìn)行UTF8編碼一次再去驗證,否則會返回21002,收據(jù)數(shù)據(jù)不符合格式。

4、順便注上蘋果反饋的錯誤碼:

21000App Store無法讀取你提供的JSON數(shù)據(jù)

21002 收據(jù)數(shù)據(jù)不符合格式

21003 收據(jù)無法被驗證

21004 你提供的共享密鑰和賬戶的共享密鑰不一致

21005 收據(jù)服務(wù)器當(dāng)前不可用

21006 收據(jù)是有效的,但訂閱服務(wù)已經(jīng)過期。當(dāng)收到這個信息時,解碼后的收據(jù)信息也包含在返回內(nèi)容中

21007 收據(jù)信息是測試用(sandbox),但卻被發(fā)送到產(chǎn)品環(huán)境中驗證

21008 收據(jù)信息是產(chǎn)品環(huán)境中使用,但卻被發(fā)送到測試環(huán)境中驗證

現(xiàn)在代碼還比較散亂,只是實現(xiàn)了功能,后面準(zhǔn)備封裝一下,遇到問題再來補充吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iOS應(yīng)用內(nèi)付費(IAP)開發(fā)步驟 1.蘋果iTunes Connect內(nèi)購產(chǎn)品信息錄入。 1)創(chuàng)建app內(nèi)購買項...
    MillerWang閱讀 11,337評論 0 7
  • iOS支付 iOS支付分為兩類,第三方支付和應(yīng)用內(nèi)支付(內(nèi)購)。 第三方支付包括:支付寶支付、微信支付、銀聯(lián)支付、...
    羊駝先生丶閱讀 952評論 0 0
  • 在我們應(yīng)用開發(fā)中我們經(jīng)常在自己的項目中使用到支付,下面我們來談?wù)刬OS這塊的支付;iOS支付主要分為兩類,第三方支...
    Hither閱讀 8,560評論 9 42
  • 添加依賴
    仍是少年呀閱讀 417評論 0 0

友情鏈接更多精彩內(nèi)容