レシート検証
定額課金に限らず、モバイルアプリに課金機能を付ける場合、サーバー側でレシート(購入履歴)の検証をしなければならない。
フロントでも完結できるそうだが、レシート情報の改ざん防止のため、サーバー側で処理することが推奨されている。
今回PHPでレシート検証を実装してみた。
何をチェックするか
主に以下をチェックする。
- 電子署名のチェック
- レシート検証用APIで最新レシートデータが返る
- 購入したアイテムが正しいか
- 定額課金の場合、購読状態が有効期限内か
処理の流れ
まずフロント側でサブスクアイテムを購入すると、ストアから購入情報が丸っと返ってくる。そこに含まれる署名データと、レシートデータをbase64エンコードしてJSON形式でサーバーに送る。(フロントコードは割愛)
※署名データはすでにbase64エンコードされてるので注意。
サーバーはそれを受け取り、レシート検証開始。検証OKだったら購入確定処理を入れ、有料サービスを提供する。
※実はレシート検証前に購入は確定してしまうが、サーバー側で確定処理を入れないと払い戻しになってしまう。
電子署名のチェック
サーバー側ではまず前述の電子署名のチェックを行う。
GooglePlayConsole管理画面からアプリのRSA公開鍵を取得できるので、txt形式からpem形式に変換してサーバー内に保存しておく。
そしてopenssl_verify関数でレシートデータを用いて署名のチェックを行う。
$info = json_decode(file_get_contents('php://input'), true);
//[Note:]base64をPOSTすると'+'が勝手に' 'に変換されるらしい
$signature = base64_decode(str_replace(' ', '+', $info["signature"]));
$receipt = base64_decode(str_replace(' ', '+', $info["receipt"]));
$receipt_data = json_decode($receipt, true);
$public_key = file_get_contents('../signature/public.pem');
$public_key_id = openssl_pkey_get_public($public_key);
$check_sign = (int)openssl_verify($receipt, $signature, $public_key_id, OPENSSL_ALGO_SHA1);
if($check_sign !== 1) {
//エラー処理
}
openssl_free_key($public_key_id);
OAuth認証
次にレシート検証用APIで最新レシート情報を取得するのだが、
その前にこのAPIを使うにはGoogleOuth2.0での認証を通す必要がある。
GoogleCloudPlatformの管理画面でプロジェクトを作成し、以下を取得する。
- リフレッシュトークン(アクセストークンを更新するのに必要)
- クライアントID
- シークレットクライアントID
これらをアクセストークン取得用のAPIへ送信して、取得する。
$google_oauth_endpoint = 'https://accounts.google.com/o/oauth2/token';
$refresh_token = "*********************";
$client_id = "************";
$client_secret = "**************";
$post_body = [
"grant_type" => "refresh_token",
"client_id" => $client_id,
"client_secret" => $client_secret,
"refresh_token" => $refresh_token,
];
$ch = curl_init($google_oauth_endpoint);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($post_body),
]);
$ch_response = curl_exec($ch);
$response_code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if($response_code == 200) {
$response_array = json_decode($ch_response, true);
$access_token = $response_array["access_token"]; //アクセストークン取得成功
} else {
//エラー処理
}
これでようやく、最新レシート情報を検証する準備が整う。
続きは次回。