java - subir - No se puede obtener la información de suscripción de la API de Google Play para desarrolladores de Android
google play developer console (5)
Estoy intentando usar Google APIs Client Library para Java para obtener información sobre las suscripciones compradas en mi aplicación de Android. Así es como estoy por ahora:
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
es la dirección de correo electrónico de acceso API desde la consola de Google. GOOGLE_KEY_FILE_PATH
es el archivo p12 descargado de API Access.
GOOGLE_PRODUCT_NAME
es el nombre del producto de la información de marca.
En Google APIS Console, el Servicio "Google Play Android Developer API" está habilitado.
Lo que obtengo es:
{
"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."
}
Realmente aprecio tu ayuda para este problema ...
¡Lo tengo trabajando! Los pasos que seguí:
Requisito previo
Antes de comenzar, necesitamos generar un token de actualización. Para hacer esto primero tenemos que crear un proyecto de consola de API:
- Acceda a la consola de API e inicie sesión con su cuenta de desarrollador de Android (la misma cuenta utilizada en la consola de desarrollo de Android para cargar el APK).
- Seleccione Crear proyecto.
- Vaya a Servicios en el panel de navegación de la izquierda.
- Activar la API de Google Play para desarrolladores de Android .
- Acepte los términos del servicio.
- Vaya a Acceso API en el panel de navegación izquierdo.
- Seleccione Crear una ID de cliente de OAuth 2.0:
- En la primera página, deberá completar el nombre del producto, pero no se requiere un logotipo.
- En la segunda página, seleccione la aplicación web y configure los orígenes de URI y Javascript de redireccionamiento . Lo usaremos más tarde el URI de redirección.
- Seleccione Crear ID de cliente. Tenga en cuenta la identificación del cliente y el secreto del cliente , los usaremos más adelante.
Entonces, ahora podemos generar el token de actualización:
- Vaya al siguiente URI (tenga en cuenta que el URI de redireccionamiento debe coincidir exactamente con el valor ingresado en la ID del cliente, incluidas las barras invertidas posteriores):
- Seleccione Permitir acceso cuando se le solicite.
- El navegador se redirigirá a su URI de redirección con un parámetro de código , que se verá similar a 4 / eWdxD7b-YSQ5CNNb-c2iI83KQx19.wp6198ti5Zc7dJ3UXOl0T3aRLxQmbwI. Copia este valor
Crea una clase principal con:
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
y GOOGLE_REDIRECT_URI
son los valores anteriores.
¡Finalmente, tenemos nuestro token de actualización! Este valor no caduca, por lo que podemos almacenarlo en algún sitio, como un archivo de propiedades.
Acceder a Google Play API para desarrolladores de Android
Obtener el token de acceso. Necesitaremos nuestro token de actualización anterior:
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;
}
Ahora, podemos acceder a la API de Android. Me interesa el vencimiento de una suscripción, así que:
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;
}
¡Y eso es todo!
Algunos pasos son de https://developers.google.com/android-publisher/authorization .
Estoy bastante seguro de que debes usar tu ID de cliente, no la dirección de correo electrónico. Se ve así: 37382847321922.apps.googleusercontent.com
Ver https://developers.google.com/android-publisher/authorization
client_id=<the client ID token created in the APIs Console>
Y estoy bastante seguro de que no necesitas un archivo P12. Solo necesitas el
client_secret=<the client secret corresponding to the client ID>
Intenta hacerlo manualmente desde la línea de comando primero, con ''wget''.
Para aprovechar la gran respuesta de Jonathan Naguin, aquí hay una versión nodejs de obtener el token de actualización y acceso:
//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();
Para aquellos que quieran consultar el estado de la suscripción en AppEngine de Google con Java, aquí está mi ejemplo de trabajo basado en muchos códigos que se encuentran en SO. Pasé un par de días para resolver muchos errores causados por la falta de experiencia. Veo muchas sugerencias para verificar el estado de la suscripción en el servidor, pero no fue fácil para mí en AppEngine. Sin respuestas encontradas en SO, no podría pensar en esto.
Paso 1
Primero, debemos pasar por la sección de "Prerrequisito" que se encuentra en la respuesta de Jonathan Naguin, hasta que obtenga el código del navegador web. Ahora tu tienes;
- Identificación del cliente
- Secreto del cliente
- Redirigir URI
- código
Listo.
Tenga en cuenta que ejecutamos todos los códigos que se muestran a continuación en App Engine. Y usé el registrador de esta manera.
static final Logger log = Logger.getLogger(MyClassName.class.getName());
Paso 2
Necesitamos obtener un token de actualización. Ejecute el código que se muestra a continuación después de reemplazar [SU ID DE CLIENTE], [SU SECRETO DEL CLIENTE], [SU CÓDIGO], [SU URI REDIRECT] con su cadena.
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;
}
Como el token de actualización no caducará, podemos guardarlo en algún lugar o simplemente codificarlo en nuestro código. (Solo tenemos que ejecutar el código una vez para obtener el token de actualización).
Paso 3
Necesitamos obtener el token de acceso. Ejecute el código que se muestra a continuación después de reemplazar [SU IDENTIFICACIÓN DEL CLIENTE], [SU SECRETO DEL CLIENTE], [SU ACTUACIÓN REFRESH] con su cadena.
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;
}
Etapa 4
Lo que quería saber es que vence el UTC de la suscripción. El código que se muestra a continuación retorna a UTC, 0 cuando se encuentra el error. Debe proporcionar el nombre de su paquete, el ID del producto (= id de suscripción), el token de acceso que recibió en el Paso 3 y el token de compra que se encuentra en los datos de su compra.
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;
}
Tenga en cuenta que la identificación del producto o la identificación de la suscripción es una cadena que se encuentra en la consola de desarrollador. Su elemento de suscripción aparece con la columna de nombre / id. Se parece a esto.
Description of item(product id)
Último paso (parte divertida)
Ahora tenemos todos los componentes para verificar que la suscripción es válida o no. Me gustó esto Debe reemplazar [SU NOMBRE DEL PAQUETE], [SU ID DE PRODUCTO] con el suyo.
Debe proporcionar los datos de compra que puede obtener con la compra # getOriginalJson () que se encuentra en el código 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;
}
Nota para la depuración
Google devuelve cadena JSON para la respuesta. Si el código no funciona como se espera, el registro de la cadena JSON puede ayudar a comprender qué es lo que está mal.
Espero que esto ayude a alguien.
Puede usar las bibliotecas com.google.api-client
y google-api-services-androidpublisher
.
Primero vaya al proyecto en la consola de desarrollador de Google ( https://console.developers.google.com )
- API y autenticación -> API
- Habilita "Google Play API para desarrolladores de Android"
- Vaya a Credenciales -> Crear nueva ID de cliente
- Seleccionar cuenta de servicio
- Crear ID de cliente
- Guarde el archivo p12 en un lugar seguro
A continuación, agregue la dirección de correo electrónico recién generada para la cuenta de servicio a su consola de desarrollador de google play ( https://play.google.com/apps/publish/ )
- Configuración -> Cuentas y derechos de usuario -> Invitar nuevo usuario
- Pegue la cuenta de correo electrónico de
@developer.gserviceaccount.com
- Seleccione "Ver informes financieros"
- Enviar invitacion
Ahora al código. Agregue las siguientes dependencias a su archivo 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>
Entonces primero valide la firma:
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 firma verifica los detalles de la suscripción:
// 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());
Esto se encargará de todos los problemas del token generando un token JWT para que no tenga que manejarlo usted mismo.