- PVSM.RU - https://www.pvsm.ru -
In-app Billing Subscriptions (подписки) позволяют автоматизировать списание средств со счета пользователя для приложений, разработанных под Android. Данный инструмент — большой помощник в задаче повышения монетизации приложений. В общем виде схема работы с подписками [1] выглядит следующим образом:
В статье представлены шаги по обеспечению серверной поддержки инструмента монетизации для In-App Billing version 2.
Для получения данных по подпискам со стороны сервера, Google предлагает использовать Google Play Android Developer API [2]. API предоставляет всего два метода:
Если попробовать получить данные по некоторой подписке myapp.month.test приложения com.myapp (кстати, все совпадения представленных ниже токенов, продуктов, идентификаторов и ключей случайны. Для повторения примеров прошу использовать данные своего аккаунта), результат будет следующим:
$ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription'
--2013-02-11 18:24:01-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription
Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f
Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected.
HTTP request sent, awaiting response... 401 Unauthorized
Authorization failed.
Для успешного вызова методов работы с подписками необходим токен доступа к API. Последний можно получить, выполнив авторизацию [5] по протоколу OAuth2 с использованием секретных данных консоли [6]. Идентифицироваться при авторизации можно различными способами. Важно, что для получения токена доступа к методам взаимодействия с подписками, нужно представиться веб-приложением [7], используя данные раздела Client ID for web applications:

Авторизация веб-приложением осуществляется в следующей последовательности
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=http://example.com/oauth2callback&client_id=someclientid.apps.googleusercontent.com
http://example.com/oauth2callback?code=4/AuthoRIZ4ti0nC0De
$ wget https://accounts.google.com/o/oauth2/token --post-data 'code=4/AuthoRIZ4ti0nC0De&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&redirect_uri=http://example.com/oauth2callback&grant_type=authorization_code' -O -
--2013-02-11 18:31:20-- https://accounts.google.com/o/oauth2/token
Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54
Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: `STDOUT'
[<=> ] 0 --.-K/s {
"access_token" : "ya29.ACCeSs-T0keN",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/rEfRE5hT0KeN"
[ <=> ] 127 --.-K/s in 0s
2013-02-11 18:31:20 (16.6 MB/s) - written to stdout [127]
$ wget https://accounts.google.com/o/oauth2/token --post-data 'code=4/0thER-AuthoRIZ4ti0nC0De&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&redirect_uri=http://example.com/oauth2callback&grant_type=authorization_code' -O -
--2013-02-11 18:31:20-- https://accounts.google.com/o/oauth2/token
Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54
Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: `STDOUT'
[<=> ] 0 --.-K/s {
"access_token" : "ya29.neW-ACCeSs-T0keN",
"token_type" : "Bearer",
"expires_in" : 3573,
[ <=> ] 127 --.-K/s in 0s
2013-02-11 18:31:20 (16.6 MB/s) - written to stdout [127]
В результате Google предоставляет новый токен доступа, но без токена обновления. Все полученные таким образом токены являются валидными для выполнения запросов, но обновить их будет невозможно. Чтобы как можно реже выполнять процедуру, описанную выше, и хоть как-то автоматизировать процесс проверки подписок рекомендую предпринять следующие действия:
$ wget 'https://accounts.google.com/o/oauth2/token' --post-data 'refresh_token=1/rEfRE5hT0KeN&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&grant_type=refresh_token' -O -
--2013-02-11 19:33:13-- https://accounts.google.com/o/oauth2/token
Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54
Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: `STDOUT'
[<=> ] 0 --.-K/s {
"access_token" : "ya29.rEFre5hED-ACCeSs-T0keN",
"token_type" : "Bearer",
"expires_in" : 3600
[ <=> ] 127 --.-K/s in 0s
2013-01-11 19:33:14 (18.7 MB/s) - written to stdout [127]
Обеспечив себе получение свежих токенов при необходимости, можно смело проверять-отменять приобретенные платящими пользвоателями подписки. Пример получения данных о подписке:
$ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN' -O -
--2013-02-11 19:50:21-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN
Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f
Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: `STDOUT'
[<=> ] 0 --.-K/s {
"kind": "androidpublisher#subscriptionPurchase",
"initiationTimestampMsec": "1357909784285",
"validUntilTimestampMsec": "1360588184285",
"autoRenewing": true
}
[ <=> ] 167 --.-K/s in 0s
2013-02-11 19:50:21 (30.7 MB/s) - written to stdout [167]
Описание полей можно посомтреть в документации к запросу [8]
Следует отметить расхождение значений initiationTimestampMsec и validUntilTimestampMsec:
Полагаю, Google резервирует дополнительно 6 часов на списание средств со счета клиента. В случае возникновения проблем при списании, периодически пытается повторить. В конкретно этом случае по данным паблишмента деньги были списаны у клиента ~ в 2013-02-11 17:10. Кстати, подписки Facebook пытаются списать средства вплоть до 4 суток после завершения платежного периода. А вот iTunes начинает списывать деньги за сутки до окончания платежного периода так, чтобы к моменту завершения подписки было точно известно, продлена она или нет.
Если с данными запроса что-то не так, Google отвечает 400-м кодом HTTP. В некоторых случаях при некорректном запроса возникает HTTP 404.
$ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/not.myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN' -O -
--2013-02-11 19:58:24-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/not.myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN
Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f
Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected.
HTTP request sent, awaiting response... 400 Bad Request
2013-02-11 19:58:25 ERROR 400: Bad Request.
Пример отмены подписки:
$ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription/cancel?access_token=ya29.rEFre5hED-ACCeSs-T0keN' --post-data '' -O -
--2013-02-11 20:01:16-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription/cancel?access_token=ya29.rEFre5hED-ACCeSs-T0keN
Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f
Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected.
HTTP request sent, awaiting response... 204 No Content
Обратите внимание на код ответа HTTP 204, а пустое тело ответа свидетельсвут об успешной отмене подписки. Если повторить запрос получения данных по подписке, можно в этом убедиться
$ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN' -O -
--2013-02-11 20:08:47-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN
Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f
Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/json]
Saving to: `STDOUT'
[<=> ] 0 --.-K/s {
"kind": "androidpublisher#subscriptionPurchase",
"initiationTimestampMsec": "1357909784285",
"validUntilTimestampMsec": "1360588184285",
"autoRenewing": false
}
[ <=> ] 167 --.-K/s in 0s
2013-02-11 20:08:49 (23.5 MB/s) - written to stdout [167]
Обратите внимание, дата окончания подписки не изменилась, но автопролонгация оказалась выключенной.
Для интеграции подписок в приложение, предлагаю следующую архитектуру системы серверной поддержки:

Описание:
Если кто-нибудь соберется реализовывать подписки, предлагаю две библиотеки:
Автор: alxmsl
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/26946
Ссылки в тексте:
[1] схема работы с подписками: http://developer.android.com/google/play/billing/v2/billing_subscriptions.html
[2] Google Play Android Developer API: https://developers.google.com/android-publisher/index
[3] Получение данных: https://developers.google.com/android-publisher/v1/purchases/get
[4] Отмена: https://developers.google.com/android-publisher/v1/purchases/cancel
[5] авторизацию: https://developers.google.com/accounts/docs/OAuth2
[6] консоли: https://code.google.com/apis/console/
[7] представиться веб-приложением: https://developers.google.com/accounts/docs/OAuth2WebServer
[8] документации к запросу: https://developers.google.com/android-publisher/v1/purchases#resource
[9] попроще: https://github.com/alxmsl/GoogleClient
[10] круче: https://code.google.com/p/google-api-php-client/
[11] Источник: http://habrahabr.ru/post/160911/
Нажмите здесь для печати.