无法从Google Play Android Developer API获取订阅信息

时间:2012-06-20 08:14:04

标签: java android google-api subscription google-play

我正在使用Google API Client Library for 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控制台的API Access的电子邮件地址。 GOOGLE_KEY_FILE_PATH是从API Access下载的p12文件 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."
}

我真的很感谢你对这个问题的帮助......

5 个答案:

答案 0 :(得分:41)

我搞定了!我遵循的步骤:

前提条件

在开始之前,我们需要生成刷新令牌。首先,我们必须创建一个API控制台项目:

  1. 转到APIs Console并与Android开发人员一起登录 帐户(Android Developer Console中用于上传APK的帐户)。
  2. 选择创建项目。
  3. 转到左侧导航面板中的“服务”。
  4. 启用 Google Play Android Developer API
  5. 接受服务条款。
  6. 转到左侧导航面板中的API Access。
  7. 选择“创建OAuth 2.0客户端ID”:
    • 在第一页上,您需要填写产品名称,但是a 不需要徽标。
    • 在第二页上,选择网络应用程序并设置重定向URI 和Javascript起源。稍后我们将使用重定向URI。
  8. 选择“创建客户端ID”。请注意客户端ID 客户端密钥,我们稍后会使用它们。
  9. 所以,现在我们可以生成刷新令牌:

    1. 转到以下URI(请注意,重定向URI必须与客户端ID中输入的值完全匹配,包括任何尾部反斜杠):
    2. 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. 选择提示时允许访问。
      2. 浏览器将重定向到您的重定向URI,其中包含代码参数,该参数类似于4 / eWdxD7b-YSQ5CNNb-c2iI83KQx19.wp6198ti5Zc7dJ3UXOl0T3aRLxQmbwI。复制此值。
      3. 使用以下命令创建一个主类:

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

          }

        3. 就是这样!

          有些步骤来自https://developers.google.com/android-publisher/authorization

答案 1 :(得分:5)

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

首先在google开发者控制台(https://console.developers.google.com

上转到该项目
  • API&amp;验证 - &gt;的API
  • 启用“Google Play Android Developer API”
  • 转到凭证 - &gt;创建新的客户端ID
  • 选择服务帐户
  • 创建客户端ID
  • 将p12文件保存在安全的地方

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

  • 设置 - &gt;用户帐户&amp;权利 - &gt;邀请新用户
  • 粘贴@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令牌来处理所有令牌问题,因此您不必自己处理它。

答案 2 :(得分:4)

借助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();

答案 3 :(得分:4)

对于那些想要在Google的AppEngine上查看订阅状态的人来说,这是我在SO上找到的许多代码的工作示例。我花了几天时间来解决因缺乏经验而导致的许多错误。我看到很多建议要检查服务器上的订阅状态,但我在AppEngine上做起来并不容易。如果没有在SO上找到答案,我无法想出这个。

第1步

首先我们需要经历&#34;先决条件&#34;在Jonathan Naguin的回答中找到的部分,直到您从网络浏览器中获得代码。现在你有了;

  • 客户ID
  • 客户端密钥
  • 重定向URI

就绪

注意我们在AppEngine上运行下面显示的所有代码。我用这样的记录器。

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

第2步

我们需要获得刷新令牌。用您的字符串替换[您的客户ID],[您的客户端密码],[您的代码],[您的重定向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步

我们需要获取访问令牌。用您的字符串替换[您的客户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中获得的访问令牌以及购买数据中的购买令牌。

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是在开发者控制台上找到的字符串。您的订阅项目将显示名称/ ID列。它看起来像这样。

Description of item(product id)

最后一步(有趣的部分)

现在我们拥有验证订阅是否有效的所有组件。我确实喜欢这个。您需要用您的[您的产品ID]替换[您的包装名称]。

您需要提供购买数据,您可以使用iabHelper代码中的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;
}

请注意调试

Google会返回JSON字符串以进行回复。如果代码不能按预期工作,那么记录JSON字符串可能有助于理解错误。

我希望这有助于某人。

答案 4 :(得分:-1)

我很确定您必须使用您的客户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'从命令行手动执行此操作。