Получаю ли я правильные шаги для проверки пользовательской подписки на Android в приложении?
Я делаю приложение, которое не требует учетной записи пользователя / логина и позволяет пользователю приобрести подписку. Я хочу использовать API разработчика Google Play, чтобы проверить, есть ли у пользователя купленная / активная подписка. Из всей документации я собрал следующие шаги.
Правильны ли они, и не могли бы вы ответить на два вопроса в них?
- Создайте учетную запись службы в консоли API Google.
- Сохраните секретный ключ, который мне дан (где? Конечно, нет в моем коде / на устройстве, как предлагает этот пример кода)
- Используйте клиентскую библиотеку API Google для Java, чтобы создать и подписать JWT с закрытым ключом (как? Документы дают мне это, но это не код Java... Что мне с ним делать?)
- Построить запрос токена доступа и получить доступ к API
- Приложение теперь может отправить запрос GET в API, чтобы узнать, есть ли у пользователя подписка
- Когда срок действия маркера доступа истечет, вернитесь к шагу 3.
Кроме того, у меня есть веб-сервис, хотя я ничего не знаю о веб-сервисах или программировании веб-сервисов... Я знаю только достаточно, чтобы осознавать, что, возможно, это необходимо использовать здесь.
РЕДАКТИРОВАТЬ: Эти шаги не были правильными. Смотрите мой ответ ниже для правильных шагов. Тем не менее, обратите внимание, что это относится только к использованию учетной записи службы (потому что я не хотел требовать от пользователя явного разрешения доступа к API)
7 ответов
Как оказалось, мои шаги не были правильными. Мне потребовались недели, чтобы понять это, и, кажется, это нигде не документировано. Пожалуйста:
Создайте учетную запись веб-приложения в консоли API Google. Поместите любой веб-сайт в качестве "URI перенаправления"; это не имеет значения, так как вы не будете его использовать. Вы получите идентификатор клиента и секрет клиента при создании учетной записи.
В браузере на вашем компьютере перейдите на
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=[YOUR REDIRECT URI]&client_id=[YOUR CLIENT ID]
и разрешить доступ при появлении запроса.Посмотри в адресной строке. В конце URI, который вы ввели изначально, будет ваш токен обновления. Это выглядит как
1/....
Вам понадобится этот "код" на следующем шаге. Токен обновления никогда не истекает.Преобразуйте этот "код" в "токен обновления", перейдя в
https://accounts.google.com/o/oauth2/token?client_id=[YOUR CLIENT ID]&client_secret=[YOUR CLIENT SECRET]&code=[CODE FROM PREVIOUS STEP]&grant_type=authorization_code&redirect_uri=[YOUR REDIRECT URI]
, Вы можете сохранить полученное значение прямо в вашей программе; он никогда не истекает, если явно не отменен. (этот шаг вставлен @BrianWhite - смотрите комментарии) Убедитесь, что вы используете POST.(вставлено Gintas)В своем коде отправьте запрос HttpPost на
https://accounts.google.com/o/oauth2/token
с BasicNameValuePairs"grant_type","refresh_token"
,"client_id",[YOUR CLIENT ID]
,"client_secret",[YOUR CLIENT SECRET]
,"refresh_token",[YOUR REFRESH TOKEN]
, Для примера посмотрите здесь. Вам нужно будет сделать это в отдельном потоке, возможно, используя AsyncTask. Это вернет JSONObject.Получите токен доступа из возвращенного JSONObject. Для примера посмотрите здесь. Вам нужно будет получить строку "access_token". Срок действия токена истекает через 1 час.
В своем коде отправьте запрос HttpGet на
https://www.googleapis.com/androidpublisher/v1/applications/[YOUR APP'S PACKAGE NAME]/subscriptions/[THE ID OF YOUR PUBLISHED SUBSCRIPTION FROM YOUR ANDROID DEVELOPER CONSOLE]/purchases/[THE PURCHASE TOKEN THE USER RECEIVES UPON PURCHASING THE SUBSCRIPTION]?accesstoken="[THE ACCESS TOKEN FROM STEP 4]"
, Для примера посмотрите здесь.
Пользователи.NET: Надеюсь, этот ответ спасет кого-то от горя.
Как отметил @Christophe Fondacci в 2015 году, принятое решение отлично сработало несколько лет назад. Сейчас 2017 год, и процесс намного проще и быстрее.
Мой вариант использования заключается в проверке подписок в приложении, когда мое мобильное приложение отправляет информацию о покупке подписки на мой сервер RESTful, который, в свою очередь, связывается с Google, чтобы подтвердить покупку подписки.
Стратегия заключается в создании Сервисной учетной записи, которая будет действовать от вашего имени.
- Войдите в консоль разработчика Google Play и выберите приложение, которое вы настраиваете.
- Посетите Настройки -> Доступ к API
- В разделе " Учетные записи служб" нажмите кнопку " Создать учетную запись службы".
По состоянию на январь 2017 года появится диалоговое окно с инструкциями по настройке учетной записи службы. Диалог перенесет вас в консоль Google API; оттуда,
A) Нажмите Создать сервисную учетную запись.
Б) Создайте имя учетной записи службы, которая имеет смысл. Поскольку мы заинтересованы в доступе к Службам Android Publisher, я выбрал "издатель".
C) Для роли просто выберите что-то - вы можете изменить это позже.
D) Выберите "Создать новый закрытый ключ" и выберите P12 для реализации.Net. Не потеряйте этот файл!
5) Теперь вы закончили с #4, вы увидите свою новую учетную запись службы в списке; нажмите "Предоставить доступ", чтобы включить его.
6) Нажмите на ссылку "Просмотр разрешений". Вы должны изменить разрешения в зависимости от ваших потребностей и API.
Чтобы проверить покупки в приложении, перейдите в раздел Cog->Change Permissions и включите GLOBAL разрешения "Visiblity" и "Manage Orders".
Хорошо, на данный момент вы настроили все на стороне Google. Теперь для настройки вашего сервера на серверные вещи. Я рекомендую создать консольное приложение.Net, чтобы протестировать вашу реализацию, а затем разгрузить ее при необходимости.
1) Добавьте клиентскую библиотеку Android Publisher из Nuget
PM> Install-Package Google.Apis.AndroidPublisher.v2
2) Добавьте файл P12 в корневой каталог вашего проекта.
3) Измените свойства P12, чтобы "Build Action" было "Content", а "Copy To Output Directory" - "Copy if newer".
4) Реализуйте что-то вроде этого, чтобы проверить ваш доступ и точную настройку.
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;
using Google.Apis.AndroidPublisher.v2;
...
public Task<SubscriptionPurchase> GetSubscriptionPurchase(string packageName, string productId, string purchaseToken)
{
var certificate = new X509Certificate2(
"{{your p12 file name}}",
"{{ your p12 secret }}",
X509KeyStorageFlags.Exportable
);
var credentials = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("{{ your service account email }}")
{
Scopes = new[] { AndroidPublisherService.Scope.Androidpublisher }
}.FromCertificate(certificate))
;
var service = new AndroidPublisherService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "my server app name",
});
return service.Purchases.Subscriptions.Get(packageName, productId, purchaseToken).ExecuteAsync();
}
Удачи, надеюсь, это кому-нибудь поможет.
Источники:
Использование OAuth 2.0 для серверных приложений
Клиентская библиотека.Net для Google.Apis.AndroidPublisher.v2
Если вы похожи на меня и хотите сделать это на PHP, вот процедура, как это сделать... Благодаря ответу Калины мне понадобилось всего три дня, чтобы понять, как это работает:).
Вот оно:
перейдите в консоль разработчиков Google https://console.developers.google.com/ и создайте веб-приложение. Поместите developers.google.com/oauthplayground'а в качестве "URI перенаправления"; Вы будете использовать его на шаге 2. При создании учетной записи вы получите идентификатор клиента и секрет клиента. Убедитесь, что у вас есть Google Play Android Developer API.
перейдите на игровую площадку Google oauth2 https://developers.google.com/oauthplayground/. Этот замечательный инструмент - ваш лучший друг на ближайшие несколько дней. Теперь перейдите к настройкам: убедитесь, что установлены собственные учетные данные OAuth. Только тогда вы сможете заполнить свой идентификатор клиента и его секрет в форме ниже.
На игровой площадке Google oauth2 перейдите к шагу 1. Выберите и авторизуйте API, заполните область в поле ввода https://www.googleapis.com/auth/androidpublisher. Я не смог найти API Google Play для разработчиков в списке, возможно, они добавятся через некоторое время. Хит АВТОРИЗАЦИЯ АПИС. Выполните авторизацию следующим образом.
В игровой площадке Google oauth2 перейдите к шагу 2 Обмен кода авторизации для токенов. Если все прошло хорошо, вы увидите код авторизации, начинающийся с /4. Если что-то пошло не так, проверьте сообщение об ошибке справа. Теперь вы нажали "Обновить токен доступа". Скопируйте токен Обновить... он начнется с /1...
Теперь вы всегда можете получить токен доступа! вот как:
$url ="https://accounts.google.com/o/oauth2/token"; $fields = array( "client_id"=>"{your client id}", "client_secret"=>"{your client secret}", "refresh_token"=>"{your refresh token 1/.....}", "grant_type"=>"refresh_token" ); $ch = curl_init($url); //set the url, number of POST vars, POST data curl_setopt($ch, CURLOPT_POST,count($fields)); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //execute post $lResponse_json = curl_exec($ch); //close connection curl_close($ch);
Теперь у вас есть знак доступа, ура... JSON будет выглядеть так:
"access_token" : "{the access token}", "token_type" : "Bearer", "expires_in" : 3600
Наконец, вы готовы спросить что-нибудь в Google! Вот как это сделать:
$lAccessToken = "{The access token you got in}" ;
$lPackageNameStr = "{your apps package name com.something.something}";
$lURLStr = "https://www.googleapis.com/androidpublisher/v1.1/applications/$lPackageNameStr/subscriptions/$pProductIdStr/purchases/$pReceiptStr";
$curl = curl_init($lURLStr);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$curlheader[0] = "Authorization: Bearer " . $lAccessToken;
curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheader);
$json_response = curl_exec($curl);
curl_close($curl);
$responseObj = json_decode($json_response,true);
Возвращаемый JSON будет содержать две метки времени: initiationTimestampMsec и validUntilTimestampMsec - время действия подписки. Оба числа миллисекунд, чтобы добавить к дате 01.01.1970!
Я не знаю, в 2012 году, но в 2015 году вы не должны делать какие-либо из этих шагов вручную. Мне было очень трудно найти документацию, поэтому я публикую здесь на случай, если она кому-нибудь поможет.
- Из соображений безопасности вы должны запрашивать только внутриигровые покупки со своего сервера, так как в противном случае вы не можете доверять ни одному из двух концов процесса покупки.
Теперь на стороне сервера (я думаю, что вы все еще можете использовать тот же код из своего приложения, если вам абсолютно необходимо), включите google-api-services-androidpublisher
клиентская библиотека для вашего проекта (см. https://developers.google.com/api-client-library/java/apis/androidpublisher/v1)
Как вы упомянули, вам нужна служебная учетная запись с файлом P12 (клиентская библиотека принимает только файл P12).
Затем следующий код аутентифицируется и получает информацию о покупке:
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = new JacksonFactory();
List<String> scopes = new ArrayList<String>();
scopes.add(AndroidPublisherScopes.ANDROIDPUBLISHER);
Credential credential = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
.setServiceAccountId(googleServiceAccountId)
.setServiceAccountPrivateKeyFromP12File(new File(googleServicePrivateKeyPath))
.setServiceAccountScopes(scopes).build();
AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential).build();
AndroidPublisher.Purchases purchases = publisher.purchases();
final Get request = purchases.get(packageName, productId, token);
final SubscriptionPurchase purchase = request.execute();
// Do whatever you want with the purchase bean
Информацию об аутентификации клиента Java можно найти здесь: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Я могу неправильно понять ваш вопрос, но я не вижу причин, по которым вы должны использовать ссылки, на которые вы ссылаетесь, чтобы заставить In-App Billing для приложения Android работать. Эта страница намного полезнее:
http://developer.android.com/guide/google/play/billing/index.html
Вы можете попробовать демонстрационное приложение, которое они включают (Dungeons - http://developer.android.com/guide/google/play/billing/billing_integrate.html). При этом используются продукты (разовые покупки), а не подписки, но вы должны иметь возможность вносить изменения, чтобы проверить, что вы хотите.
Я думаю, что ключом для вас будет метод restoreTransactions, который они предоставляют в образце, чтобы увидеть, есть ли в аккаунте Google Play какие-либо подписки для вашего приложения:
@Override
public void onRestoreTransactionsResponse(RestoreTransactions request, int responseCode) {
if (responseCode == BillingVars.OK) {
// Update the shared preferences so that we don't perform a RestoreTransactions again.
// This is also where you could save any existing subscriptions/purchases the user may have.
SharedPreferences prefs = getSharedPreferences(my_prefs_file, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(DB_INITIALIZED, true);
edit.commit();
} else {
Log.e(TAG, "RestoreTransactions error: " + responseCode);
}
}
Если у кого-то возникнут проблемы с последним этапом принятых сообщений (#7), я нашел ?access_token=
работать вместо ?accessToken=
Слишком плохое переполнение стека не позволит мне сделать этот комментарий непосредственно в ветке...
Поскольку у вас есть веб-служба, которую может вызывать ваше приложение, я бы рекомендовал надежно хранить ваш закрытый ключ на вашем сервере. Вам следует обратить внимание на то, чтобы как можно больше перемещать содержимое приложения в сервисные вызовы, см. Эту ссылку. Я реализовал подписку в приложении, но это было до того, как эта часть API вышла из строя. Я должен был сделать свою собственную регистрацию и проверку безопасности, но, похоже, этот API делает большую часть этого за вас, используя OAuth, хотя похоже, что вы по-прежнему отвечаете за хранение запроса / подтверждения подписки.
Там, где говорится о подписании ваших JWT с существующей библиотекой, они, по-видимому, предоставляют вам ссылки на библиотеку Java, библиотеку Python и библиотеку PHP - это зависит от того, на чем написан ваш веб-сервис или серверный компонент (мой - C#, поэтому я использую RSACryptoServiceProvider) для проверки подписанных покупок. Они используют объекты JSON для фактической передачи данных.