因業務需要實現了APP內購處理,但在過程中出現了部分不可控的因素,導致部分用戶反映有充值不成並漏單的情況。
仔細考慮了幾個付費安全上的問題,凡是涉及到付費的問題都很敏感,任何一方出現損失都是不能接受的,所以在這里整理一些支付安全的要點分享一下。
支付方式
IAP是指In-App Purchase, 是一種付費方式,而並不是蘋果專有的付費方式,在其它平台上也會有不同的實現,這里針對Apple IAP。
說到IAP安全問題,在蘋果的IAP流程中有一個比較明顯的邏輯漏洞,這個邏輯漏洞是建立在我們處理不當的情況下發生的,會導致己方提供的服務和用戶之間出現問題。先看看IAP支付時序圖:
支付流程
1.客戶端向Appstore請求購買產品(假設產品信息已經取得),Appstore驗證產品成功后,從用戶的Apple賬戶余額中扣費。
2.Appstore向客戶端返回一段receipt-data,里面記錄了本次交易的證書和簽名信息。
3.客戶端向我們可以信任的服務器提供receipt-data
4.服務器對receipt-data進行一次base64編碼
5.把編碼后的receipt-data發往itunes.appstore進行驗證
6.itunes.appstore返回驗證結果給服務器
7.服務器對商品購買狀態以及商品類型,向客戶端發放相應的道具與推送數據更新通知
8.客戶端收到服務器的處理狀態,進行相應的結單處理
這八個步驟實際上是一個很安全的流程了。那問題出在哪里呢?我們談談兩種蘋果IAP的驗證模型。
驗證方式
1.IAP built-in Model,本地驗證
有些APP甚至是網游,都直接跳過了3~7步驟,在第2步拿到receipt-data之后,直接由客戶端向itunes.appstore發送驗證請求,並且拿到結果,根據結果修改數據。
我們在設計APP的時候都遵循一個真理,“凡是在客戶端的數據都是不安全的”,深以為然。如果沒有獨立服務器輔助驗證,這樣也就避免不了數據被修改的事實了,是的,你會少賺錢。
不過如果APP也不通過獨立服務器驗證,而是在客戶端驗證之后再告知服務器狀態讓其發放游戲道具,那就太可怕了點。這是IAP built-in Model
那是不是就完全不能讓這個過程變得安全了呢?也不是,但這個安全保障只是讓修改變得困難而已。蘋果官方提供了 Validating Receipts Locally 在客戶端對receipt-data進行安全驗證,主要是對證書以及簽名的合法性驗證。如果不想自己寫代碼驗證,也可以借助第三方機構提供的receipt-data驗證API,比較著名的有 urbanairship和 beeblex 。
但如果能偽造一個完全合法的receipt-data,是不是一樣可以達到欺騙目的。是的,為了繞過Validating Locally,於是黑客開始用自己偽造的receipt-data進行移花接木,所以出現了可以偽造”合法訂單”的 in-appstore 。因此這種本地加強驗證的方法也不能完全避免IAP攻擊。
2.IAP Server Model,服務器驗證
而如果我們把驗證邏輯移到服務器上,這個過程就變得容易多了。因為不再需要擔心receipt-data被偽造的問題。不過就算把步驟4~7在服務器上做了,同樣也會產生一些幼稚的邏輯漏洞:
對驗證receipt-data的reponse content不進行驗證和記錄,只根據Product直接發放商品。這樣只要客戶端不斷提交receipt-data,按照正常邏輯你就需要不斷驗證並且重復發放商品。較為安全的做法是:
在每一次收到receipt-data之后,都把提交的用戶賬號以及receipt-data中的單號建立映射並記錄下來,在每次驗證receipt-data時,先判斷其是否已經存在。
只要做了這樣的驗證,整個支付流程都變得明朗起來。
確保receipt-data的成功提交與異常處理
建立在IAP Server Model的基礎上,並且我們知道手機網絡是不穩定的,在付款成功后不能確保把receipt-data一定提交到服務器。如果出現了這樣的情況,那就意味着用戶被appstore扣費了,卻沒收到服務器發放的道具。(這樣就引發了漏單)
解決這個問題的方法是在客戶端提交receipt-data給我們的服務器,讓我們的服務器向蘋果服務器發送驗證請求,驗證這個receipt-data賬單的有效性. 在沒有收到回復之前,客戶端必須要把receipt-data保存好,並且定期或在合理的UI界面觸發向服務端發起請求,直至收到服務端的回復后刪除客戶端的receipt賬單記錄。
如果是客戶端沒成功提交receipt-data,那怎么辦?就是用戶被扣費了,也收到appstore的消費收據了,卻依然沒收到道具,於是投訴到客服處。
這種情況在以往的經驗中也會出現,常見的用戶和運營商發生的糾紛。客服向用戶索要賬號和appstore的收據單號,通過查詢itunes-connect看是否確有這筆訂單。
如果訂單存在,則要聯系研發方去查詢服務器,看訂單號與用戶名是否對應,並且是否已經被使用了,做這一點檢查的目的是 為了防止惡意用戶利用已經使用過了的訂單號進行欺騙(已驗證的賬單是可以再次請求驗證的,曾經為了測試,將賬單手動發給服務器處理並成功),謊稱自己沒收到商品。
當然了,如果查不到這個訂單號,就意味着這個訂單確實還沒使用過,手動給用戶補發商品即可。
有朋友問怎么通過itunes-connect查看具體訂單,itunes-connect中無法直接看到訂單信息,可以用以下方法來查詢
1.可以通過賬單向蘋果發送賬單驗證,有效可以手動補發
2.用自己的服務器的記錄賬單列表對比
3.利用第三方的TalkingData等交易函數,會自動記錄賬單數據
建議
為保證審核的通過,需要在客戶端或server進行雙重驗證,即,先以線上交易驗證地址進行驗證,如果蘋果正式驗證服務器的返回驗證碼code為21007,則再一次連接沙盒測試服務器進行驗證即可。
在應用提審時,蘋果IAP提審驗證時是在沙盒環境的進行的,即:蘋果在審核App時,只會在sandbox環境購買,其產生的購買憑證,也只能連接蘋果的測試驗證服務器,如果沒有做雙驗證,需要特別注意此問題,否則會被拒。
其他
在sandbox中驗證receipt:https://sandbox.itunes.apple.com/verifyReceipt
在生產環境中驗證receipt:https://buy.itunes.apple.com/verifyReceipt
那么如何自動的識別收據是否是sandbox receipt呢?
識別沙盒環境下收據的方法有兩種:
- 根據收據字段 environment = sandbox。
- 根據收據驗證接口返回的狀態碼。
如果status=21007,則表示當前的收據為沙盒環境下收據, 進行驗證。
蘋果反饋的狀態碼
- 21000 App Store無法讀取你提供的JSON數據
- 21002 收據數據不符合格式
- 21003 收據無法被驗證
- 21004 你提供的共享密鑰和賬戶的共享密鑰不一致
- 21005 收據服務器當前不可用
- 21006 收據是有效的,但訂閱服務已經過期。當收到這個信息時,解碼后的收據信息也包含在返回內容中
- 21007 收據信息是測試用(sandbox),但卻被發送到產品環境中驗證
- 21008 收據信息是產品環境中使用,但卻被發送到測試環境中驗證
作者:舊舊的 <393210556@qq.com> 解決問題的方式,就是解決它一次