驗證收據

由 2 位貢獻者:

如果你的是付費 App,就應該檢查消費者的收據是否有效。如果開發者完全不檢查,則可能會有消費者購買 App 之後立刻退款,就變成免費取得 App 了;或可能直接從你的網站參照 manifest 檔案,就能安裝 App。如果開發者要建構收據驗證機制,可參閱本文提供的程式碼與作業流程。

收據 (Receipt) 即為消費者的 App 購買證明。收據將經由付款處理機制 (由消費者完成的交易) 產生確認資料 (Affirmation),以取得特定的數位產品。在消費者付費購買 Firefox Marketplace 上的 App 之後,收據就隨即傳送到消費者的裝置之中。位於消費者裝置上的收據,將用在 mozApps.install() 函式的第二組參數之上。Firefox Marketplace 將使用該筆收據資料,進而呼叫 install() 以完成 App 的安裝。

驗證收據的時機

開發者必須決定 App 該何時驗證收據。一般都是在消費者啟動 App 時開始驗證。如果是屬於長時間執行 App,則可以定時/定期進行驗證。例如影片串流 App 就可以每隔 20 分鐘檢查收據 1 次。

另請注意,如果消費者為離線狀態,App 也就無法驗證收據。開發者可決定 App 在離線時所進行的作業。為了避免打擾某些消費者,開發者可停止強制的收據驗證作業 (消費者可能進入隧道而暫時離線),只要等消費者上線時再次檢查即可。

如何驗證收據

最簡單就是使用 Firefox Marketplace 的驗證服務即可。如果你的 App 屬於「HTML-only」,也就是伺服器只會提供靜態檔案,則可使用 Mozilla 的「receiptverifier」JavaScript 函式庫。此函式庫基本上只會將 receiptverifier.js 指令碼加入 App 之中,並使用 verifyReceipts() 函式。可參閱上述連結以進一步了解。

收據內容

Open Web App 所使用的收據,均為可攜式、可驗證的「Token」購買證明,即所謂 JSON Web Token (JWT) 的數位簽署 JSON 資料架構,且此格式可讓所有用戶端與伺服器讀取。許多程式語言均提供 JWT 函式庫。

應接受的收據

收據就是付款的證明,但 App 開發者可決定自己想要的收據類型。以下是你該檢查的要項:

  • 收據來自於售出 App 的商店。請檢查自己清單中的 iss 欄位。
  • 自己 App 的專屬收據。如果是架設/托管式 (Hosted) App,可檢查 product URL 欄位是否正確。如果是封裝式 (Packaged) App,可檢查 product storeData 是否正確,直到 bug 867265 解決為止。除非已發出收據,否則 storeData 均為未知值,且發出收據之後將不再變動。
  • 驗證作業的網址是位於商店的網域或子網域之下。可檢查 verify 的網址是否為 iss 欄位的子網域。
  • 收據真正用於購買活動,而非測試用收據。請檢查 typ 欄位。

receipt verifier 函式庫可進行其中數項檢查。

測試用收據

在開發期間,Firefox Marketplace 即可協助發出測試用收據,以供完整測試 App 的相關付款程序。可透過 Firefox 開發者交流中心 (Firefox Developer Hub) 的公用程式頁面產生測試用收據。此種收據將具備「test-receipt」的 typ。且 App 只會在開發期間接收這類收據,一旦上架之後就停止接收;否則消費者可略過 App 銷售程序,直接使用測試用收據。

receipt verifier 函式庫預設不會接收 test-receipts

收據驗證

在沒有 JWT 函式庫的情況下,開發者也能在收據的 verify 欄位中,找到驗證服務的對應網址,並將收據傳送給驗證服務。各個 App 均具備不同的 Firefox Marketplace 驗證服務網址。

下列範例程式碼可於收據中顯示 verify 網址。該程式碼將用於 Firefox 的「網頁主控台 (Web console)」開發者工具。

var request = navigator.mozApps.getSelf();
request.onsuccess = function() {
  // Get the app's receipt and decode it
  console.log(atob(request.result.receipts[0].split('~')[1].split('.')[1])); 
};

為了取得 verify 網址,此範例將近行下列作業:

  • 針對由 mozApps.getSelf() (對目前 App 的參照) 所回傳的物件,將之儲存於 request 變數中。
  • 如果請求成功,則會檢索 App 的第一筆收據,且將切割由 JWT 送進的值,以取得包含收據的實際部位。
  • 透過 JavaScript 的 Window.atob 函式,而解碼 Base64-encoded 的收據。

接著是上述程式碼結果的完整範例。此即為扣除 JWT 部分的完整收據。

{
  "product": {
    "url": "http://example.com",
    "storedata": "id=111111"
  },
  "iss": "https://marketplace.mozilla.org",
  "verify": "https://receiptcheck.marketplace.mozilla.org/verify/111111", // The verify URL
  "detail": "https://marketplace.mozilla.org/en-US/purchases/111111",
  "reissue": "https://marketplace.mozilla.org/en-US/app/example/purchase/reissue",
  "user": {
    "type": "directed-identifier",
    "value": "1234-abcd99ef-a123-456b-bbbb-cccc11112222"
  },
  "exp": 1353028900,
  "iat": 1337304100,
  "typ": "purchase-receipt",
  "nbf": 1337304100
}

在開發者取得 verify 網址之後,就可透過 POST 函式,在訊息主體之內傳送完整的 JWT。驗證服務的回應即如上方所述。下列程式碼片段 (snippet),將從上列範例中取得 App 第一筆收據的完整 JWT。

request.result.receipts[0]

請注意,上述程式碼並不會處理 JWT 的加密部分。如果你要透過 Marketplace 驗證收據,則收據將檢查本身的加密簽章。

注意:可在 Kumar McMillan 提供的 Private Yacht 範例中,找到其他更高擴充性的收據驗證範例。

App 盜版問題

即使你驗證了自己付費 App 的收據,還是可能有人繞過收據機制而盜用 App。上列的收據驗證函式並無法避免此問題。

如果想要更好的防盜版機制,就必須設定代理伺服器,將之作為 App 與 Firefox Marketplace 之間的媒介。代理伺服器可檢查收據、IP 位址,還有更多物件。如果單一收據來自於不同的 IP 位址,且能一樣執行正確動作,則伺服器就可進行類似通知的作業。如果是大型、複雜、使用伺服器處理功能的 App,就能達到更適當、更正確的設定。

此 Python 程式碼仍是開發中的專案,可提供某些代理伺服器的靈感。Django Receipts 則是測試用的代理伺服器,可驗證收據。開發者最好不要直接將之作為正式運行的代理伺服器,但可進行測試以了解相關機制。另可參閱更多資訊,讓自己了解收據驗證作業。

收據欄位

收據應包含下列欄位:

typ
可供識別收據類型的字串,必為以下之一:
  • purchase-receipt ─ 交易完成之後隨即發出的收據。這類收據應隨時能由 App 接收。
  • developer-receipt ─ 由 App 開發者所發出的收據。一般是開發者交由自己使用的 App 商店所發出。這類收據有效期較短。
  • reveiwer-receipt ─ 由 App 審查者所發出的收據。一般是 App 審查者透過其所位於的商店發出。這類收據只需要在審查期間接收即可,有效期較短。
  • test-receipt ─ 在開發期間用以測試 App 所發出的收據。只要是開發期間以外,均不應該接收這類收據,有效期較短。
product
JSON 物件在識別產品時,包含收據所囊括的範圍,以及任何特定商店的資料。包含下列欄位:
  • url ─ 代表該網址的根網域 (root of a domain),且沒有最後的斜線 (例如「https://someapp.com」)。一般這樣就代表了 Web App。如果是以根網域開頭的其他網址,則代表「App 內購買」。只要開發者或收據開立者覺得方便,則可使用各種路徑規則 (Path scheme)。
  • storedata ─ 此字串為 App 所獨有,將提供給收據驗證器 (Verifier) 所用。
user
針對完成購買作業的消費者,此 JSON 物件包含了使用者 ID,並包含下列欄位:
  • type ─ 此字串內有「directed-identifier」值。
  • value ─ 此字串即為消費者的專屬 ID。消費者每次購買 App,都會擁有不同的 ID。
iss
發出收據的商店,其所屬之網域。
nbf
完成購買程序之後,顯示「Not-before」的時間戳記。時間戳記是從 1970-01-01T00:00:00Z (UTC, RFC 3339) 開始的秒數。
iat
發出收據之後,顯示「Issued-at」的時間戳記。此時間戳記的格式如同 nbf。可透過此數值決定收據的存在時間。
exp
(選填) 代表收據過期的時間戳記。此時間戳記的格式如同 nbf
detail
(選填) 網址包含額外人、機均可讀取的購買細節。如果另提供購買行為的交易記錄或退款功能,則本頁亦應包含相關函式。
verify
(選填) 由經過授權的 App 使用此網址,以驗證收據。另請注意,Firefox Marketplace 一定會為 App 提供此欄位。如果開發者想自己建立 App 的商城,就可能不需使用此欄位。
reissue
(選填) 網址可重新發出新收據。

Document Tags and Contributors

Contributors to this page: MashKao, PYChen
最近更新: MashKao,