web-dev-qa-db-fra.com

Impossible d'obtenir les informations d'abonnement de Google Play Android API développeur

J'essaie d'utiliser la bibliothèque cliente des API Google pour Java pour obtenir des informations sur les abonnements des utilisateurs achetés dans mon Android. Voici comment je fais pour l'instant :

HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();

GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
                    .setJsonFactory(JSON_FACTORY)
                    .setServiceAccountId(GOOGLE_CLIENT_MAIL)
                    .setServiceAccountScopes("https://www.googleapis.com/auth/androidpublisher")
                    .setServiceAccountPrivateKeyFromP12File(new File(GOOGLE_KEY_FILE_PATH))
                    .build();

Androidpublisher publisher = new Androidpublisher.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).
                    setApplicationName(GOOGLE_PRODUCT_NAME).
                    build();

Androidpublisher.Purchases purchases = publisher.purchases();
Get get = purchases.get("XXXXX", subscriptionId, token);
SubscriptionPurchase subscripcion = get.execute(); //Exception returned here

GOOGLE_CLIENT_MAIL est l'adresse e-mail d'API Access depuis la console Google. GOOGLE_KEY_FILE_PATH est le fichier p12 téléchargé depuis l'API Access.
GOOGLE_PRODUCT_NAME est le nom du produit à partir des informations de marque.
Dans la console Google APIS, le service "Google Play Android Developer API" est activé.

Ce que je reçois, c'est:

{
  "code" : 401,
  "errors" : [ {
    "domain" : "androidpublisher",
    "message" : "This developer account does not own the application.",
    "reason" : "developerDoesNotOwnApplication"
  } ],
  "message" : "This developer account does not own the application."
}

J'apprécie vraiment votre aide pour ce problème ...

25
Jonathan Naguin

Je l'ai fait fonctionner! Les étapes que j'ai suivies:

Prérequis

Avant de commencer, nous devons générer un jeton d'actualisation. Pour ce faire, nous devons d'abord créer un projet de console d'API:

  1. Accédez à APIs Console et connectez-vous avec votre Android (le même compte utilisé dans Android Developer Console pour télécharger l'APK)) .
  2. Sélectionnez Créer un projet.
  3. Accédez à Services dans le panneau de navigation de gauche.
  4. Activez Google Play Android API développeur.
  5. Acceptez les conditions d'utilisation.
  6. Accédez à API Access dans le panneau de navigation de gauche.
  7. Sélectionnez Créer un OAuth ID client 2.0:
    • Sur la première page, vous devrez remplir le nom du produit, mais aucun logo n'est requis.
    • Sur la deuxième page, sélectionnez application Web et définissez l'URI de redirection et Origines Javascript. Nous l'utiliserons plus tard l'URI de redirection.
  8. Sélectionnez Créer un ID client. Gardez à l'esprit l'ID client et le secret client , nous utiliserons les plus tard.

Donc, maintenant nous pouvons générer le jeton d'actualisation:

  1. Accédez à l'URI suivant (notez que l'URI de redirection doit correspondre exactement à la valeur entrée dans l'ID client, y compris les barres obliques inverses de fin):

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID

  1. Sélectionnez Autoriser l'accès lorsque vous y êtes invité.
  2. Le navigateur sera redirigé vers votre URI de redirection avec un paramètre de code , qui ressemblera à 4/eWdxD7b-YSQ5CNNb-c2iI83KQx19.wp6198ti5Zc7dJ3UXOl0T3aRLxQmbmbI. Copiez cette valeur.

Créez une classe principale avec:

public static String getRefreshToken(String code)
{

    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token");
    try 
    {
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(5);
        nameValuePairs.add(new BasicNameValuePair("grant_type",    "authorization_code"));
        nameValuePairs.add(new BasicNameValuePair("client_id",     GOOGLE_CLIENT_ID));
        nameValuePairs.add(new BasicNameValuePair("client_secret", GOOGLE_CLIENT_SECRET));
        nameValuePairs.add(new BasicNameValuePair("code", code));
        nameValuePairs.add(new BasicNameValuePair("redirect_uri", GOOGLE_REDIRECT_URI));
        post.setEntity(new UrlEncodedFormEntity(nameValuePairs));

        org.Apache.http.HttpResponse response = client.execute(post);
        BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        StringBuffer buffer = new StringBuffer();
        for (String line = reader.readLine(); line != null; line = reader.readLine())
        {
            buffer.append(line);
        }

        JSONObject json = new JSONObject(buffer.toString());
        String refreshToken = json.getString("refresh_token");                      
        return refreshToken;
    }
    catch (Exception e) { e.printStackTrace(); }

    return null;
}

GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET et GOOGLE_REDIRECT_URI sont les valeurs précédentes.

Enfin, nous avons notre jeton d'actualisation! Cette valeur n'expire pas, nous pouvons donc stocker dans certains sites, comme un fichier de propriétés.

Accès à Google Play Android API développeur

  1. Obtention du jeton d'accès. Nous aurons besoin de notre jeton de rafraîchissement précédent:

    private static String getAccessToken(String refreshToken){
    
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token");
    try 
    {
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
        nameValuePairs.add(new BasicNameValuePair("grant_type",    "refresh_token"));
        nameValuePairs.add(new BasicNameValuePair("client_id",     GOOGLE_CLIENT_ID));
        nameValuePairs.add(new BasicNameValuePair("client_secret", GOOGLE_CLIENT_SECRET));
        nameValuePairs.add(new BasicNameValuePair("refresh_token", refreshToken));
        post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    
        org.Apache.http.HttpResponse response = client.execute(post);
        BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        StringBuffer buffer = new StringBuffer();
        for (String line = reader.readLine(); line != null; line = reader.readLine())
        {
            buffer.append(line);
        }
    
        JSONObject json = new JSONObject(buffer.toString());
        String accessToken = json.getString("access_token");
    
        return accessToken;
    
    }
    catch (IOException e) { e.printStackTrace(); }
    
    return null;
    

    }

  2. Maintenant, nous pouvons accéder à l'API Android. Je suis intéressé par le délai d'expiration d'un abonnement, donc:

    private static HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private static JsonFactory JSON_FACTORY = new com.google.api.client.json.jackson2.JacksonFactory();
    
    private static Long getSubscriptionExpire(String accessToken, String refreshToken, String subscriptionId, String purchaseToken){
    
    try{
    
        TokenResponse tokenResponse = new TokenResponse();
        tokenResponse.setAccessToken(accessToken);
        tokenResponse.setRefreshToken(refreshToken);
        tokenResponse.setExpiresInSeconds(3600L);
        tokenResponse.setScope("https://www.googleapis.com/auth/androidpublisher");
        tokenResponse.setTokenType("Bearer");
    
        HttpRequestInitializer credential =  new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
                .setJsonFactory(JSON_FACTORY)
                .setClientSecrets(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
                .build()
                .setFromTokenResponse(tokenResponse);
    
        Androidpublisher publisher = new Androidpublisher.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).
                setApplicationName(GOOGLE_PRODUCT_NAME).
                build();
    
        Androidpublisher.Purchases purchases = publisher.purchases();
        Get get = purchases.get(GOOGLE_PACKAGE_NAME, subscriptionId, purchaseToken);
        SubscriptionPurchase subscripcion = get.execute();
    
        return subscripcion.getValidUntilTimestampMsec();
    
    }
    catch (IOException e) { e.printStackTrace(); }
    return null;
    

    }

Et c'est tout!

Certaines étapes sont de https://developers.google.com/Android-publisher/authorization .

43
Jonathan Naguin

Vous pouvez utiliser com.google.api-client et google-api-services-androidpublisher bibliothèques.

Accédez d'abord au projet sur la console développeur de Google ( https://console.developers.google.com )

  • API et authentification -> API
  • Activez "Google Play Android API développeur"
  • Accédez à Informations d'identification -> Créer un nouvel ID client
  • Sélectionnez un compte de service
  • Créer un ID client
  • Enregistrez le fichier p12 dans un endroit sûr

Ajoutez ensuite l'adresse e-mail que vous venez de générer pour le compte de service à votre console développeur Google Play ( https://play.google.com/apps/publish/ )

  • Paramètres -> Comptes d'utilisateurs et droits -> Inviter un nouvel utilisateur
  • Collez le @developer.gserviceaccount.com compte email
  • Sélectionnez "Afficher les rapports financiers"
  • Envoyer une invitation

Passons maintenant au code. Ajoutez les dépendances suivantes à votre fichier pom.xml:

<dependency>
    <groupId>com.google.api-client</groupId>
    <artifactId>google-api-client</artifactId>
    <version>1.18.0-rc</version>
</dependency>
<dependency>
    <groupId>com.google.http-client</groupId>
    <artifactId>google-http-client-jackson2</artifactId>
    <version>1.18.0-rc</version>
</dependency>
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-androidpublisher</artifactId>
    <version>v1.1-rev25-1.18.0-rc</version>
</dependency>

Validez d'abord la signature:

byte[] decoded = BASE64DecoderStream.decode(KEY.getBytes());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(decoded));
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (sig.verify(BASE64DecoderStream.decode(signature.getBytes())))
{
    // Valid
}

Si la signature vérifie les détails de l'abonnement fetch:

// fetch signature details from google
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleCredential credential = new GoogleCredential.Builder()
    .setTransport(httpTransport)
    .setJsonFactory(jsonFactory)
    .setServiceAccountId(ACCOUNT_ID)
    .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/androidpublisher"))
    .setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
    .build();

AndroidPublisher pub = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential)
    .setApplicationName(APPLICATION_NAME)
    .build();
AndroidPublisher.Purchases.Get get = pub.purchases().get(
    APPLICATION_NAME,
    PRODUCT_ID,
    token);
SubscriptionPurchase subscription = get.execute();
System.out.println(subscription.toPrettyString());

Cela résoudra tous les problèmes de jeton en générant un jeton JWT afin que vous n'ayez pas à le gérer vous-même.

5
Miha Hribar

Pour ceux qui veulent vérifier l'état de l'abonnement sur AppEngine de Google avec Java, voici mon exemple de travail basé sur de nombreux codes trouvés sur SO. J'ai passé quelques jours pour résoudre de nombreuses erreurs causées par le manque d'expérience. Je vois beaucoup de suggestions pour vérifier l'état de l'abonnement sur le serveur mais cela n'a pas été facile pour moi de le faire sur AppEngine. Sans réponses trouvées sur SO, je ne pourrais pas trouver cela.

Étape 1

Nous devons d'abord passer par la section "Prérequis" trouvée dans la réponse de Jonathan Naguin, jusqu'à ce que vous obteniez code du navigateur Web. Maintenant vous avez;

  • Identité du client
  • Secret client
  • Rediriger l'URI
  • code

prêt.

Notez que nous exécutons tous les codes indiqués ci-dessous sur AppEngine. Et j'ai utilisé un enregistreur comme celui-ci.

static final Logger log = Logger.getLogger(MyClassName.class.getName());

Étape 2

Nous devons obtenir un jeton d'actualisation. Exécutez le code ci-dessous après avoir remplacé [VOTRE ID DE CLIENT], [VOTRE SECRET CLIENT], [VOTRE CODE], [VOTRE URI REDIRECT] par votre chaîne.

private String getRefreshToken()
{
    try
    {
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("grant_type","authorization_code");
        params.put("client_id",[YOUR CLIENT ID]);
        params.put("client_secret",[YOUR CLIENT SECRET]);
        params.put("code",[YOUR CODE]);
        params.put("redirect_uri",[YOUR REDIRECT URI]);

        StringBuilder postData = new StringBuilder();
        for(Map.Entry<String,Object> param : params.entrySet())
        {
            if(postData.length() != 0)
            {
                postData.append('&');
            }
            postData.append(URLEncoder.encode(param.getKey(),"UTF-8"));
            postData.append('=');
            postData.append(URLEncoder.encode(String.valueOf(param.getValue()),"UTF-8"));
        }
        byte[] postDataBytes = postData.toString().getBytes("UTF-8");

        URL url = new URL("https://accounts.google.com/o/oauth2/token");
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.getOutputStream().write(postDataBytes);

        BufferedReader  reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuffer buffer = new StringBuffer();
        for (String line = reader.readLine(); line != null; line = reader.readLine())
        {
            buffer.append(line);
        }

        JSONObject json = new JSONObject(buffer.toString());
        String refreshToken = json.getString("refresh_token");
        return refreshToken;
    }
    catch (Exception ex)
    {
        log.severe("oops! " + ex.getMessage());
    }
    return null;
}

Puisque le jeton d'actualisation n'expirera pas, nous pouvons l'enregistrer quelque part ou simplement coder en dur dans notre code. (Nous n'avons besoin d'exécuter le code ci-dessus qu'une seule fois pour obtenir un jeton d'actualisation.)

Étape

Nous devons obtenir un jeton d'accès. Exécutez le code ci-dessous après avoir remplacé [VOTRE ID DE CLIENT], [VOTRE SECRET CLIENT], [VOTRE JETON DE RAFRAÎCHISSEMENT] par votre chaîne.

private String getAccessToken()
{
    try
    {
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("grant_type","refresh_token");
        params.put("client_id",[YOUR CLIENT ID]);
        params.put("client_secret",[YOUR CLIENT SECRET]);
        params.put("refresh_token",[YOUR REFRESH TOKEN]);

        StringBuilder postData = new StringBuilder();
        for(Map.Entry<String,Object> param : params.entrySet())
        {
            if(postData.length() != 0)
            {
                postData.append('&');
            }
            postData.append(URLEncoder.encode(param.getKey(),"UTF-8"));
            postData.append('=');
            postData.append(URLEncoder.encode(String.valueOf(param.getValue()),"UTF-8"));
        }
        byte[] postDataBytes = postData.toString().getBytes("UTF-8");

        URL url = new URL("https://accounts.google.com/o/oauth2/token");
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.getOutputStream().write(postDataBytes);

        BufferedReader  reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuffer buffer = new StringBuffer();
        for (String line = reader.readLine(); line != null; line = reader.readLine())
        {
            buffer.append(line);
        }

        JSONObject json = new JSONObject(buffer.toString());
        String accessToken = json.getString("access_token");
        return accessToken;
    }
    catch (Exception ex)
    {
        log.severe("oops! " + ex.getMessage());
    }
    return null;
}

Étape 4

Ce que je voulais savoir, c'est expirer simplement UTC de l'abonnement. Le code ci-dessous renvoie expire UTC, 0 en cas d'erreur trouvée. Vous devez fournir le nom de votre package, l'identifiant du produit (= id d'abonnement), le jeton d'accès que vous avez obtenu à l'étape 3 et le jeton d'achat trouvé dans vos données d'achat.

private long getExpireDate(String packageName,String productId,String accessToken,String purchaseToken)
{
    try
    {
        String charset = "UTF-8";
        String query = String.format("access_token=%s",URLEncoder.encode(accessToken,charset));

        String path = String.format("https://www.googleapis.com/androidpublisher/v1/applications/%s/subscriptions/%s/purchases/%s",packageName,productId,purchaseToken);
        URL url = new URL(path + "?" + query);
        HttpURLConnection connection = (HttpURLConnection)url.openConnection();
        connection.setRequestProperty("Accept-Charset",charset);
        connection.setRequestMethod("GET");

        BufferedReader  reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuffer buffer = new StringBuffer();
        for(String line = reader.readLine(); line != null; line = reader.readLine())
        {
            buffer.append(line);
        }

        JSONObject json = new JSONObject(buffer.toString());
        return json.optLong("validUntilTimestampMsec");
    }
    catch (Exception ex)
    {
        log.severe("oops! " + ex.getMessage());
    }
    return 0;
}

L'ID de produit ou l'ID d'abonnement est une chaîne trouvée sur la console du développeur. Votre élément d'abonnement apparaît avec la colonne nom/id. Cela ressemble à ceci.

Description of item(product id)

Dernière étape (partie amusante)

Nous avons maintenant tous les composants pour vérifier que l'abonnement est valide ou non. J'ai aimé ça. Vous devez remplacer [VOTRE NOM DE PAQUET], [VOTRE ID DE PRODUIT] par le vôtre.

Vous devez fournir des données d'achat que vous pouvez obtenir avec Purchase # getOriginalJson () trouvé dans le code iabHelper.

private boolean checkValidSubscription(String purchaseData)
{
    String purchaseToken;
    JSONObject json;
    try
    {
        json = new JSONObject(purchaseData);
    }
    catch (JSONException e)
    {
        log.severe("purchaseData is corrupted");
        return true;    // false positive
    }
    purchaseToken = json.optString("purchaseToken");
    if(purchaseToken.length() == 0)
    {
        log.severe("no purchase token found");
        return true;    // false positive
    }
    String accessToken = getAccessToken();
    if(accessToken == null)
    {
        return true;    // false positive
    }
    long expireDate = getExpireDate([YOUR PACKAGE NAME],[YOUR PRODUCT ID],accessToken,purchaseToken);
    if(expireDate == 0)
    {
        log.severe("no expire date found");
        return true;    // false positive
    }
    expireDate += 86400000l;    // add one day to avoid mis judge
    if(expireDate  < System.currentTimeMillis())
    {
        log.severe("subscription is expired");
        return false;
    }
    // just for log output
    long leftDays = (expireDate - System.currentTimeMillis()) / 86400000l;
    log.info(leftDays + " days left");
    return true;
}

Remarque pour le débogage

Google renvoie une chaîne JSON pour réponse. Si le code ne fonctionne pas comme prévu, la journalisation de la chaîne JSON peut aider à comprendre ce qui ne va pas.

J'espère que ça aidera quelqu'un.

4
Tomcat

Pour vous baser sur la grande réponse de Jonathan Naguin, voici une version nodejs pour obtenir le jeton d'actualisation et d'accès:

//This script is to retreive a refresh token and an access token from Google API. 
//NOTE: The refresh token will only appear the first time your client credentials are used. 
//      I had to delete my client id within api console and create a new one to get the refresh token again.

//This is the downloaded json object from Google API Console. Just copy and paste over the template below.
var googleJson = {"web":{"auth_uri":"","client_secret":"","token_uri":"","client_email":"","redirect_uris":[""],"client_x509_cert_url":"","client_id":"","auth_provider_x509_cert_url":"","javascript_origins":[""]}};

//Retrieved from OAuth
var code            = ''; // Retrieved from the response of the URL generated by printGoogleAuthUrl(). You will need to be logged in as your publisher. Copy and paste the generated url. Copy the code parameter into this variable.
var refreshToken    = ''; // Retrieved from the printRefreshToken() function call. Requires the code variable to be filled out.
var accessToken     = ''; // Retrieved from the printAccessToken() function call. Requires the refreshToken variable to be filled out.


var querystring = require('querystring');
var https = require('https');
var fs = require('fs');

function printGoogleAuthUrl()
{
    console.log("https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=" + googleJson.web.redirect_uris[0] + "&client_id=" + googleJson.web.client_id);
}

function printRefreshToken()
{
    var post_data = querystring.stringify({
        'grant_type'    : 'authorization_code',
        'client_id'     : googleJson.web.client_id,
        'client_secret' : googleJson.web.client_secret,
        'code'          : code,
        'redirect_uri'  : googleJson.web.redirect_uris[0]
    });

    var post_options = {
      Host: 'accounts.google.com',
      port: '443',
      path: '/o/oauth2/token',
      method: 'POST',
      headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Length': post_data.length
        }
    };

    var post_req = https.request(post_options, function(res) {
        res.setEncoding('utf8');
        var data = "";
        res.on('data', function (chunk) {
            data += chunk;
        });

        res.on('end', function(){
            var obj = JSON.parse(data);
            if(obj.refresh_token)
            {
                refreshToken = obj.refresh_token;
            }
            else
            {
                console.log("No refresh token found. I had to clear the web client id in Google Api Console and create a new one. There might be a better way here.");
            }   

            console.log(data);

        });
    });

    post_req.write(post_data);
    post_req.end();
}

function printAccessToken()
{
    var post_data = querystring.stringify({
        'grant_type'    : 'refresh_token',
        'client_id'     : googleJson.web.client_id,
        'client_secret' : googleJson.web.client_secret,
        'refresh_token' : refreshToken
    });

    var post_options = {
      Host: 'accounts.google.com',
      port: '443',
      path: '/o/oauth2/token',
      method: 'POST',
      headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Length': post_data.length
        }
    };

    var post_req = https.request(post_options, function(res) {
        res.setEncoding('utf8');
        var data = "";
        res.on('data', function (chunk) {
            data += chunk;
        });

        res.on('end', function(){
            var obj = JSON.parse(data);
            if(obj.access_token)
                accessToken = obj.access_token;
            else
                console.log("No access token found.");

            console.log(data);

        });
    });

    post_req.write(post_data);
    post_req.end();
}

printGoogleAuthUrl();
//printRefreshToken();  
//printAccessToken();
4
KoboldAtWork