web-dev-qa-db-ja.com

Google Playのアプリ内課金バージョン3購入のサーバー側の検証

ダウンロード可能なコンテンツをユーザーが利用できるようにする前に、サーバーでアプリ内課金の購入を確認する方法について、簡単な答えを見つけることができません。

アプリ課金バージョン3で使用します。TrivialDriveサンプルコードのIabHelperクラスに基づくコードを使用して管理対象製品を購入します。すべてが正常であり、購入は正常に完了し、完全なPurchaseオブジェクトと次の元のJSONデータを取得します。

{
    "orderId":"12999763169054705758.1364365967744519",
    "packageName":"my package name",
    "productId":"77",
    "purchaseTime":1366217534000,
    "purchaseState":0,
    "purchaseToken":"utfwimslnrrwvglktizikdcd.AO-J1OwZ4l5oXz_3d2SAWAAUgFE3QErKoyIX8WuSEnBW26ntsyDmlLgoUd5lshqIY2p2LnlV4tpH4NITB4mJMX98sCtZizH7wGf6Izw3tfW_GflJDKFyb-g"
}

私はそれを理解しているので、purchaseTokenとサーバーに署名と呼ばれるものを渡す必要があります。サーバーは、秘密鍵を使用して購入を確認します。これは正しいです?もしそうなら、どこから署名を取得しますか?実際には、購入のサーバー側の検証に関するまともな文書はありませんか?

34
britzl
_where do I get the signature from ?
_

公式ドキュメント をご覧ください。

onActivityResult()メソッド内で、例に示すように次のデータを取得できると書かれています。

_    @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
   if (requestCode == 1001) {           
      int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
      String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
      String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");//this is the signature which you want

      if (resultCode == RESULT_OK) {
         try {
            JSONObject jo = new JSONObject(purchaseData);//this is the JSONObject which you have included in Your Question right now
            String sku = jo.getString("productId");
            String purchaseToken = jo.getString("purchaseToken");
           //you need to send sku and purchaseToken to server for verification
          }
          catch (JSONException e) {
             alert("Failed to parse purchase data.");
             e.printStackTrace();
          }
      }
   }
}
_

サーバー側の検証については、official docs をご覧ください

前述のように、クライアントアプリはskuおよびpurchaseTokenをサーバーAPIに送信します。サーバーはこれらの値を受け取る必要があり、Android apiを発行して購入を確認するためのチェックを実行する必要があります。

サーバーは、必要なパラメーターを追加することで、次のGET要求を呼び出すことができます。

https://www.googleapis.com/androidpublisher/v2/applications/packageName/ purchases/products /productId/ tokens /token

ここに、
packageName =クライアントアプリのpackageName
productId =クライアントアプリから受信したsku
token =クライアントアプリから購入したpurchaseToken

前述の形式のように、JSONObject応答になります。

_{
  "kind": "androidpublisher#productPurchase",
  "purchaseTimeMillis": long,
  "purchaseState": integer,
  "consumptionState": integer,
  "developerPayload": string,
  "orderId": string,
  "purchaseType": integer
}
_

ここで、purchaseState = 0は有効な購入を意味します

お役に立てばと思います!!

16
Mehul Joisar

アプリ内購入の詐欺を減らすための私の小さな貢献

あなたのAndroidコード上の外部サーバーでの署名検証:

verifySignatureOnServer()

  private boolean verifySignatureOnServer(String data, String signature) {
        String retFromServer = "";
        URL url;
        HttpsURLConnection urlConnection = null;
        try {
            String urlStr = "https://www.example.com/verify.php?data=" + URLEncoder.encode(data, "UTF-8") + "&signature=" + URLEncoder.encode(signature, "UTF-8");

            url = new URL(urlStr);
            urlConnection = (HttpsURLConnection) url.openConnection();
            InputStream in = urlConnection.getInputStream();
            InputStreamReader inRead = new InputStreamReader(in);
            retFromServer = convertStreamToString(inRead);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }

        return retFromServer.equals("good");
    }

convertStreamToString()

 private static String convertStreamToString(Java.io.InputStreamReader is) {
        Java.util.Scanner s = new Java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }

verify.php Webホスティングのルートディレクトリ

<?php
// get data param
$data = $_GET['data'];

// get signature param
$signature = $_GET['signature'];

// get key
$key_64 = ".... put here the base64 encoded pub key from google play console , all in one row !! ....";



$key =  "-----BEGIN PUBLIC KEY-----\n".
        chunk_split($key_64, 64,"\n").
       '-----END PUBLIC KEY-----';   
//using PHP to create an RSA key
$key = openssl_get_publickey($key);


// state whether signature is okay or not
$ok = openssl_verify($data, base64_decode($signature), $key, OPENSSL_ALGO_SHA1);
if ($ok == 1) {
    echo "good";
} elseif ($ok == 0) {
    echo "bad";
} else {
    die ("fault, error checking signature");
}

// free the key from memory
openssl_free_key($key);

?>

注:

  • JavaコードでURLを暗号化する必要があります。そうでない場合は、解凍されたアプリapkで簡単なテキスト検索を使用してURLを簡単に見つけることができます

  • また、PHPファイル名、URL引数、良い/悪い応答を意味のないものに変更する方が適切です。

  • メインスレッドのネットワーク例外がスローされない場合、verifySignatureOnServer()は別のスレッドで実行する必要があります。

それが役立つことを願っています...

4
Lluis Felisart

これはあまりにも古い質問ですが、私の答えが誰かを助けることを願っています。

クライアント側で署名を検証する必要があります。その後、purchaseTokenサーバーに渡す必要がありますside、その後サーバーはGoogleのサーバーに連絡し、purchaseStateおよびconsumptionState

https://developers.google.com/Android-publisher/api-ref/purchases/products

2
Oleksii K.

これは非常に古い質問ですが、まだ関連があると思います。

私の小さな貢献。購入オブジェクトは、1つの新しいパラメーター「acknowledged」用に拡張され、次のようになりました。

{
  "orderId":"12999763169054705758.1364365967744519",
  "packageName":"my package name",
  "productId":"77",
  "purchaseTime":1366217534000,
  "purchaseState":0,
  "purchaseToken":"utfwimslnrrwvglktizikdcd.AO-J1OwZ4l5oXz_3d2SAWAAUgFE3QErKoyIX8WuSEnBW26ntsyDmlLgoUd5lshqIY2p2LnlV4tpH4NITB4mJMX98sCtZizH7wGf6Izw3tfW_GflJDKFyb-g",
  "acknowledged":true
}

そのため、署名をチェックして、この1つの追加パラメーターを追加する場合は注意してください。

0
Nikola Budjevac