ios 應用內支付(In-App Purchase,沙箱測試,後台驗證)

來源:互聯網
上載者:User

ios 應用內支付(In-App Purchase,沙箱測試,後台驗證)

 

1.蘋果iTunes Connect內購產品資訊錄入。

 

1)建立app內購買項目(Create New),選擇類型:

1.消耗型項目

對於消耗型 App 內購買項目,使用者每次下載時都必須進行購買。一次性服務通常屬於消耗型項目,例如釣魚App 中的魚餌。

2.非消耗型項目

對於非消耗型 App 內購買項目,使用者僅需要購買一次。不會到期或隨使用而減少的服務通常為非消耗型項目,例如遊戲App 的新跑道。

3.自動續訂訂閱

通過自動續訂訂閱,使用者可以購買指定時間期限內的更新和動態內容。除非使用者取消選擇,否則訂閱(例如雜誌訂閱等)會自動續訂。

4.免費訂閱

通過免費訂閱,開發人員可以將免費訂閱內容放入“報刊雜誌”。使用者註冊免費訂閱後,該訂閱內容將會出現在與該使用者Apple ID 關聯的所有裝置上。請注意,免費訂閱不會到期,並且僅在支援報刊雜誌功能的 App 中提供。

5.非續訂訂閱

非續訂訂閱允許有時限性的營銷服務。對於 App 內購買項目中的限時訪問內容,就需使用非續訂訂閱。例如,導航App 中語音導航功能的一周訂閱,或者年度訂閱已存檔的視頻或音訊線上目錄。

一定要根據自己應用的情況選擇正確,不然會被App Store審核團隊拒絕。應用內的虛擬幣要採用消耗型的,有固定時限的會員選擇自動續訂訂閱。也可以只選擇虛擬幣儲值自己後台購買的情況解決會員問題。

2)產生共用密鑰

共用密鑰是在您聯絡我們的伺服器擷取 App 內購買項目收據時使用的唯一代碼。沒有共用密鑰,您將無法在沙箱技術模式下測試自動續訂 App 內購買項目。另外,共用密鑰不能在 App Store 使用。

註:無論與哪個 App 相關聯,您的所有自動續訂訂閱都將使用同一共用密鑰。

此共用密鑰用於後台伺服器驗證使用者購買項目的憑證,產生新密要伺服器也立即改變驗證密鑰。共用密鑰在驗證自動續訂訂閱類型項目的時候必須需要。

3)內購項目的狀態

A) Pending Developer Approval – Your in apppurchase has been created but has not been tested in a sandbox environment andapproved by you.

B) Approved By Developer – Your in apppurchase has been tested in a sandbox environment and has been approved by you.

C) Waiting For Review – You have submittedyour in app purchase to be reviewed by Apple.

D) In Review – Your in app purchase iscurrently being reviewed by Apple no edits can be made.

E) Developer Action Required – In app purchasedetail changes that you submitted have been rejected. You are required to takeaction to edit the detail information or cancel the request to change thedetail information before this in app purchase can be reviewed again.(內購項目詳情介面會提示那個地方出現了問題,稍微修改一下再次提交就行了)

F) Ready for Sale – Apple has approved your inapp purchase to go live on the App Store with its associated application. Thein app purchase must be cleared for sale in iTunes Connect to be Ready forSale.

G) Rejected – Apple has rejected your in apppurchase during the review process. If you have not already been contacted byApple with more information about your rejection, you may inquire through theContact Us section of iTunes Connect. A rejected in app purchase cannot bereinstated. You must create a new in app purchase if you still wish for it tobe sold.

H) Developer Removed from Sale – You havemarked your in app purchase as not cleared for sale in iTunes Connect.

2.app端程式碼編寫(代碼僅供參考)

#pragma mark - 支付以單利的形式展開

+(PurchasesObject*)SharePurchases

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

if (_purchase == nil) {

 

_purchase = [[super alloc]init];

[[SKPaymentQueue defaultQueue]addTransactionObserver:_purchase];

}

});

return _purchase;

}

+(id)allocWithZone:(struct _NSZone *)zone

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

if (_purchase == nil) {

 

_purchase = [[super allocWithZone:zone]init];

}

});

return _purchase;

}

+(id)alloc

{

return _purchase;

 

}

#pragma mark -支付鑽石會員

_alter = [[UIAlertView alloc]initWithTitle:@遊客模式購買僅限當前裝置使用所購買的許可權,推薦您登入購買 message:nil delegate:self cancelButtonTitle:@取消 otherButtonTitles:@登陸購買(推薦),@遊客模式購買 ,nil];

//遊客購買很重要,會被AppStore審核團隊拒絕。

#pragma mark -開始支付,根據錄入內購項目的產品id去AppStore請求產品資訊。

if ([SKPaymentQueue canMakePayments]) {

NSSet * set = [NSSet setWithArray:@[ProductID]];

SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];

request.delegate = self;

[request start];

 

}

else

{

NSLog(@無許可權購買);

}

 

#pragma mark -SKProductsRequestDelegate 擷取appstroe產品資訊

- (void)productsRequest:(SKProductsRequest *)requestdidReceiveResponse:(SKProductsResponse *)response {

self.mySelfView.userInteractionEnabled = NO;

[AFTools showHUD:@擷取產品資訊 atView:self.mySelfView];

NSLog(@-----------收到產品反饋資訊--------------);

NSArray *myProduct = response.products;

if (myProduct.count == 0) {

[AFTools alertWithTitle:@購買失敗 message:@無法擷取產品資訊];

NSLog(@無法擷取產品資訊,購買失敗。);

return;

}

NSLog(@產品products==%@,myProduct);

NSLog(@產品id==%@,response.invalidProductIdentifiers);

NSLog(@產品數量==========%lu,(unsigned long)myProduct.count);

for(SKProduct *product in myProduct){

// SKMutablePayment

NSLog(@SKProduct描述資訊%@, [product description]);

NSLog(@產品標題 %@ , product.localizedTitle);

NSLog(@產品描述資訊: %@ , product.localizedDescription);

NSLog(@價格: %@ , product.price);

NSLog(@Product id: %@ , product.productIdentifier);

SKMutablePayment *MpayMent = [SKMutablePayment paymentWithProduct:product];

NSLog(@===%@,MpayMent.requestData);

if ([[SKPaymentQueue defaultQueue]respondsToSelector:@selector(addPayment:)]){

[[SKPaymentQueue defaultQueue] addPayment:MpayMent];

}

else

{

}

}

}

#pragmamark-SKPaymentTransactionObserver支付結果

- (void)paymentQueue:(SKPaymentQueue *)queueupdatedTransactions:(NSArray *)transactions

{

 

for (SKPaymentTransaction *transaction in transactions)

{

switch (transaction.transactionState)

{

case SKPaymentTransactionStatePurchased://交易完成

NSLog(@交易完成transactionIdentifier= %@, transaction.transactionIdentifier);

[self completeTransaction:transaction];

break;

case SKPaymentTransactionStateFailed://交易失敗

[self failedTransaction:transaction];

NSLog(@交易失敗);

break;

case SKPaymentTransactionStateRestored://已經購買過該商品

[self restoreTransaction:transaction];

NSLog(@已買過商品);

break;

case SKPaymentTransactionStatePurchasing://商品添加進列表

NSLog(@商品添加進列表);

break;

default:

break;

}

}

 

}

- (void)paymentQueue:(SKPaymentQueue *)queueremovedTransactions:(NSArray *)transactions

{

NSLog(@---------------移除-------------);

}

- (void)paymentQueue:(SKPaymentQueue *)queuerestoreCompletedTransactionsFailedWithError:(NSError *)error

{

NSLog(@---------------重複支付失敗-------------);

}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {

NSLog(@-------------------支付完成--------------------);

[self commitSeversSucceeWithTransaction:transaction];

 

}

 

-(void)restoreTransaction: (SKPaymentTransaction *)transaction

{

NSLog(@----------------重複支付-----------------);

[self commitSeversSucceeWithTransaction:transaction];

}

- (void)commitSeversSucceeWithTransaction:(SKPaymentTransaction *)transaction

{

NSString * productIdentifier = transaction.payment.productIdentifier;

NSString *transactionReceiptString= nil;

//系統IOS7.0以上擷取支付驗證憑證的方式應該改變,切驗證返回的資料結構也不一樣了。

if(IOSSystemVersion>=7.0)

{

NSURLRequest*appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];

NSError *error = nil;

NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequestreturningResponse:nil error:&error];

transactionReceiptString = [receiptDatabase64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

}

else

{

NSData * receiptData = transaction.transactionReceipt;

transactionReceiptString = [receiptDatabase64EncodedString];

}

// 向自己的伺服器驗證購買憑證

}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {

 

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

}

最好在用戶端上鍵一個資料庫,跟蹤訂單的狀態,防止使用者訂單在某個環節出現問題時無法尋找到訂單進行二次處理。

去AppStore請求資料時有時候會出現錯誤,你可以iTunes connect裡的connect us去給他們寫郵件反饋問題。但是大部分時間你等等就能解決了,對就是什麼也不做等著。也許那一天他就好了。

3.後台伺服器驗證

IOS 內支付有兩種模式:

1) 內建模式

2) 伺服器模式

內建模式的流程可以簡單的總結為以下幾步:

1) app從app store 擷取產品資訊

2) 使用者選擇需要購買的產品

3) app發送支付請求到app store

4) app store 處理支付請求,並返回transaction資訊

5) app將購買的內容展示給使用者

伺服器模式的主要流程如下所示:

1) app從伺服器擷取產品識別欄位表

2) app從app store 擷取產品資訊

3) 使用者選擇需要購買的產品

4) app 發送 支付請求到app store

5) app store 處理支付請求,返回transaction資訊

6) app 將transaction receipt 發送到伺服器

7) 伺服器收到收據後發送到app stroe驗證收據的有效性

8) app store 返回收據的驗證結果

9) 根據app store 返回的結果決定使用者是否購買成功

上述兩種模式的不同之處主要在於:交易的收據驗證,內建模式沒有專門去驗證交易收據,而伺服器模式會使用獨立的伺服器去驗證交易收據。內建模式簡單快捷,但容易被破解。伺服器模式流程相對複雜,但相對安全。

開發之初,蘋果方就很負責的告知:我們的伺服器不穩定。真正開發之後,發現蘋果方果然是很負責的,不僅是不穩定,而且足夠慢。app store server驗證一個收據需要3-6s時間。

1.使用者能否忍受3-6s的等待時間

2.如果app store server 宕機,如何確保成功付費的使用者能夠得到正常服務。

對於第一個問題,我們有理由相信使用者完全無法忍受,所以採用非同步驗證的方式,伺服器收到用戶端的請求後,就將請求放到MCQ中去處理。

對於第二個問題,由於蘋果人員很負責人的告知:我們的伺服器不穩定,所以不排除收據驗證逾時的情況。對於驗證逾時的收據,儲存到資料庫中並標記為驗證逾時,定時任務每隔一定的時間去app store驗證,確保能夠擷取收據的驗證結果。

在開發過程中,需要測試應用是否能夠正常的進行支付,但是又不能進行實際的支付,因此需要使用蘋果提供的sandbox Store測試。Store Kit不能在iOS模擬器中使用,測試Store必須在真機上進行。

在sandbox中驗證receipt

https://sandbox.itunes.apple.com/verifyReceipt

在生產環境中驗證receipt

https://buy.itunes.apple.com/verifyReceipt

在實際開發過程中,伺服器端通過issandbox欄位標識用戶端傳遞的收據是沙箱環境中的收據還是生產環境中的收據。在提交蘋果審核前,沙箱測試均無問題。提交蘋果審核後,被告知購買失敗,審核未通過。通過查詢日誌發現,用戶端發送的交易收據為沙箱收據,但是issandbox欄位卻標識為生產環境。

結論:蘋果審核app時,仍然在沙箱環境下測試。但是用戶端同事在app提交蘋果審核時,將issandbox欄位寫死,設定為生產環境。這樣就導致沙箱收據發送到https://buy.itunes.apple.com/verifyReceipt去驗證。

那麼如何自動的識別收據是否是sandbox receipt呢?

識別沙箱環境下收據的方法有兩種:

1.根據收據欄位 environment = sandbox。

2.根據收據驗證介面返回的狀態代碼

如果status=21007,則表示當前的收據為沙箱環境下收據, t進行驗證。

蘋果反饋的狀態代碼;

21000App Store無法讀取你提供的JSON資料
21002 收據資料不符合格式
21003 收據無法被驗證
21004 你提供的共用密鑰和賬戶的共用密鑰不一致
21005 收據伺服器當前不可用
21006 收據是有效,但訂閱服務已經到期。當收到這個資訊時,解碼後的收據資訊也包含在返回內容中
21007 收據資訊是測試用(sandbox),但卻被發送到產品環境中驗證
21008 收據資訊是產品環境中使用,但卻被發送到測試環境中驗證

先生產驗證後測實驗證,可以避免來回切換介面的麻煩。測實驗證只要用你自己申請的測試appid的時候才會用到,使用者不會擁有測試appid,所以不會走到測實驗證這一步。即使生產驗證出錯,應該也不回返回21007狀態嗎。測實驗證通過的使用者名稱,和儲值金額最好用資料庫記錄下來,方便公司資金核對。

 


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.