web-dev-qa-db-fra.com

Vérification côté serveur de l'achat de la version 3 de la facturation via l'application Google Play

Je ne parviens pas à trouver une réponse claire quant à la façon de vérifier un achat de facturation via l'application sur le serveur avant de mettre du contenu téléchargeable à la disposition de l'utilisateur.

J'utilise dans la version de facturation d'application 3. J'achète des produits gérés en utilisant du code basé sur la classe IabHelper à partir de l'exemple de code TrivialDrive. Tout va bien et dandy et l'achat est terminé avec succès, je reçois un objet d'achat complet et les données JSON originales suivantes:

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

Si je comprends bien, je dois transmettre le jeton d'achat et quelque chose que je vois appelé une signature au serveur. Le serveur utilise ensuite une clé privée pour vérifier l'achat. Est-ce correct? Si oui, d'où puis-je obtenir la signature et n'y a-t-il vraiment pas de documentation décente concernant la vérification côté serveur d'un achat?

34
britzl
where do I get the signature from ?

Jetez un oeil à documents officiels ,

Il indique qu'à l'intérieur de votre méthode onActivityResult(), vous pouvez obtenir les données suivantes comme indiqué dans l'exemple,

    @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();
          }
      }
   }
}

Pour la vérification côté serveur, Jetez un œil à documents officiels

Comme mentionné précédemment, l'application cliente enverra sku et purchaseToken à l'API serveur. Le serveur devra recevoir ces valeurs et devra effectuer une vérification avec Android publier l'api pour vérifier l'achat:

Le serveur peut appeler la requête GET suivante en ajoutant les paramètres nécessaires:

https://www.googleapis.com/androidpublisher/v2/applications/ packageName /achats/produits/ productId /tokens/ jeton

ici,
packageName = packageName de l'application cliente
productId = sku reçu de l'application cliente
token = PurchaseToken reçu de l'application cliente

Il en résultera une réponse JSONObject au format mentionné:

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

ici, PurchaseState = 0 signifie un achat valide

J'espère que ce sera utile !!

16
Mehul Joisar

Ma petite contribution pour réduire la fraude dans les achats intégrés

Vérification de signature sur un serveur externe, sur votre 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 sur le répertoire racine de l'hébergement 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);

?>

REMARQUES:

  • Vous devez crypter l'URL dans votre Java, sinon l'URL peut être trouvée facilement avec une simple recherche de texte dans votre application décompressée apk

  • Il est également préférable de changer le nom du fichier php, les arguments d'URL, les bonnes/mauvaises réponses à quelque chose sans sens.

  • verifySignatureOnServer () doit être exécuté dans un thread séparé si aucune exception de réseau sur le thread principal n'est levée.

J'espère que cela vous aidera ...

4
Lluis Felisart

C'est une question trop ancienne, mais j'espère que ma réponse pourra aider quelqu'un.

Vous devez valider la signature côté client , puis vous devez passer purchaseToken au serveur , puis le serveur contactera le serveur de Google et obtiendra toutes les informations nécessaires sur l'achat, telles que purchaseState et consumptionState.

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

2
Oleksii K.

C'est une très vieille question mais je pense qu'elle est toujours d'actualité.

Ma petite contribution. L'objet d'achat est étendu pour un nouveau paramètre "reconnu" et maintenant il ressemble:

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

Soyez donc prudent lorsque vous vérifiez la signature pour ajouter ce paramètre supplémentaire.

0
Nikola Budjevac