1、In App Purchase概覽安全
Store Kit表明App和App Store之間進行通訊。程序將從App Store接收那些你想要提供的產品的信息,並將它們顯示出來供用戶購買。
當用戶須要購買某件產品時,程序調用StoreKit來收集購買信息。下圖即爲基本的store kit 模型:服務器
Store Kit的API只是爲程序添加In App Purchase功能的一小部分。你須要決定如何去記錄那些你想要提交的產品,如何在程序中將商店功能展示給用戶,
還要考慮如何將用戶購買的產品提交。本章的剩餘部分會展現整個流程。併發
Products
產品能夠是任意一項你想要出售的特性。產品在iTunes Connect中被組織,這和你添加一個新的App是同樣的。支持的產品種類共有四種:
1. 內容型。包括電子書,電子雜誌,照片,插圖,遊戲關卡,遊戲角色,和其餘的數字內容。
2. 擴展功能。這些功能已經包含在App內部。在未購買以前被鎖定。例如,你能夠在一個遊戲程序中包含若干個小遊戲,用戶能夠分別來購買這些遊戲。
3. 服務。容許程序對單次服務收費。好比錄音服務。
4. 訂閱。支持對內容或服務的擴展訪問。例如,你的程序能夠每週提供財務信息或遊戲門戶網站的信息。應該設定一個合理的更新週期,以免過於頻繁的
提示困擾用戶。要記住:你將負責跟蹤訂閱的過時信息,而且管理續費。App Store不會替你監視訂閱的週期,也不提供自動收費的機制。app
In App Purchase爲建立產品提供了一種通用的機制,如何操做將由你負責。當你設計程序的時候,有如下幾點須要注意:異步
1. 你必須提供電子類產品和服務。不要使用In App Purchase 去出售實物和實際服務。
2. 不能提供表明中介貨幣的物品,由於讓用戶知曉他們購買的商品和服務是很重要的。ide
2. 服務器類型
使用這終方式,要提供另外的服務器將產品發送給程序。 服務器交付適用於訂閱、內容類商品和服務,由於商品能夠做爲數據發送,而不需改動程序束。 例如,一個遊戲提供的新的內容(關卡等)。 Store Kit不會對服務器端的設計和交互作出定義,這方面工做須要你來完成。 並且,Store Kit不提供驗證用戶身份的機制,你須要來設計。 若是你的程序須要以上功能,例如,紀錄特定用戶的訂閱計劃, 你須要本身來設計和實現。函數
圖1-3 展現了服務器類型的購買過程。測試
1. 程序向服務器發送請求,得到一份產品列表。
2. 服務器返回包含產品標識符的列表。
3. 程序向App Store發送請求,獲得產品的信息。
4. App Store返回產品信息。
5. 程序把返回的產品信息顯示給用戶(App的store界面)
6. 用戶選擇某個產品
7. 程序向App Store發送支付請求
8. App Store處理支付請求並返回交易完成信息。
9. 程序從信息中得到數據,併發送至服務器。
10. 服務器紀錄數據,並進行審(咱們的)查。
11. 服務器將數據發給App Store來驗證該交易的有效性。
12. App Store對收到的數據進行解析,返回該數據和說明其是否有效的標識。
13. 服務器讀取返回的數據,肯定用戶購買的內容。
14. 服務器將購買的內容傳遞給程序。網站
Apple建議在服務器端存儲產品標識,而不要將其存儲在plist中。 這樣就能夠在不升級程序的前提下添加新的產品。ui
在服務器模式下, 你的程序將得到交易(transaction)相關的信息,並將它發送給服務器。服務器能夠驗證收到的數據,並將其解碼以肯定須要交付的內容。 這個流程將在「驗證store收據」一節討論。
對於服務器模式,咱們有安全性和可靠性方面的顧慮。 你應該測試整個環境來避免威脅。《Secure Coding Guide》文檔中有相關的提示說明。
雖然非消耗性商品能夠用內置模式來恢復,訂閱類商品必須經過服務器來恢復。你要負責紀錄訂閱信息、恢復數據。
消耗類商品也能夠經過服務器方式來紀錄。例如,由服務器提供的一項服務, 你可能須要用戶在多個設備上從新得到結果。
(這段翻譯的比較生硬,由於我我的也沒有機會把各類類型的服務跑一遍,後續會檢查並修改。但願你們一塊兒來看看,歡迎補充。)
取得產品信息
要在程序內部顯示「商店」,須要從App Store獲得信息來購建界面。 本章詳細講解如何從App Store獲取產品信息。
向App Store發送請求
Store Kit提供了從App Store上請求數據的通用機制。 程序能夠建立並初始化一個request對象, 爲其附加delegate, 而後啓動請求過程。請求將被髮送到App Store,在那裏被處理。 處理完成時, request對象的delegate方法將被異步調用,以得到請求的結果。 圖2-1顯示了請求的數據模型。
若是程序在請求期間退出,則須要從新發送請求。
下面講解請求過程當中用到的類:
SKRequest
SKRequest爲request的抽象根類。
SKRequestDelegate
SKRequestDelegate是一個protocol, 實現用以處理請求結果的方法,好比請求成功,或請求失敗。
發送得到產品信息的請求
程序使用products request來得到產品的信息。 要完成這一過程,程序需建立一個request對象,其中會包含一個產品標識的列表。以前提到過,你的程序既能夠內置產品列表,又能夠經過外部服務器來得到。
當發送請求時,產品標識會傳送到App Store,App Store將會返回本地化信息(這些信息事先已經在iTunes Connect中設置好了),你將使用這些信息來購建內置商店的界面(顯示商品名,描述,等等)。 圖2-2顯示了請求的過程。
SKProductsRequest
用來請求商品的信息。 建立時,咱們將須要顯示的商品列表加入該對象。
SKProductsRequestDelegate
該protocol定義了處理App Store響應的方法。
SKProductsResponse
SKProductsResponse對象爲App Store返回的響應信息。裏面包含兩個列表(固然是NSArray了):一是通過驗證有效的商品,
@property(nonatomic, readonly) NSArray *products
另一個是沒法被識別的商品信息:
@property(nonatomic, readonly) NSArray * invalidProductIdentifiers
有幾種緣由將形成商品標識沒法被識別,如拼寫錯誤(固然),被標記爲不可出售(unavailable for sale),或是對商品信息的改變沒有傳送到全部App Store的服務器。(這個緣由不是很清楚,再議)。
SKProduct
SKProduct對象包含了在App Store上註冊的商品的本地化信息。
購買商品
當用戶準備購買商品時,程序向App Store請求支付信息,而後App Store將會建立持久化的交易信息,並繼續處理支付流程,即便用戶重啓程序,這個過程亦是如此。App Store同步待定交易的列表到程序中,並在交易狀態發生改變時向程序發送更新的數據。
收集支付信息
要收集支付信息, 你的程序能夠建立一個payment的對象,將它放到支付隊列中,如圖3-1所示。
1. 一個SKPayment的對象,包含了"Sword"的商品標識,而且制定購買數量爲1。
2. 使用addPayment:方法將SKPayment的對象添加到SKPaymentQueue裏。
3. SKPaymentmentQueue包含的全部請求商品,
4. 使用SKPaymentTransactionObserver的paymentQueue: updatedTransactions: 方法來檢測全部完成的購買,併發送購買的商品。
5. 最後,使用finishTransaction:方法完成交易。
當payment的對象被添加到支付隊列中的時候, 會建立一個持久保存的transaction對象來存放它。 當支付被處理後,transaction被更新。 程序中將實現一個觀察者(observer)對象來獲取transaction更新的消息。 觀察者應該爲用戶提供購買的商品,而後將transaction從隊列中移除。
下面介紹在購買過程當中用到的幾個類:
SKPayment
要收集支付信息,先要了解一下支付對象。 支付對象包含了商品的標識(identifier)和要購買商品的數量(quantity)(數量可選)。你能夠把同一個支付對象重複放入支付隊列,,每一次這樣的動做都至關於一次獨立的支付請求。
用戶能夠在Settings程序中禁用購買的功能。 所以在請求支付以前,程序應該首先檢查支付是否能夠被處理。 調用SKPaymentQueue的canMakePayments方法來檢查。
SKPaymentQueue
支付隊列用以和App Store之間進行通訊。 當新的支付對象被添加到隊列中的時候, Store Kit向App Store發送請求。 Store Kit將會彈出對話框詢問用戶是否肯定購買。 完成的交易將會返回給程序的observer對象。
SKPaymentTransaction
transaction對象在每次添加新的payment到隊列中的時候被建立。 transaction對象包含了一些屬性,可讓程序肯定當前的交易狀態。
程序能夠從支付隊列那裏獲得一份審覈中的交易列表,但更經常使用的作法仍是等待支付隊列告知交易狀態的更新。
SKPaymentTransactionObserver
在程序中實現SKPaymentTransactionObserver的協議,而後把它做爲SKPaymentQueue對象的觀察者。該觀察者的主要職責是:檢查完成的交易,交付購買的內容,和把完成後的交易對象從隊列中移除。
在程序一啓動,就應該爲支付隊列指定對應的觀察者對象,而不是等到用戶想要購買商品的時候。 Transaction對象在程序退出時不會丟失。程序重啓時, Store Kit繼續執行未完成的交易。 在程序初始化的時候添加觀察者對象,能夠保證全部的交易都被程序接收(也就時說,若是有未完成的transaction,若是程序重啓,就從新開始了,若是稍候再添加觀察者,就可能會漏掉部分交易的信息)。
恢復交易信息(Transactions)
當transaction被處理並從隊列移除以後,正常狀況下,程序就再也看不到它們了。 若是你的程序提供的是非消耗性的或是訂閱類的商品,就必須提供restore的功能,使用戶能夠在其餘設備上從新存儲購買信息。
Store Kit提供內建的功能來從新存儲非消耗商品的交易信息。 調用SKPaymentQueue的restoreCompletedTransactions的方法來從新存儲。對於那些以前已經完成交易的非消耗性商品,Apple Store生成新的,用於恢復的交易信息。 它包含了原始的交易信息。你的程序能夠拿到這個信息,而後繼續爲購買的功能解鎖。 當以前全部的交易都被恢復時, 就會調用觀察者對象的paymentQueueRestoreCompletedTransactionsFinished方法。
若是用戶試圖購買已經買過的非消耗性商品,程序會收到一個常規的交易信息,而不是恢復的交易信息。可是用戶不會被再次收費。程序 應把這類交易和原始的交易同等對待。
訂閱類服務和消耗類商品不會被Store Kit自動恢復。 要恢復這些商品,你必須在用戶購買這些商品時,在你本身的服務器上記錄這些交易信息, 而且爲用戶的設備提供恢復交易信息的機制。
在程序中添加Store功能
本章爲添加購買功能的指導
詳細流程:
準備工做固然是添加StoreKit.framework了。
而後是具體的步驟:
1. 決定在程序內出售的商品的類型。
以前提到過,程序內能夠出售的新feature類型是有限制的。 Store Kit不容許咱們下載新的代碼。 你的商品要麼能夠經過當前的代碼工做(bundle類型),要麼能夠經過服務器下載(固然,這裏下載的爲數據文件,代碼是不能夠的)。 若是要修改源代碼,就只能老實的升級了。
2. 經過iTunes Connect註冊商品
每次添加新商品的時候都須要執行這一步驟。 每一個商品都須要一個惟一的商品標識。 App Store經過這個標識來查找商品信息並處理支付流程。 註冊商品標識的方法和註冊程序的方法相似。
要了解如何建立和註冊商品信息,請參考「iTunes Connect Developer Guide」文檔。
3. 檢測是否能夠進行支付
用戶能夠禁用在程序內部支付的功能。在發送支付請求以前,程序應該檢查該功能是否被開啓。程序可在顯示商店界面以前就檢查該設置(沒啓用就不顯示商店界面了),也能夠在用戶發送支付請求前再檢查,這樣用戶就能夠看到可購買的商品列表了。
例子:
- if([SKPaymentQueue canMakePayments])
- {
- ...//Display a store to the user
- }
- else
- {
- ...//Warn the user that purchases are disabled.
- }
4. 得到商品的信息
程序建立SKProductsRequest對象,用想要出售的商品的標識來初始化, 而後附加上對應的委託對象。 該請求的響應包含了可用商品的本地化信息。
- //這裏發送請求
- - (void)requestProductData
- {
- SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:
- [NSSet setWithObject: kMyFeatureIdentifier]];
- request.delegate = self;
- [request start];
- }
- //這個是響應的delegate方法
- - (void)productsRequest: (SKProductsRequest *)request
- didReceiveResponse: (SKProductsResponse *)response
- {
- NSArray *myProduct = response.products;
- //生成商店的UI
- [request autorelease];
- }
5. 添加一個展現商品的界面
Store Kit不提供界面的類。 這個界面須要咱們本身來設計並實現。
6. 爲支付隊列(payment queue)註冊一個觀察者對象
你的程序須要初始化一個transaction observer對象並把它指定爲payment queue的觀察者。
上代碼:
- MyStoreObserver *observer = [[MyStoreObserver alloc]init];
- [[SKPaymentQueue defaultQueue]addTransactionObserver: observer];
應該在程序啓動的時候就添加好觀察者,緣由前面說過,重啓後程序會繼續上次未完的交易,這時就添加觀察者對象就不會漏掉以前的交易信息。
7. 在MyStoreObserver類中執行paymentQueue: updatedTransactions: 方法。
這個方法會在有新的交易被建立,或者交易被更新的時候被調用。
- - (void)paymentQueue: (SKPaymentQueue *)queue updatedTransactions: (NSArray *)transactions
- {
- for(SKPaymentTransaction * transaction in transactions)
- {
- switch(transaction.transactionState)
- {
- case SKPaymentTransactionStatePurchased:
- [self completeTransaction: transaction];
- break;
- case SKPaymentTransactionStateFailed:
- [self failedTransaction: transaction];
- break;
- case SKPaymentTransactionStateRestored:
- [self restoreTransaction: transaction];
- default:
- break;
- }
- }
- }
上面的函數針對不一樣的交易返回狀態,調用對應的處理函數。
8. 觀察者對象在用戶成功購買一件商品時,提供相應的內容,如下是在交易成功後調用的方法
- - (void) completeTransaction: (SKPaymentTransaction *)transaction
- {
- //你的程序須要實現這兩個方法
- [self recordTransaction: transaction];
- [self provideContent: transaction.payment.productIdentifier];
- //將完成後的交易信息移出隊列
- [[SKPaymentQueue defaultQueue]finishTransaction: transaction];
- }
交易成功的信息包含transactionIdentifier和transactionReceipt的屬性。其中,transactionReceipt記錄了支付的詳細信息,這個信息能夠幫助你跟蹤、審(咱們的)查交易,若是你的程序是用服務器來交付內容,transactionReceipt能夠被傳送到服務器,而後經過App Store驗證交易。(以前提到的server模式,能夠參考之前的圖)
9. 若是交易是恢復過來的(restore),咱們用這個方法來處理:
- - (void) restoreTransaction: (SKPaymentTransaction *)transaction
- {
- [self recordTransaction: transaction];
- [self provideContent: transaction.payment.productIdentifier];
- [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
- }
這個過程完成購買的過程相似。 恢復的購買內容提供一個新的交易信息,這個信息包含了新的transaction的標識和receipt數據。 若是須要的話,你能夠把這些信息單獨保存下來,供追溯審(咱們的)查之用。但更多的狀況下,在交易完成時,你可能須要覆蓋原始的transaction數據,並使用其中的商品標識。
10. 交易過程失敗的話,咱們調用以下的方法:
- - (void)failedTransaction: (SKPaymentTransaction *)transaction
- {
- if(transaction.error.code != SKErrorPaymentCancelled)
- {
- //在這類顯示除用戶取消以外的錯誤信息
- }
- [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
- }
一般狀況下,交易失敗的緣由是取消購買商品的流程。 程序能夠從error中讀出交易失敗的詳細信息。
顯示錯誤信息不是必須的,但在上面的處理方法中,須要將失敗的交易從支付隊列中移除。 通常來講,咱們用一個對話框來顯示錯誤信息,這時就應避免將用戶取消購買這個error顯示出來。
11. 組織好程序內「商店」的UI。當用戶選擇一件商品時, 建立一個支付對象,並放到隊列中。
- SKPayment *payment = [SKPayment paymentWithProductIdentifier: kMyFeatureIdentifier];
- [[SKPaymentQueue defaultQueue] addPayment: payment];
若是你的商店支持選擇同一件商品的數量,你能夠設置支付對象的quantity屬性
- SKMutablePayment *payment = [SKMutablePayment paymentWithProductIdentifier: kMyFeatureIdentifier];
- payment.quantity = 3;
- [[SKPaymentQueue defaultQueue] addPayment: payment];
下一步:
本章中所示代碼可用於內置型商品模式(Built-in)。 若是你的程序要使用服務器來發布商品,你須要負責設計和執行iPhone程序和你的服務器之間的通訊。服務器應該驗證數據併爲程序提供內容。
驗證store的收據
使用服務器來交付內容,咱們還須要作些額外的工做來驗證從Store Kit發送的收據信息。
重要信息:來自Store的收據信息的格式是專用的。 你的程序不該直接解析這類數據。可以使用以下的機制來取出其中的信息。
驗證App Store返回的收據信息
當交易完成時,Store Kit告知payment observer這個消息,並返回完成的transaction。 SKPaymentTransaction的transactionReceipt屬性就包含了一個通過簽名的收據信息,其中記錄了交易的關鍵信息。你的服務器要負責提交收據信息來肯定其有效性,並保證它未通過篡改。 這個過程當中,信息被以JSON數據格式發送給App Store,App Store也以JSON的格式返回數據。
(你們能夠先了解一下JSON的格式)
驗證收據的過程:
1. 從transaction的transactionReceipt屬性中獲得收據的數據,並以base64方式編碼。
2. 建立JSON對象,字典格式,單鍵值對,鍵名爲"receipt-data", 值爲上一步編碼後的數據。效果爲:
- {
- "receipt-data" : "(編碼後的數據)"
- }
3. 發送HTTP POST的請求,將數據發送到App Store,其地址爲:
https://buy.itunes.apple.com/verfyReceipt
4. App Store的返回值也是一個JSON格式的對象,包含兩個鍵值對, status和receipt:
- {
- "status" : 0,
- "receipt" : { … }
- }
若是status的值爲0, 就說明該receipt爲有效的。 不然就是無效的。
App Store的收據
發送給App Store的收據數據是經過對transaction中對應的信息編碼而建立的。 當App Store驗證收據時, 將從其中解碼出數據,並以"receipt"的鍵返回。 返回的響應信息是JSON格式,被包含在SKPaymentTransaction的對象中(transactionReceipt屬性)。Server可經過這些值來了解交易的詳細信息。 Apple建議只發送receipt數據到服務器並使用receipt數據驗證和得到交易詳情。 由於App Store可驗證收據信息,返回信息,保證信息不被篡改,這種方式比同時提交receipt和transaction的數據要安全。(這段得再看看)
表5-1爲交易信息的全部鍵,不少的鍵都對應SKPaymentTransaction的屬性。
備註:一些鍵取決於你的程序是連接到App Store仍是測試用的Sandbox環境。更多關於sandbox的信息,請查看"Testing a Store"一章。
Table 5-1 購買信息的鍵:
鍵名 | 描述 |
quantity | 購買商品的數量。對應SKPayment對象中的quantity屬性 |
product_id | 商品的標識,對應SKPayment對象的productIdentifier屬性。 |
transaction_id | 交易的標識,對應SKPaymentTransaction的transactionIdentifier屬性 |
purchase_date | 交易的日期,對應SKPaymentTransaction的transactionDate屬性 |
original_-transaction_id | 對於恢復的transaction對象,該鍵對應了原始的transaction標識 |
original_purchase_-date | 對於恢復的transaction對象,該鍵對應了原始的交易日期 |
app_item_id | App Store用來標識程序的字符串。一個服務器可能須要支持多個server的支付功能,能夠用這個標識來區分程序。連接sandbox用來測試的程序的不到這個值,所以該鍵不存在。 |
version_external_-identifier | 用來標識程序修訂數。該鍵在sandbox環境下不存在 |
bid | iPhone程序的bundle標識 |
bvrs | iPhone程序的版本號 |
測試Store功能
開發過程當中,咱們須要測試支付功能以保證其工做正常。然而,咱們不但願在測試時對用戶收費。 Apple提供了sandbox的環境供咱們測試。
備註:Store Kit在模擬器上沒法運行。 當在模擬器上運行Store Kit的時候,訪問payment queue的動做會打出一條警告的log。測試store功能必須在真機上進行。
Sandbox環境
使用Sandbox環境的話,Store Kit並無連接到真實的App Store,而是連接到專門的Sandbox環境。 SandBox的內容和App Store一致,只是它不執行真實的支付動做。 它會返回交易成功的信息。 Sandbox使用專門的iTunes Connect測試 帳戶。不能使用正式的iTunes Connect帳戶來測試。
要測試程序,須要建立一個專門的測試帳戶。你至少須要爲程序的每一個區域建立至少一個測試帳戶。詳細信息,請查看iTunes Connect Developer Guide文檔。
在Sandbox環境中測試
步驟:
1. 在測試的iPhone上退出iTunes帳戶
Settings中可能會記錄以前登陸的帳戶,進入並退出。
重要信息:不能在Settings 程序中經過測試帳戶登陸。
2. 運行程序
當你在程序的store中購買商品後,Store kit提示你去驗證交易。用測試帳戶登陸,並批准支付。 這樣虛擬的交易就完成了。
在Sandbox中驗證收據
驗證的URL不一樣了:
- NSURL *sandboxStoreURL = [[NSURL alloc]initWithString:
- @"https://sandbox.itunes.apple.com/verifyReceipt"];
本文來自:http://xiongzhend.blog.163.com/blog/static/640985012010825105825754/