无法从Google Play Android开发者API获取订阅信息

我尝试使用Google API的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是来自Google Console的API访问的电子邮件地址。 GOOGLE_KEY_FILE_PATH是从API Access下载的p12文件。
GOOGLE_PRODUCT_NAME是品牌信息中的产品名称。
在Google APIS控制台中启用了“Google Play Android开发者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." } 

我非常感谢你对这个问题的帮助

我得到它的工作! 我遵循的步骤是:

条件

开始之前,我们需要生成一个刷新令牌。 要做到这一点,我们必须创build一个API控制台项目:

  1. 转到API控制台并使用您的Android开发者帐户(与Android开发者控制台中使用的相同帐户上传APK)login。
  2. select创build项目。
  3. 转到左侧导航面板中的“服务”。
  4. 开启Google Play Android开发者API
  5. 接受服务条款。
  6. 转到左侧导航面板中的“API访问”。
  7. select创buildOAuth 2.0客户端ID:
    • 在第一页上,您需要填写产品名称,但不需要徽标。
    • 在第二页上,selectWeb应用程序并设置redirectURI和Javascript来源。 稍后我们将使用redirectURI。
  8. select创build客户端ID。 请记住客户端ID客户端密码 ,稍后我们会使用它们。

所以,现在我们可以生成刷新标记了:

  1. 转到以下URI(请注意,redirectURI必须完全匹配在客户端ID中input的值,包括任何尾随的反斜杠):

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. 提示时select允许访问。
  2. 浏览器将通过代码参数redirect到您的redirectURI,该代码参数类似于4 / eWdxD7b-YSQ5CNNb-c2iI83KQx19.wp6198ti5Zc7dJ3UXOl0T3aRLxQmbwI。 复制这个值。

创build一个主要的类:

 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_IDGOOGLE_CLIENT_SECRETGOOGLE_REDIRECT_URI是以前的值。

最后,我们有我们的刷新标记! 这个值不会过期,所以我们可以存储在某个站点,比如一个属性文件。

访问Google Play Android开发者API

  1. 获取访问令牌。 我们将需要我们以前的刷新标记:

     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. 现在,我们可以访问Android API。 我在订阅的到期时间很有趣,所以:

     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

为了搭载Jonathan Naguin的出色答案,下面是一个获取刷新和访问令牌的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(); 

您可以使用com.google.api-clientgoogle-api-services-androidpublisher库。

首先转到Google开发者控制台( https://console.developers.google.com )上的项目

  • API和身份validation – > API
  • 启用“Google Play Android开发人员API”
  • 转到凭据 – >创build新的客户端ID
  • select服务帐户
  • 创build客户端ID
  • 将p12文件保存在安全的地方

然后将刚刚生成的电子邮件地址添加到您的Google Play开发者控制台( https://play.google.com/apps/publish/

  • 设置 – >用户帐户和权限 – >邀请新用户
  • 粘贴@developer.gserviceaccount.com电子邮件帐户
  • select“查看财务报告”
  • 发送邀请

现在到代码。 将以下依赖项添加到您的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> 

然后首先validation签名:

 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 } 

如果签名validation提取订阅详细信息:

 // 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令牌来处理所有令牌问题,因此您不必亲自处理它。

对于那些想在Java的AppEngine上检查订阅状态的人,这里是基于在SO上发现的许多代码的工作示例。 我花了几天的时间来解决由于经验不足而造成的许多错误。 我看到很多build议来检查服务器上的订阅状态,但是在AppEngine上做这件事并不容易。 没有在SO上find答案,我不能拿出这个。

步骤1

首先,我们需要通过Jonathan Naguin的答案中的“先决条件”部分,直到您从Web浏览器获取代码 。 现在你有;

  • 客户端ID
  • 客户秘密
  • redirectURI

准备。

请注意,我们在AppEngine上运行以下所示的所有代码。 我用这样的logging器。

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

第2步

我们需要获得更新令牌。 使用stringreplace[您的客户端ID],[您的客户端机密],[您的代码],[您的redirectURI]后,运行下面的代码。

 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步

我们需要获得访问令牌。 使用stringreplace[您的客户端ID],[您的客户端密码],[您的刷新密钥]后,运行下面的代码。

 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。 下面显示的代码返回UTC到期日,发现错误时为0。 您需要提供您的包裹名称,产品ID(=订阅ID),您在步骤3中获取的访问令牌,以及在您的购买数据中find的购买令牌。

 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; } 

注意产品ID或订阅ID是在开发者控制台上find的string。 您的订阅项目显示名称/ ID列。 看起来像这样

 Description of item(product id) 

最后一步(有趣的部分)

现在我们有所有的组件来validation订阅是否有效。 我喜欢这个。 您需要将“您的产品编号”,“您的产品编号”replace为您的产品编号。

您需要提供您可以通过在iabHelper代码中find的Purchase#getOriginalJson()获得的购买数据。

 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; } 

注意debugging

Google返回JSONstring以进行响应。 如果代码不能按预期工作,则loggingJSONstring可能有助于理解错误。

我希望这可以帮助别人。

我很确定你必须使用你的客户端ID,而不是电子邮件地址。 它看起来像这样:37382847321922.apps.googleusercontent.com

请参阅https://developers.google.com/android-publisher/authorization

 client_id=<the client ID token created in the APIs Console> 

而且我很确定你不需要P12文件。 你只需要

 client_secret=<the client secret corresponding to the client ID> 

首先尝试从命令行手动执行,使用“wget”。