Реализация In-App Purchases для мобильной игры
IAP в мобильной игре — это не просто «добавить кнопку купить». Apple и Google предъявляют требования к верификации чеков, архитектуре серверной части и обработке восстановления покупок. Пропуск любого из этих пунктов — либо потеря денег, либо отклонение приложения.
StoreKit 2 vs StoreKit 1
До iOS 15 в ходу был StoreKit 1 с SKPaymentQueue, SKProduct, SKPaymentTransactionObserver. Это legacy: колбэки через делегат, ручное управление транзакциями, verbose код.
StoreKit 2 (Swift concurrency, iOS 15+) — принципиально другой API: async/await, Product.products(for:), product.purchase(), Transaction.updates AsyncSequence для отслеживания транзакций. Код чище в два раза.
Если целевая аудитория игры — iOS 14 и ниже, пишем на StoreKit 1 или используем кроссплатформенную абстракцию. Если iOS 15+ — StoreKit 2 однозначно.
На Android: BillingClient (Play Billing Library 6+). Ключевые методы: launchBillingFlow(), queryProductDetailsAsync(), acknowledgePurchase(). Важно: Google требует подтверждать (acknowledge) каждую покупку в течение 3 дней, иначе автоматически рефандит. Это не баг — это политика. Не забываем вызывать consumeAsync() для расходуемых товаров (валюта игры, жизни) и acknowledgePurchase() для неконсумируемых (убрать рекламу, premium).
Серверная верификация чеков — обязательна
Клиентская верификация (парсим appStoreReceiptURL прямо в приложении) — небезопасна. Jailbroken устройство + iap-receipt-generator = фейковые чеки. Правильная схема:
- Приложение получает чек/токен от StoreKit/BillingClient.
- Отправляет на свой сервер:
POST /api/purchases/verify. - Сервер верифицирует через Apple
/verifyReceiptendpoint или Google Play Developer API (purchases.products.get/purchases.subscriptions.get). - Сервер зачисляет товар пользователю.
- Возвращает результат клиенту.
Apple устаревает /verifyReceipt в пользу JWS-транзакций StoreKit 2 — на сервере декодируем signedTransactionInfo (JWT), верифицируем подпись Apple Root CA. Библиотеки: appstore-connect-sdk (Node.js), apple-receipt-verifier (Python/Go).
Типы покупок в игре
| Тип | Пример | Consumable | Логика |
|---|---|---|---|
| Игровая валюта | 1000 монет | Да | consume после зачисления |
| Жизни/энергия | +5 жизней | Да | consume, не дублировать |
| Убрать рекламу | Нет рекламы | Нет | acknowledge, restore |
| Сезонный пропуск | Battle Pass | Subscription | проверка expiry |
| Разовый контент | Скин персонажа | Нет | acknowledge, restore |
Для подписок (autoRenewableSubscription) — отдельная логика: renewalInfo, grace period, billing retry state. Подписка истекла, но пользователь ещё в grace period — не лишаем доступа сразу.
Восстановление покупок
StoreKit 2: for await transaction in Transaction.currentEntitlements — возвращает все активные неконсумируемые покупки и подписки. Кнопка «Восстановить покупки» — обязательный элемент App Store Review Guideline 3.1.1. Без неё — отклонят.
На Android: queryPurchasesAsync(QueryPurchasesParams) по INAPP и SUBS — все активные покупки без сети.
Оффлайн и edge-cases
Пользователь купил, сервер недоступен для верификации. Правильная схема: сохраняем pending-транзакцию локально (Core Data / Room), повторяем верификацию при следующем запуске с exponential backoff. Не завершаем транзакцию (finish()) до успешной серверной верификации.
Процесс
Настройка продуктов в App Store Connect и Google Play Console → серверная верификация → клиентский IAP модуль → тестирование в Sandbox/Test environment → проверка restore → QA edge-cases (прерывание оплаты, повторная покупка) → сабмит.
Ориентиры по срокам
Базовая интеграция (consumables + non-consumables, серверная верификация): 3–4 дня. Подписки с grace period, billing retry, receipt migration: +2 дня. Кроссплатформа Flutter/React Native с абстракцией над StoreKit и BillingClient: +1–2 дня.







