Встраивание в ваше приложение
Подписки Google Play
Для Android-приложений, которые продают подписки через Google Play и не имеют собственного бэкенда. Приложение отправляет токен покупки на шлюз вместе с API-ключом; шлюз обращается к Google от имени вашего сервисного аккаунта и сообщает, активна ли подписка. Проверка бесплатна.
#Перед началом
- Вход в Play Console с правами владельца аккаунта или администратора, чтобы можно было приглашать пользователей.
- Аккаунт Google, который может создать проект в Google Cloud console (это может любой аккаунт Google).
- Имя пакета вашего приложения (applicationId), например com.example.app, и хотя бы одна подписка, созданная в Play Console.
- Около 15 минут на настройку, затем до суток, пока Google включит права.
#1. Получите JSON сервисного аккаунта
JSON-файл — это ключ сервисного аккаунта. С ним шлюз может запрашивать у Google данные о покупках в вашем приложении — и ничего больше. Google называет его ключом сервисного аккаунта (service account key); это не файл OAuth-клиента (client_secret_….json).
- 1
Откройте Google Cloud console и выберите проект
console.cloud.google.comВоспользуйтесь выбором проекта вверху. Подойдёт любой проект, но отдельный проект только для проверок Play поможет поддерживать порядок.
- 2
Включите Play API
APIs & Services → Library → Google Play Android Developer API → EnableИщите точно по названию. Если на кнопке написано Manage, API уже включён.
- 3
Создайте сервисный аккаунт
IAM & Admin → Service accounts → Create service accountНазовите его как угодно, например play-verify. Пропустите необязательные шаги с выдачей ролей в проекте или доступа пользователям: роли Cloud ему не нужны. Нажмите Done.
- 4
Скачайте его JSON-ключ
Откройте сервисный аккаунт → Keys → Add key → Create new key → JSON → CreateБраузер скачает файл .json. Храните его надёжно: Google больше его не покажет, а любой, у кого он есть, сможет читать данные о покупках в вашем приложении.
- 5
Скопируйте email сервисного аккаунта
Он выглядит как play-verify@your-project.iam.gserviceaccount.com и указан на странице сервисного аккаунта, а в файле — в поле client_email. Он понадобится на следующем шаге.
Кнопка Create new key неактивна или выдаёт ошибку?
#2. Выдайте доступ в Play Console
- 1
Пригласите сервисный аккаунт
play.google.com/console → Users and permissions → Invite new usersВставьте email сервисного аккаунта в поле адреса. Сервисному аккаунту не нужно принимать приглашение.
- 2
Выдайте эти два права
App permissions → Add app → выберите своё приложениеОтметьте View financial data, orders, and cancellation survey responses и Manage orders and subscriptions. Остальное не включайте. Если отметить эти же два пункта в Account permissions, доступ будет ко всем вашим приложениям.
- 3
Отправьте приглашение
Apply → Invite userТеперь сервисный аккаунт отображается в Users and permissions со статусом Active.
Зачем эти права
Google может включать права до суток
#3. Привяжите его к API-ключу
- 1
Откройте ключ, который использует приложение
Консоль → API-ключи → ИзменитьИспользуйте отдельный ключ для приложения (см. ниже).
- 2
Заполните «Проверка подписок Google Play»
Настройка проверки Google PlayВведите имя пакета точно как в build.gradle приложения (applicationId), выберите JSON-файл из шага 1 и сохраните.
- 3
Прочитайте сообщение после сохранения
Перед сохранением шлюз проверяет файл в Google. Если Google всё ещё отказывает, в окне будет указано, чего не хватает, — обычно API из шага 1 или прав из шага 2. Файл хранится в зашифрованном виде и больше не показывается; чтобы заменить его, загрузите файл заново.
Отдельный ключ для приложения
#4. Проверка из приложения
Проверяйте сразу после покупки и снова при запуске приложения с сохранённым токеном. Не задерживайте запуск приложения ради ответа: показывайте последний сохранённый результат и обновляйте его, когда придёт ответ. Для одного и того же токена ответ берётся из кеша на одну минуту. По умолчанию каждый ключ может выполнять 120 проверок в минуту; текущий лимит указан в настройках Google Play ключа, и поддержка может повысить его для отдельного ключа. Вызовы сверх лимита получают 429 и не логируются, поэтому храните последний сохранённый ответ.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:in_app_purchase/in_app_purchase.dart';
const apiKey = 'sk-YOUR_KEY';
Future<Map<String, dynamic>?> checkSubscription(String purchaseToken) async {
final res = await http.post(
Uri.parse('https://www.aisplice.store/v1/google-play/verify'),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
// Request signing on for this key? Then this call must be signed too:
// ...signedHeaders('POST', '/v1/google-play/verify'),
},
body: jsonEncode({'purchase_token': purchaseToken}),
);
if (res.statusCode != 200) return null; // keep the last saved answer
return jsonDecode(res.body) as Map<String, dynamic>;
}
// In your purchaseStream listener:
Future<void> onPurchase(PurchaseDetails purchase) async {
if (purchase.status != PurchaseStatus.purchased &&
purchase.status != PurchaseStatus.restored) return;
final token = purchase.verificationData.serverVerificationData;
final status = await checkSubscription(token);
if (status != null && status['active'] == true) {
await saveEntitlement(token, status); // your local storage
unlockPaidFeatures();
}
if (purchase.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchase); // acknowledges on Android
}
}#5. Решите, что открывать
| Ответ проверки | Что делает приложение | Типичная причина |
|---|---|---|
| active: true, in_trial: true | Открыть доступ. Показать «Пробный период заканчивается» с expiry_time. | Пробный период |
| active: true | Открыть доступ до expiry_time. | Оплачена, включая отменённые подписки, срок которых ещё не истёк |
| subscribed: true, active: false | Закрыть доступ и показать экран продления. | Истекла, приостановлена, на удержании |
| subscribed: false | Закрыть доступ и показать экран оформления подписки. | Неизвестный токен, ожидается оплата |
| HTTP 502 / 503 | Использовать последний сохранённый ответ и повторить позже. | Google или сервисный аккаунт не отвечает |
Сохраняйте expiry_time вместе с ответом. Между проверками считайте подписку действующей до этого времени, чтобы приложение работало и без сети.
#6. Подтверждайте каждую покупку
Неподтверждённые покупки возвращаются через 3 дня
#7. Защитите ключ в приложении
Ключ внутри приложения можно извлечь из перехваченного трафика. Включите для него подпись запросов, чтобы скопированный ключ нельзя было использовать сам по себе, обфусцируйте сборку и задайте для ключа предел квоты.
Подпись распространяется на все вызовы с этим ключом
#8. Посмотрите, что произошло
Каждая проверка, включая ответы из кеша и ошибки, отображается в разделе Консоль → Журнал запросов как бесплатная «Проверка Google Play» у этого ключа. Ищите по полному токену покупки, его последним символам или ID заказа (GPA.…), который пользователь пришлёт вам из письма-чека Google.
- google_play_api_disabled: Play API из шага 1 не включён в проекте, к которому относится JSON-файл.
- google_play_permission_denied: приглашения из шага 2 нет, в нём нет двух нужных прав, оно для другого приложения или ещё не вступило в силу.
- google_play_credentials_invalid: JSON-ключ удалён или отключён в Google Cloud. Создайте новый ключ и загрузите его в настройках API-ключа.
- При сохранении сообщается, что файл не является ключом сервисного аккаунта: вы выбрали файл OAuth-клиента. Скачайте ключ на вкладке Keys сервисного аккаунта.
- Каждая проверка сообщает, что покупка не найдена: имя пакета в ключе отличается от applicationId приложения или токен получен из другого приложения.
- Для тестовых покупок лицензированных тестировщиков возвращается test_purchase: true.
Проблема не решена?
Пришлите нам ID запроса или текст ошибки, и мы разберёмся.