Android定額課金のレシート検証をサーバーで行う (1/2)

レシート検証

定額課金に限らず、モバイルアプリに課金機能を付ける場合、サーバー側でレシート(購入履歴)の検証をしなければならない。
フロントでも完結できるそうだが、レシート情報の改ざん防止のため、サーバー側で処理することが推奨されている。
今回PHPでレシート検証を実装してみた。

何をチェックするか

主に以下をチェックする。

  • 電子署名のチェック
  • レシート検証用APIで最新レシートデータが返る
  • 購入したアイテムが正しいか
  • 定額課金の場合、購読状態が有効期限内か

処理の流れ

まずフロント側でサブスクアイテムを購入すると、ストアから購入情報が丸っと返ってくる。そこに含まれる署名データと、レシートデータをbase64エンコードしてJSON形式でサーバーに送る。(フロントコードは割愛)

サーバーはそれを受け取り、レシート検証開始。検証OKだったら購入確定処理を入れ、有料サービスを提供する。

※署名データはすでにbase64エンコードされてるので注意。
※実際にはレシート検証前に購入は確定してしまうが、サーバー側で確定処理を入れないと払い戻しになってしまう。

電子署名のチェック

サーバー側ではまず前述の電子署名のチェックを行う。
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を使うにはGoogleOauth2.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 {
    //エラー処理
}


これでようやく、最新レシート情報を検証する準備が整う。
続きは次回。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール