Не удается получить информацию о подписке из Google Play Android Developer API
Я пытаюсь использовать клиентскую библиотеку Google APIs для Java, чтобы получать информацию о пользовательских подписках, приобретенных в моем приложении для Android. Вот как я сейчас поступаю.:
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
это адрес электронной почты из API Access из консоли Google.GOOGLE_KEY_FILE_PATH
это файл p12, загруженный из API Access.GOOGLE_PRODUCT_NAME
это название продукта из информации о бренде.
В консоли Google APIS включена услуга "Google Play Android Developer API".
Что я получаю, так это:
{
"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."
}
Я действительно ценю вашу помощь в решении этой проблемы...
Переведено автоматически
Ответ 1
У меня получилось! Шаги, которые я выполнил:
Обязательное условие
Перед запуском нам нужно сгенерировать токен обновления. Для этого сначала нам нужно создать проект консоли API:
- Перейдите в консоль API и войдите в свою учетную запись разработчика Android (та же учетная запись, которая использовалась в Консоли разработчика Android для загрузки APK).
- Выберите Создать проект.
- Перейдите к Сервисам на левой панели навигации.
- Включите Google Play Android Developer API.
- Принимаю Условия предоставления услуг.
- Перейдите к API Access на левой панели навигации.
- Выберите Создать идентификатор клиента OAuth 2.0:
- На первой странице вам нужно будет ввести название продукта, но логотип не требуется.
- На второй странице выберите веб-приложение и задайте URI перенаправления и Javascript origins. Позже мы будем использовать URI перенаправления.
- Выберите Создать идентификатор клиента. Имейте в виду идентификатор клиента и секрет клиента, мы будем использовать их позже.
Итак, теперь мы можем сгенерировать токен обновления:
- Перейдите к следующему URI (обратите внимание, что URI перенаправления должен точно соответствовать значению, введенному в идентификаторе клиента, включая любые завершающие обратные косые черты):
- Выберите Разрешить доступ при появлении запроса.
- Браузер будет перенаправлен на ваш URI перенаправления с параметром code, который будет выглядеть примерно так: 4/eWdxD7b-YSQ5CNNb-c2iI83KQx19.wp6198ti5Zc7dJ3UXOl0T3aRLxQmbwI. Скопируйте это значение.
Создайте основной класс с:
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
and GOOGLE_REDIRECT_URI
are the previously values.
Finally, we have our refresh token! This value does not expire, so we can store in some site, like a property file.
Accessing to Google Play Android Developer API
Getting the access token. We will need our previosly refresh token:
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;}
Now, we can access to the Android API. I'm interesting in the expiration time of a subscription, so:
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;}
И это все!
Некоторые шаги приведены в https://developers.google.com/android-publisher/authorization.
Ответ 2
Вы можете использовать com.google.api-client
и google-api-services-androidpublisher
библиотеки.
Сначала перейдите к проекту в консоли разработчика Google (https://console.developers.google.com)
- API и аутентификация -> API
- Включить "Google Play Android Developer API"
- Перейдите в раздел Учетные данные -> Создать новый идентификатор клиента
- Выберите учетную запись службы
- Создать идентификатор клиента
- Сохраните файл p12 в безопасном месте
Затем добавьте только что сгенерированный адрес электронной почты для учетной записи службы в консоль разработчика Google Play (https://play.google.com/apps/publish /)
- Настройки -> Пользователи и разрешения -> Пригласить новых пользователей
- Вставьте
@developer.gserviceaccount.com
учетную запись электронной почты - Выберите "Просмотреть финансовые отчеты"
- Отправить приглашение
Теперь к коду. Добавьте следующие зависимости в файл 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>
Затем сначала проверьте подпись:
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
}
Если подпись подтверждает получение сведений о подписке:
// 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());
Это решит все проблемы с токенами путем генерации токена JWT, так что вам не придется справляться с этим самостоятельно.
Ответ 3
Для тех, кто хочет проверить состояние подписки в AppEngine от Google с помощью Java, вот мой рабочий пример, основанный на множестве кодов, найденных на SO. Я потратил пару дней, чтобы исправить множество ошибок, вызванных отсутствием опыта. Я вижу много предложений по проверке состояния подписки на сервере, но мне было нелегко сделать это в AppEngine. Не найдя ответов на SO, я не смог придумать это.
Шаг 1
Сначала нам нужно пройти раздел "Предварительные требования", найденный в ответе Джонатана Нагина, пока вы не получите код из веб-браузера. Теперь у вас есть;
- Идентификатор клиента
- Секрет клиента
- Перенаправить URI
- код
готово.
Примечание. мы запускаем все коды, показанные ниже, в AppEngine. И я использовал logger, подобный этому.
static final Logger log = Logger.getLogger(MyClassName.class.getName());
Шаг 2
Нам нужно получить токен обновления. Запустите приведенный ниже код после замены [ВАШЕГО ИДЕНТИФИКАТОРА КЛИЕНТА], [ВАШЕГО СЕКРЕТА КЛИЕНТА], [ВАШЕГО КОДА], [ВАШЕГО URI ПЕРЕНАПРАВЛЕНИЯ] на вашу строку.
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;
}
Поскольку срок действия токена обновления не истекает, мы можем сохранить его где-нибудь или просто жестко прописать в нашем коде. (Нам нужно только один раз запустить приведенный выше код, чтобы получить токен обновления.)
Шаг 3
Нам нужно получить токен доступа. Запустите приведенный ниже код после замены [ВАШЕГО ИДЕНТИФИКАТОРА КЛИЕНТА], [ВАШЕГО СЕКРЕТА КЛИЕНТА], [ВАШЕГО ТОКЕНА ОБНОВЛЕНИЯ] на вашу строку.
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;
}
Шаг 4
Я хотел знать, что срок действия подписки истекает по UTC. Приведенный ниже код возвращает ошибку expire UTC, 0 при обнаружении. Вам необходимо указать название вашего пакета, идентификатор продукта (= идентификатор подписки), токен доступа, полученный на шаге 3, и токен покупки, указанный в ваших данных о покупке.
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;
}
Примечание: идентификатор продукта или идентификатор подписки - это строка, найденная в консоли разработчика. Элемент вашей подписки отображается в столбце name / id . Это выглядит следующим образом.
Description of item(product id)
Последний шаг (забавная часть)
Теперь у нас есть все компоненты для проверки того, действительна подписка или нет. Мне понравилось это. Вам нужно заменить [ИМЯ ВАШЕГО ПАКЕТА], [ВАШ ИДЕНТИФИКАТОР ПРОДУКТА] на ваши.
Вам необходимо предоставить данные о покупке, которые вы можете получить с помощью функции Purchase#getOriginalJson(), найденной в коде 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;
}
Примечание по отладке
Google возвращает строку JSON для ответа. Если код работает не так, как ожидалось, регистрация строки JSON может помочь понять, в чем проблема.
Я надеюсь, что это кому-то поможет.
Ответ 4
В дополнение к замечательному ответу Джонатана Нагуина, вот версия получения токена обновления и доступа на nodejs:
//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();