概要
Androidのアイテム課金を行うに当たり、サーバ側でアプリから送られたレシートの正当性を検証する必要があったため、サーバ側でやったことをまとめます。
環境
- In-app Billing Version 3 API
ざっくり手順説明
- PEM形式の公開鍵を準備
- アプリ側は署名と、Developer Payloadを含んだレシートをサーバに送信
- サーバ側で検証する
RSA公開鍵をPEM形式のものに変更
Google Play Developer Consoleの [サービスとAPI] でRSA公開鍵がありますので、
コピーしてpublicというファイル名でローカルに保存します。
以下コマンドで先ほど保存したpublicファイルをPEM形式に変換し、public.pemを作成します。
(自分はMacのターミナルでやったので base64 -D となってます)
$ base64 -D public > public.der
$ openssl rsa -inform DER -outform PEM -pubin -in public.der -out public.pem
アプリ側
レシートにはDeveloper Payloadというものがあり、開発者側で任意の文字列をDeveloper Payloadに指定することができます。アプリ側は、レシートにユーザ識別子を入れることで、サーバ側はより強固にレシートの検証を行うことが出来ます。
String developerPayload = "ユーザ識別子";
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
sku, "inapp", developerPayload);
サーバ側
サーバ側は、送れられたレシートと署名を使って検証を行います。
レシートに含まれるDeveloper Payloadには想定されたユーザ識別子があることを確認しています。
<?php
// 1. レシートと署名どちらもBase64エンコードされていることを想定
$receipt = base64_decode($_POST['receipt']);
$signature = base64_decode($_POST['signature']);
// 2. レシートの検証
$public_key = file_get_contents('/path/to/file/public.pem');
$public_key_id = openssl_get_publickey($public_key);
$result = (int)openssl_verify($receipt, $signature, $public_key_id);
if ($result === 0) {
echo '署名が正しくありません';
// error_handle();
} else if ($result === -1) {
echo '署名の検証でエラーが発生しました';
// error_handle();
}
openssl_free_key($public_key_id);
// 3. Developer Payloadの確認
$user_identifier = 'ユーザ識別子';
$obj = json_decode($receipt);
if ($obj->developerPayload !== $user_identifier) {
echo 'Developer Payloadが正しくありません';
// error_handle();
}
// 4. 以下、サービスのアイテム課金処理
問題がなければDB登録等、サービスがやりたいことを行って終わりです!