问题描述
我正在尝试设置一个网络服务来查询 Google Play 购买.我们为客户存储订单信息,该服务会调用 Google Play API 来查询订阅详情.
I am trying to set up a web service to query Google Play purchases. We store the order information for customers and this service would call Google Play API to query the subscription details.
每次我尝试查询购买时,都会出现错误:
Every time i try to query a purchase, it gives me the error:
HTTP/1.1 400 Bad Request { "error":{ "errors":[ { "domain":"global", "reason":"invalid", "message":"Invalid Value" } ], "code":400, "message":"Invalid Value" } }
这是我尝试过的:
- 在 https://console.developers.google.com 中创建了一个项目,启用了Google玩 Android 开发者 API"
- 为类型 Web 应用程序创建了 oAuth 2.0 client_id 和 client_secret
- 以账户所有者身份登录,我生成了一个 refresh_token
- 在 https://play.google.com/apps/publish 我去了设置 -> API 访问并将项目链接到我的应用
- Created a project in https://console.developers.google.com enabled the "Google Play Android Developer API"
- Created an oAuth 2.0 client_id and client_secret for type Web application
- Logged in as the account owner, I generated a refresh_token
- In https://play.google.com/apps/publish I went to Settings -> API Access and linked the the project to my app
代码方面,我使用了 refresh_token 来获取 access_token:
Code wise, I used the refresh_token to get an access_token:
String refreshToken = "1/ljll6d9ME3Uc13jMrBweqXugV4g4timYcXXXXXXXXX"; HttpPost request = new HttpPost("https://accounts.google.com/o/oauth2/token"); List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("client_id", client_id)); params.add(new BasicNameValuePair("client_secret", client_secret)); params.add(new BasicNameValuePair("refresh_token", refreshToken)); params.add(new BasicNameValuePair("grant_type", "refresh_token")); request.setEntity(new UrlEncodedFormEntity(params)); HttpResponse response = httpClient.execute(request); HttpEntity entity = response.getEntity(); String body = EntityUtils.toString(entity); JSONObject json = new JSONObject(body); String accessToken = json.getString("access_token");
这里的 access_token 有效,因为我可以用它调用这个 API 并获取响应:
The access_token from this works because i can call this API with it and get the response back:
String url = String.format("https://www.googleapis.com/androidpublisher/v2/applications/%s/inappproducts/%s", packageName, productId); HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(url); get.setHeader("Authorization", "Bearer " + accessToken); HttpResponse response = client.execute(get); // parse response etc...
这会返回:
{ "packageName":"com.my.app", "sku":"com.my.app.premium", "status":"active", "purchaseType":"subscription", "defaultPrice":{ //... } }, "listings":{ "en-US":{ "title":"My App Premium", "description":"My App" } }, "defaultLanguage":"en-US", "subscriptionPeriod":"P1Y" }
现在,我想获取有关购买的信息.我有这样的购买信息:
Now, I want to get informatoin about a purchase. I have a information from a purchase as such:
{ "orderId":"GPA.1111-1111-1111-11111", "packageName":"com.my.app", "productId":"com.my.app.premium", "purchaseTime":1452801843877, "purchaseState":0, "developerPayload":"XXXXXXXd9261023a407ae5bb6ab8XXXXXXX", "purchaseToken":"xxxxxxxxxxxxxx.YY-J123o12-xxxxxxxxxxxxxxxmYRk2itBkNdlXhyLMjXsxxxxxxxxxxxxLfBxabaAjKbeBC0PVhHnHd1DDbFkgZtbQxxk5pDIAH3xBHu8HrcWfRgewAYnFeW9xxxxxxxxxxxxxC5TDjcBL8fhf", "autoRenewing":true } String url = String.format("https://www.googleapis.com/androidpublisher/v2/applications/%s/purchases/products/%s/tokens/%s",packageName, productId, purchaseToken); HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(url); get.setHeader("Authorization", "Bearer " + accessToken); HttpResponse response = client.execute(get); // parse response etc...
由于 packageName/productId 和 access_token 似乎适用于第一次调用,并且 purchaseToken 正好在订单信息之外.什么是无效值错误?
Since the packageName / productId and access_token seem to work for the first call, and the purchaseToken is right out of the order info. What is giving the invalid value error?
任何帮助表示赞赏 - 不知道还有什么可以尝试的.谢谢!
Any help appreciated - not sure what else to try. Thanks!
更新:我检查并验证了所有包名称和帐户设置真正的问题似乎是我所打的服务.我将其切换为:https://www.googleapis.com/androidpublisher/v2/applications/packageName/purchases/subscriptions/subscriptionId/tokens/purchaseToken
UPDATE: I went through and validated all the package names and account setup The real issue seemed to be the service i was hitting. I switched it to: https://www.googleapis.com/androidpublisher/v2/applications/packageName/purchases/subscriptions/subscriptionId/tokens/purchaseToken
我也改用 Google Client API,因为它看起来比手动创建请求更简洁.
I also swapped to use the Google Client API as it was much cleaner looking that manually creating requests.
感谢您的帮助和回复
推荐答案
首先我想和大家分享一下什么是400 bad request,什么是发生的真正原因?
First I want to share with you what is 400 bad request and what is the real cause for occuring it?
Ans:表示查询无效.例如,缺少父 ID,或者请求的维度或指标组合无效.
Ans: It indicates that the query was invalid. E.g., parent ID was missing or the combination of dimensions or metrics requested was not valid.
推荐操作:您需要对 API 查询进行更改才能使其正常工作.
Recommended Action: You need to make changes to the API query in order for it to work.
资源链接: 标准错误回应
您的代码运行正常并返回相关的 json 文件作为输出.但是过了一段时间,你想获取购买信息的时候就不行了.它给出错误消息 "HTTP/1.1 400 Bad Request"
Your code was running properly and returning related json file as output. But after a period,it is not working when you want to get information about purchase. It gives error message "HTTP/1.1 400 Bad Request"
对于刷新令牌,响应总是包含一个新的访问令牌.响应如下:
For refresh token, the response always includes a new access token. A response is shown below:
{ "access_token":"1/fFBGRNJru1FQd44AzqT3ZgXXXXXX", "expires_in":3920, "token_type":"Bearer", }
因此,访问令牌有一个到期时间.过期后,访问令牌将失效.
So, access token has a expiry time. after a expiry time, the access token will not work.
还有另一个限制.将发行的刷新令牌的数量有限制;每个客户端/用户组合一个限制,所有客户端的每个用户另一个限制.
There is another restriction also. There are limits on the number of refresh tokens that will be issued; one limit per client/user combination, and another per user across all clients.
因此,就您而言,您已经超过了创建刷新令牌的限制.
因此,您首先需要撤销令牌.然后将刷新令牌保存在长期存储中,并在它们保持有效期间继续使用它们.
So, you first need to revoke the token. Then save refresh tokens in long-term storage and continue to use them as long as they remain valid.
由于您使用的是刷新令牌,因此您需要将 http post 请求 https://accounts.google.com/o/oauth2/token 更改为 https://www.googleapis.com/oauth2/v4/token
As you are using refresh token, then you need to change the http post request https://accounts.google.com/o/oauth2/token to https://www.googleapis.com/oauth2/v4/token
所以你的代码将如下所示:
So your code will be look like below:
String refreshToken = "1/ljll6d9ME3Uc13jMrBweqXugV4g4timYcXXXXXXXXX"; HttpPost request = new HttpPost("https://www.googleapis.com/oauth2/v4/token"); List<NameValuePair> params = new ArrayList<NameValuePair>(); ............... ...............
撤销程序:
有两种撤销方式.
Revoking procedure:
There are 2 ways for revoking.
- 用户可以通过访问帐户设置 来撤消访问权限
- 应用程序也可以通过编程方式撤销授予它的访问权限.
要以编程方式撤销令牌,您的应用程序会向 https://accounts.google.com/o/oauth2/revoke 发出请求,并将令牌作为参数包含在内:
To programmatically revoke a token, your application makes a request to https://accounts.google.com/o/oauth2/revoke and includes the token as a parameter:
curl https://accounts.google.com/o/oauth2/revoke?token={token}
token 可以是访问令牌或刷新令牌.如果token是访问token并且有对应的刷新token,那么刷新token也会被撤销.
The token can be an access token or a refresh token. If the token is an access token and it has a corresponding refresh token, the refresh token will also be revoked.
N.B:如果撤销成功处理,则状态码的响应是 200.对于错误条件,状态代码 400 是连同错误代码一起返回.
N.B: If the revocation is successfully processed, then the status code of the response is 200. For error conditions, a status code 400 is returned along with an error code.
资源链接:
- 离线访问,使用刷新令牌和撤销令牌