22

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

Here is what I tried:

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");

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...

This returns:

{  
    "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...

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!

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

I also swapped to use the Google Client API as it was much cleaner looking that manually creating requests.

Thanks for help and replies

mishsx
  • 1,153
  • 4
  • 15
  • 29
Fid
  • 221
  • 1
  • 2
  • 7

3 Answers3

13

First I want to share with you what is 400 bad request and what is the real cause for occuring it?

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.

Recommended Action: You need to make changes to the API query in order for it to work.

Resource Link: Standard Error Responses

Your problem:

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"

Root cause:

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, in your case, you have already crossed your limit of creating refresh token.

Solution:

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.

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.

  1. A user can revoke access by visiting Account Settings
  2. It is also possible for an application to programmatically revoke the access given to it.

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}

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: 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.

Resource Link:

  1. Offline access, Using refresh token and Revoke a token
SkyWalker
  • 24,796
  • 7
  • 62
  • 118
  • hi ~ @SkyWalker, I use your method, but have same error, has anything else to try ? – wawa Aug 16 '16 at 01:51
  • @wawa Please check my another answer: http://stackoverflow.com/a/36266696/2293534 and have a look this tutorial: https://developers.google.com/identity/protocols/OAuth2#basicsteps. Hope it will clarify your concept. – SkyWalker Aug 16 '16 at 02:11
  • hi~ @SkyWalker, check your two answer carefully, but I still get error:"error": { "errors": [ { "domain": "global", "reason": "invalid", "message": "Invalid Value" } ], "code": 400, "message": "Invalid Value" } what should I do? – wawa Aug 17 '16 at 04:04
  • @wawa I think it it token expiration issue. So, I have some questions `1. Have you revoked your token yet? 2. Have your token not used more than 6 months? 3. Have you changed your password? 4. How much time you have token request? If it already over 25 times, creating a new token automatically invalidates the oldest token without warning.` Another way: If possible, for testing purpose only, you can create a new token with new mail and check that if it is working properly or not. You can read Token Expiration:https://developers.google.com/identity/protocols/OAuth2#expiration – SkyWalker Aug 17 '16 at 08:53
  • hi~ @SkyWalker thank you for your reply.I revoke my refresh_token and create new refresh_token ,then create new access_token, and then request purchase info, also return "400 Invalid Value".I haven't changed my password and hasn't over 25 times also. – wawa Aug 17 '16 at 10:50
  • Would you please check with new mail id for confirmation...that it works fine.Check it from here https://support.google.com/a/#topic=29157 @wawa – SkyWalker Aug 17 '16 at 10:54
  • @wawa Checking Procedure --**without refresh token:-** `1. send API request with access token 2. if access token is invalid, fail and ask user to re-authenticate` **with refresh token:-** `1. send API request with access token 2. If access token is invalid, try to update it using refresh token 3. if refresh request passes, update the access token and re-send the initial API request 4. If refresh request fails, ask user to re-authenticate` For more: http://stackoverflow.com/a/12885823/2293534 – SkyWalker Aug 17 '16 at 11:04
  • hi~ @SkyWalker, my order's itemType is subs, Is it possible this order have no Purchases.products value? I can get the order's Purchases.subscriptions value. – wawa Aug 20 '16 at 02:49
2

This happened to me when I was testing with Static Responses, i.e. using reserved product IDs for testing (like android.test.purchased). SkyWalker's solution did not help in this case.

Then I used real product IDs, published my app as alpha to google play and side-loaded the release apk into my device and now everything works as expected.

Be sure to read carefully chapter Setting Up for Test Purchases in google docs to prepare your app and account properly for testing.

David Riha
  • 1,210
  • 12
  • 22
0

Check out this to see API request and response. Need help with the API Explorer

API: https://www.googleapis.com/androidpublisher/v1.1/applications/packageName/subscriptions/subscriptionId/purchases/token

Request parameters:

packageName:PACKAGE_NAME

subscriptionId:SUBSCRIPTION_ID

token:PURCHASE_TOKEN

Community
  • 1
  • 1
Prashant Arvind
  • 1,281
  • 13
  • 20