Інтеграція внутрішньоігрових покупок (In-app purchases)
Unity IAP на перший виглядня виглядає просто: підключив пакет, зареєстрував продукти, викликав BuyProductID(). На практиці — перший же InitializationFailureReason.PurchasingUnavailable у продакшені на iOS 17 змушує провести кілька годин у документації StoreKit 2 та розуміти, що невірно настроєна entitlement для In-App Purchase у App Store Connect.
IAP-інтеграція — це не «додати SDK». Це зв'язка між клієнтом, платіжною системою магазину та бекендом, яка повинна працювати коректно в умовах нестабільного інтернету, переривистих транзакцій та спроб шахрайства.
Де чаще всього падає
Pending-транзакції. Користувач натиснув «Купити», гроші списалися, з'єднання оборвалось — ProcessPurchase не викликався. Unity IAP зберігає транзакцію в чергу та при наступному запуску пробує її завершити. Але якщо бекенд не реалізує idempotency по transactionID, гравець отримає товар двічі чи взагалі не отримає. Бачив проекти, де PendingOrderResponse накопичувався тижнями через відсутність ConfirmPendingPurchase() у потрібному місці.
Receipt validation. Без серверної валідації квитанцій гра уразлива до фродових покупок через модифіковані APK або jailbroken-пристрої. Apple повертає base64-encoded receipt у Product.receipt, Google — JSON з підписю. Локальна перевірка через UnityEngine.Purchasing.Security.CrossPlatformValidator — мінімальний бар'єр, але не достатній. Повноцінна валідація: надсилання квитанції на свій сервер, перевірка через Apple App Store Server API (/verifyReceipt або новий StoreKit 2 JWS-токен) або Google Play Developer API (purchases.products.get).
Restore Purchases на iOS. Apple вимагає кнопку відновлення покупок для non-consumable та subscriptions — без неї додаток не пройде ревью. IAppleExtensions.RestoreTransactions() повинен бути доступний з UI, а обробник OnTransactionsRestored — коректно оновлювати стан інвентарю без дублів.
Окрема біль — підписки. SubscriptionManager у Unity IAP умеет парсити дату истечения та статус renewal, але тільки при наявності валідного receipt. На Android з Google Play Billing Library 5+ потрібно явно запитувати queryPurchasesAsync при кожному старті — кеш застаріває.
Що робимо при інтеграції
Починаємо з аудиту: є ли бекенд, потрібна ли серверна валідація, яка модель монетизації (consumable, non-consumable, subscriptions, або все разом). Під це проектуємо схему.
Настроюємо конфіґурацію продуктів у Unity IAP через ProductCatalog або програмно через ConfigurationBuilder. Для мультиплатформенних ігор — єдиний каталог з платформо-специфічними ID (Apple/Google часто вимагають різні ідентифікатори).
Реалізуємо повний цикл: інеціалізація UnityPurchasing.Initialize() → обробка ProcessPurchase → підтвердження ConfirmPendingPurchase() → видача товара → запис у базу. Якщо є бекенд — додаємо серверну валідацію з retry-логікою при таймаутах.
Для iOS додатково: настройка StoreKit окружения для тестування (Xcode Sandbox), обробка промо-офферів через IAppleExtensions.SetStorePromotionOrder(), коректна робота з Family Sharing якщо потрібно.
Для Android: настройка тестових аккаунтів у Google Play Console, перевірка роботи у alpha/internal треку до публікації.
Тестування — окремий етап
Сценарії, які перевіряємо обов'язково: успішна покупка, покупка при відключенні мережі в момент транзакції, дублюючий запит покупки, відновлення покупок, покупка на пристрої без платіжного методу, апгрейд/даунгрейд підписки.
Sandbox-тестування на iOS має обмеження — деякі сценарії (наприклад, billing retry) відтворюємі тільки у TestFlight. На Android — через internal testing track з ліцензійними тест-аккаунтами.
Строки
| Складність | Строк |
|---|---|
| Consumable IAP, одна платформа, без бекенда | 2–4 дні |
| Повна інтеграція (дві платформи + серверна валідація) | 1–2 тижні |
| Subscriptions з управлінням на бекенді + аналітика | 2–4 тижні |
Вартість розраховується після аналізу архітектури проекту та вимог до монетизаційної моделі.





