Marketplace で有料アプリを提供している場合、ユーザが有効なレシートを所持していることを確認する必要があります。確認しないと、ユーザがアプリを購入した直後に 購入を取り消して返金を受け取れば、アプリを無料で使用できてしまいます。または、単純に開発者のサイトからマニフェストファイルを直接参照して、アプリ をインストールすることも可能です。この記事では、独自のレシート検証を組み込むためのコードとワークフローについて説明します。
レシートは、アプリの購入者がアプリを購入したことの証明です。特定のユーザが特定のデジタル製品を取得するトランザクションを完了した時に、決済処理プログラムからその確認がレシートによってエンコードされて提供されます。レシートは、ユーザが Firefox Marketplace のアプリに対して支払いを行った後、ユーザのマシンに送信されます。レシートは、mozApps.install()
メソッドの 2 番目の引数に従ってユーザのマシンに保存されます。Firefox Marketplace は、このレシートを使用して install()
を呼び出します。
レシートを検証するタイミング
開発者は、アプリがレシートを検証するタイミングを決定する必要があります。通常は、ユーザがアプリを起動した時にレシートが検証されます。長時間稼働し続けるアプリの場合は、一定の間隔で検証を行うこともあります。例えば、ビデオをストリーミング配信するアプリについて、20 分ごとにレシートを確認することが考えられます。
1 つ注意が必要なのは、ユーザがオフラインの場合、アプリがレシートを検証できない点です。このような状況でのアプリの動作については、開発者の判断にゆだねられています。ユーザの利便性を高めるために、レシート検証を緩和し (例えばユーザがトンネルを通過中の場合など)、ユーザが次にオンラインになるまで検証を継続的に試みることもできます。
レシートの検証方法
レシートを検証するには、Firefox Marketplace を使用するのが最も簡単です。アプリが HTML のみの場合、サーバは静的ファイルを提供する以外の処理は行わないので、Mozilla の receiptverifier JavaScript ライブラリを使用できます。このライブラリでは、基本的な処理として、receiptverifier.js
スクリプトをアプリに追加し、verifyReceipts()
関数を使用します。詳細については、上のリンクを参照してください。
レシートの内容
Open Web app で使用されるレシートは、移植可能で、検証可能な購入トークンの証明です。これは、JSON Web Token (JWT) と呼ばれる電子署名付きの JSON データ構造です。この形式はどのクライアントとサーバでも幅広く読み取ることができます。用意されている JWT ライブラリは多くのプログラミング言語で使用可能です。
受け入れるべきレシートの条件
レシートは支払の証明ですが、どのような種類のレシートを受け入れるかはアプリ開発者の判断にゆだねられています。以下に、確認すべき条件を示します。
- レシートがアプリを販売しているストアによって発行されたものであること。
iss
フィールドがホワイトリストに含まれていること。 - レシートが自分の提供しているアプリに対するものであること。ホスト型アプリの場合、
product
URL
フィールドが正しいことを確認します。パッケージ型アプリの場合、バグ 867265 が修正されるまでは、product storeData
が正しいことを確認する必要があります。storeData
の値はレシートが発行されるまで不明ですが、発行後は一定に保たれます。 - 検証 URL は、ストアのドメインまたはサブドメインにあります。
verify
URL がiss
フィールドのサブドメインであることを確認します。 - レシートが購入のレシートであって、テストレシートでないこと。
typ
フィールドを確認してください。
これらの確認の一部は、receipt verifier ライブラリによって自動的に実行されます。
テスト用レシート
Firefox Marketplace では、開発者がアプリの開発時に課金ライフサイクルをすべてテストできるように、テスト用レシートを発行します。テスト用レシートを生成するには、Firefox Developer Hub のこのユーティリティページを使用してください。テスト用レシートは、typ
が test-receipt
である以外は、通常のレシートと同じ形式です。アプリは開発中にはテスト用レシートを受け入れ、販売を開始した後は受け入れないようにする必要があります。販売を開始した後もテスト用レシートを受け入れると、誰もが販売プロセスをバイパスしてテスト用レシートをアプリに使用できてしまいます。
標準設定では、receipt verifier ライブラリは、test-receipts
を受け入れません。
レシートの検証
JWT ライブラリを使用しなくても、レシートの verify
フィールドで検証サービスの URL を確認して、レシートを検証サービスに送信できます。Firefox Marketplace 検証サービスへの URL は、アプリによって異なります。
次のコードサンプルは、レシート内の verify
URL の例を示します。このコードは、Firefox Web コンソール開発者ツールで使用するように設定されています。
var request = navigator.mozApps.getSelf();
request.onsuccess = function() {
// アプリのレシートを取得してデコード
console.log(atob(request.result.receipts[0].split('~')[1].split('.')[1]));
};
このサンプルコードでは、verify
URL を取得するための次の処理を行います。
mozApps.getSelf()
(現在のアプリへの参照) からの戻り値をrequest
変数に保存します。- 要求が正常に処理されると、アプリの最初のレシートが取得され、JWT から割り当てられた値が分割されて、レシートの入った実際の部分が取り出されます。
- JavaScript の
関数を使用して、Base64 エンコードされたレシートをデコードします。Window.atob
以下は、上のコードの結果を見やすくレイアウトしたものです。これは、JWT 部分を除いたレシート全体です。
{
"product":{
"url":"https://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
URL を取得した後、POST
メソッドを使用して、メッセージ本文に完全な JWT を含めて送信します。検証サービスの応答は上記のとおりです。次のコードは、上のサンプルに示したアプリの最初のレシートの JWT 全体を取得します。
request.result.receipts[0]
上のコードではJWT の暗号化を処理しないことに注意してください。Marketplace でレシートを検証すると、署名が暗号化されたまま検証されます。
注:Kumar McMillan のプライベートヨットの例では、これとは別の高価なレシート検証の例が示されています。
アプリの無断使用の防止
有料アプリでレシートを検証していても、ユーザ同士がレシートを融通し合えば、無断使用が可能になります。上のレシート検証方法では、このような無断使用を防止できません。
無断使用の防止策を強化するためには、アプリと Firefox Marketplace の間を仲介するプロキシサーバを設定する方法があります。プロキシサーバは、レシートに加え、IP アドレスやその他の項目を確認します。また、同じレシートが異なる IP アドレスで使用された場合はそれを通知し、適切な措置を取ることもできます。この方法は、サーバ処理を使用する大規模で複雑なアプリに適していると言えま す。
この Python コードは、開発中のプロジェクトですが、プロキシサーバの理解に役立ちます。Django Receipts は、レシートを検証するテストプロキシです。これは本番用プロキシとして使用できますが、テストとして学習用に使用してください。レシートを自身で検証する方法の詳細については、こちらを参照してください。
レシートの項目
レシートには、次のフィールドが含まれています。
typ
- レシートのタイプを示す文字列。次のいずれかの値に設定されます。
purchase-receipt
- トランザクションが完了したときに使用されるレシート。アプリは、このタイプのレシートを常に受け入れる必要があります。developer-receipt
- アプリの開発者に対して発行されるレシート。このレシートは、通常、開発者が使用するために、ストアによって発行されます。このレシートには、短期の有効期限が設定されていることがあります。reveiwer-receipt
- アプリのレビューアに対して発行されるレシート。このレシートは、通常、ストアからレビューアに対して発行されます。このレシートは、レビュー期間中のみ 受け入れられるように設定する必要があります。このレシートには、短期の有効期限が設定されていることがあります。test-receipt
- 開発時にアプリのテスト用に発行されるレシート。このレシートは、開発時以外には受け入れないようにする必要があります。このレシートには、短期の有効期限が設定されていることがあります。
product
- レシートの対象である製品を特定する JSON オブジェクトおよびストア固有のデータ。これには次のフィールドが含まれます。
url
- ドメインのルートを表す URL。末尾のスラッシュなしで表記します (例: https://someapp.com)。これは慣習的に、「Web アプリケーション」を表します。サイトのさらに内側を示す URL は、慣習的に「アプリケーション内購入」を表すものとされており、開発者やレシートの発行者にとって便利なパス表記法を使用することができます。storedata
- レシートの検証者に対してこのアプリを一意に特定する文字列。
user
- 購入したユーザのユーザ ID が含まれている JSON オブジェクト。これには次のフィールドが含まれます。
type
-"directed-identifier"
という値を持つ文字列。value
- ユーザの一意の ID である文字列。1 人のユーザが複数のアプリを購入した場合、それぞれの購入に対して異なるユーザ ID が割り当てられます。
iss
- レシートを発行したストアのドメイン。
nbf
- 購入完了の日時を示す「Not-before (その日時以降有効)」型のタイムスタンプです。このタイムスタンプは、UTC 時刻の 1970-01-01T00:00:00Z からの経過時間を秒数で表した値です (RFC 3339)。
iat
- レシートの発行日時を示す「Issued-at (発行日時)」型のタイムスタンプです。タイムスタンプの形式は、
nbf
と同じです。この値は、レシート発行からの経過時間を判定するために使用できます。 exp
- (オプション) レシートが期限切れであることを示す期限切れタイムスタンプ。タイムスタンプの形式は
nbf
と同じです。 detail
- (オプション) 人間または機械が判読可能な、購入に関する詳細情報が含まれている URL。購入に対してトランザクションログ機能または返金機能が用意されている場合、通常、このページでそれらの機能を使用できるように設定します。
verify
- (オプション) 認証済みのアプリケーションが、レシートの検証に使用できる URL。Firefox Marketplace は、アプリに対して常にこの不フィールドを提供することに注意してください。独自のアプリマーケットプレースを作成する場合、このフィールドを使用しない 場合があります。
reissue
- (オプション) 新しいレシートの再発行に使用できる URL。