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

Java 无法从Google Play Android开发者API获取订阅信息,java,android,google-api,subscription,google-play,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().setT

我正在尝试使用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\u CLIENT\u MAIL
是来自谷歌控制台API访问的电子邮件地址。
GOOGLE\u KEY\u FILE\u PATH是从API访问下载的p12文件。
GOOGLE\u PRODUCT\u NAME
是品牌信息中的产品名称。
在Google API控制台中,启用了“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."
}

我非常感谢您在这个问题上的帮助…

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

client\u id=
我很确定你不需要P12文件。你只需要

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

首先尝试使用“wget”从命令行手动执行此操作

我成功了!我遵循的步骤是:

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

  • 转到并与您的Android开发人员一起登录 帐户(与中用于上载APK的帐户相同)
  • 选择创建项目
  • 转到左侧导航面板中的“服务”
  • 打开Google Play Android开发者API
  • 接受服务条款
  • 转到左侧导航面板中的API访问
  • 选择“创建OAuth 2.0客户端ID”:
    • 在第一页,您需要填写产品名称,但是 标志是不需要的
    • 在第二页上,选择web应用程序,并设置重定向URI 和Javascript的起源。稍后我们将使用它作为重定向URI
  • 选择创建客户端ID。请记住客户端ID客户端机密,我们稍后将使用它们
  • 因此,现在我们可以生成刷新令牌:

    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;
    
  • 转到以下URI(请注意,重定向URI必须与在客户端ID中输入的值完全匹配,包括任何尾随的反斜杠):
  • 出现提示时选择允许访问
  • 浏览器将通过一个代码参数重定向到重定向URI,该参数看起来类似于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;
    }
    
    }

  • 现在,我们可以访问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;
    
    }

  • 就这些


    从。

    到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客户端
    谷歌api服务和IDPublisher

    首先转到google开发者控制台上的项目()

    • api&Auth->api
    • 启用“Google Play Android开发者API”
    • 转到凭据->创建新的客户端ID
    • 选择服务帐户
    • 创建客户端ID
    • 将p12文件保存到安全的地方
    然后将刚刚生成的服务帐户电子邮件地址添加到您的google play开发者控制台()

    • 设置->用户和权限->邀请新用户
    • 粘贴
      @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>
    
    如果签名验证获取订阅详细信息:

    // 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检查Google AppEngine订阅状态的人,下面是我的工作示例,它基于在so上找到的许多代码。我花了几天时间解决了许多因缺乏经验而导致的错误。我看到很多关于在服务器上检查订阅状态的建议,但在AppEngine上做起来并不容易。如果没有找到答案,我就想不出这个

    步骤1

    首先,我们需要浏览乔纳森·纳金答案中的“先决条件”部分,直到您从web浏览器获得代码。现在你有了

    • 客户端ID
    • 客户机密
    • 重定向URI
    • 代码
    准备好了

    注意:我们在AppEngine上运行下面显示的所有代码。我以前也是这样

    static final Logger log = Logger.getLogger(MyClassName.class.getName());
    
    Description of item(product id)
    
    步骤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;
    }
    
    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;
    }
    
    注意:产品id或订阅id是在开发人员控制台上找到的字符串。您的订阅项目将显示“名称/id”列。看起来像这样

    static final Logger log = Logger.getLogger(MyClassName.class.getName());
    
    Description of item(product id)
    
    最后一步(有趣的部分)

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

    您需要提供购买数据,这些数据可以通过代码中的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;
    }
    
    private boolean checkValidSubscription(字符串purchaseData)
    {
    字符串purchaseToken;
    JSONObject json;
    尝试
    {
    json=新的JSONObject(purchaseData);
    }
    捕获(JSONException e)
    {
    日志严重(“purchaseData已损坏”);
    返回true;//假阳性
    }
    purchaseToken=json.optString(“purchaseToken”);
    if(purchaseToken.length()=0)
    {
    日志严重(“未找到购买令牌”);
    返回true;//假阳性
    }
    字符串accessToken=getAccessToken();
    if(accessToken==null)
    {
    返回true;//假阳性
    }
    long expireDate=getExpireDate([您的包名][
    
    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;
    }