API разработчика Google Play - токен покупки запроса возвращает недопустимое значение

Я пытаюсь настроить веб-службу для запроса покупок в Google Play. Мы храним информацию о заказе для клиентов, и эта услуга будет обращаться к API Google Play для запроса деталей подписки.

Каждый раз, когда я пытаюсь запросить покупку, это дает мне ошибку:

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 включил "API разработчика Android для Google Play"
  • Создан oAuth 2.0 client_id и client_secret для веб-приложения типа
  • Записанный как владелец учетной записи, я создал refresh_token
  • В https://play.google.com/apps/publish Я пошел в Настройки → API Access и связал проект с моим приложением

Код мудрый, я использовал refresh_token, чтобы получить 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 с ним и вернуть ответ:

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

Теперь я хочу получить информацию о покупке. У меня есть информация о покупке как таковой:

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

Теперь я хочу получить информацию о покупке. У меня есть информация о покупке как таковой:

{  
"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, похоже, работают для первого вызова, а покупкаToken прямо из информации о заказе. Что дает неверная ошибка значения?

Любая помощь оценивается - не уверен, что еще попробовать. Спасибо!

UPDATE: Я прошел и проверил все имена пакетов и настройки учетной записи Реальная проблема, казалось, была службой, в которую я попадал. Я переключил его на: https://www.googleapis.com/androidpublisher/v2/applications/packageName/purchases/subscriptions/subscriptionId/tokens/purchaseToken

Я также поменялся на использование API-интерфейсов Google, так как он был намного чище, чем вручную создавал запросы.

Спасибо за помощь и ответы

Ответы

Ответ 1

Сначала я хочу поделиться с вами тем, что является 400 плохим запросом, и что такое реальная причина его возникновения?

Ans: Указывает, что запрос был недопустимым. Например, отсутствовал родительский идентификатор или не была использована комбинация требуемых размеров или показателей.

Рекомендуемое действие: Вам нужно внести изменения в запрос API, чтобы он работал.

Ссылка ресурса: Стандартные ответы об ошибках

Ваша проблема:

Ваш код работал правильно и возвращал связанный файл json в качестве вывода. Но через некоторый период it is not working when you want to get information about purchase. Он дает сообщение об ошибке "HTTP/1.1 400 Bad Request"

Корневая причина:

Для обновления токена ответ всегда включает в себя новый токен доступа. Ответ показан ниже:

{
  "access_token":"1/fFBGRNJru1FQd44AzqT3ZgXXXXXX",
  "expires_in":3920,
  "token_type":"Bearer",
}

Таким образом, токен доступа имеет время истечения срока действия. после истечения срока действия маркер доступа не будет работать.

Существует и другое ограничение. Существуют ограничения на количество токенов обновления, которые будут выпущены; один предел для каждой комбинации клиент/пользователь, а другой для каждого пользователя для всех клиентов.

Итак, в вашем случае вы уже преодолели лимит создания токена обновления.

Решение:

Итак, сначала нужно отменить токен. Затем сохраните токены обновления в долгосрочном хранилище и продолжайте использовать их, пока они остаются в силе.

Когда вы используете токен обновления, вам нужно изменить http post request https://accounts.google.com/o/oauth2/token на https://www.googleapis.com/oauth2/v4/token

Итак, ваш код будет выглядеть следующим образом:

String refreshToken = "1/ljll6d9ME3Uc13jMrBweqXugV4g4timYcXXXXXXXXX";
HttpPost request = new HttpPost("https://www.googleapis.com/oauth2/v4/token");
List<NameValuePair> params = new ArrayList<NameValuePair>();
...............
...............

Процедура отзыва:

Есть два способа отмены.

  • Пользователь может отменить доступ, посетив Настройки учетной записи
  • Также приложение может программно отменить доступ, предоставленный ему.

Чтобы программно отменить токен, ваше приложение отправляет запрос на https://accounts.google.com/o/oauth2/revoke и включает токен в качестве параметра:

curl https://accounts.google.com/o/oauth2/revoke?token={token}

token может быть токеном доступа или токеном обновления. Если токен является токеном доступа и имеет соответствующий токен обновления, токен обновления также будет отменен.

N.B: Если аннулирование успешно обработано, тогда код состояния ответа - 200. Для условий ошибки код 400 состояния возвращается вместе с кодом ошибки.

Ссылка на ресурс:

Ответ 2

Это случилось со мной, когда я тестировал Static Responses, т.е. используя зарезервированные идентификаторы продуктов для тестирования (например, android.test.purchased). Решение SkyWalker в этом случае не помогло.

Затем я использовал реальные идентификаторы продуктов, опубликовал свое приложение как альфа для игры в Google и загрузил пакет apk в устройство, и теперь все работает так, как ожидалось.

Обязательно внимательно прочитайте главу "Настройка для тестирования покупок" в документах Google, чтобы правильно подготовить ваше приложение и учетную запись для тестирования.

Ответ 3

Вызовите этот метод для реальных данных, чтобы увидеть запрос и ответ API. Нужна помощь с API Explorer

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

Параметры запроса:

packageName:PACKAGE_NAME

subscriptionId:SUBSCRIPTION_ID

token:PURCHASE_TOKEN