Реалізація Non-Consumable IAP для iOS
Non-consumable IAP — категорія, де кожна помилка в логіці відновлення покупок перетворюється на скаргу в App Store та чарджбек. Користувач купив «безлімітний режим» або «прибрати рекламу», перевстановив додаток — і не отримав своє. Підтримка Apple не допоможе: відновлення non-consumable це відповідальність розробника. Наш досвід п'яти років та більше двадцяти проектів з IAP показує, що коректна реалізація з першого разу економить до 40% часу на баг-фіксах. Згідно з документацією StoreKit, правильне відновлення критично важливе.
Що найчастіше йде не так
Найпоширеніша помилка — викликати SKPaymentQueue.default().restoreCompletedTransactions() тільки по натисканню кнопки «Відновити». Правильно: при кожному запуску перевіряти originalTransaction через SKReceiptRefreshRequest або серверну валідацію. Без цього користувач, який повернувся через півроку з новим iPhone, опиниться без оплаченого контенту.
Другий кейс — неправильна обробка SKPaymentTransactionObserver. Якщо updatedTransactions не викликає finishTransaction(_:) для всіх станів (.purchased, .restored, .failed), транзакція зависає в черзі і при наступному запуску додатку повторно тригерить observer. Бачили проекти, де це призводило до подвійного відкриття paid-контенту після рестарту.
Третя проблема — відсутність серверної валідації. Локальна перевірка receipt через ASN.1 парсинг складна та вразлива: зловмисник може підмінити receipt у файловій системі. Серверна верифікація через ендпоінт Apple /verifyReceipt (або альтернативний) дає гарантію справжності.
Як влаштована правильна реалізація
Архітектура non-consumable IAP будується навколо StoreKit 2 (iOS 15+) або StoreKit 1 з підтримкою iOS 13–14. Порівняємо два підходи:
| Аспект |
StoreKit 2 |
StoreKit 1 (Legacy) |
| Мінімальна версія iOS |
15.0 |
3.0 |
| API стиль |
async/await |
Delegate + completion handlers |
| Відновлення покупок |
Автоматичне через Transaction.currentEntitlements |
Ручне restoreCompletedTransactions() |
| Перевірка підпису |
Вбудована VerificationResult |
Потрібен ReceiptValidator |
| Складність коду |
Низька |
Середня |
StoreKit 2 радикально спрощує код: він в 2 рази скорочує кількість рядків порівняно з StoreKit 1.
// Запит продуктів
let products = try await Product.products(for: ["com.app.premium_unlock"])
// Покупка
let result = try await products.first?.purchase()
switch result {
case .success(let verification):
switch verification {
case .verified(let transaction):
// unlock контент
await transaction.finish()
case .unverified:
// receipt підроблений — не розблоковуємо
break
}
case .pending:
// SCA або батьківський контроль — чекаємо
break
case .userCancelled:
break
}
Transaction.currentEntitlements — async sequence, який при кожному запуску додатку повертає всі активні покупки. Ітеруємо його в @main або в AppDelegate.applicationDidFinishLaunching і відновлюємо стан без кнопки «Відновити».
Для iOS 13–14 залишається StoreKit 1 з SKPaymentTransactionObserver. Там потрібен окремий ReceiptValidator — або локальна верифікація через openssl (складно, але без мережевих запитів), або серверна через Apple /verifyReceipt endpoint (deprecated, але працює). Рекомендуємо серверну: локальна вимагає вбудовування Apple root certificate та коректного ASN.1-парсингу.
Як уникнути проблем з відновленням покупок?
Ключове — робити відновлення автоматичним при кожному запуску, а не тільки по кнопці. В StoreKit 2 це досягається підпискою на Transaction.updates. В StoreKit 1 — викликом restoreCompletedTransactions() та збереженням originalTransaction.transactionIdentifier у UserDefaults або Keychain. Без автоматизації користувачі з новими пристроями залишаться без контенту.
Чому серверна валідація критична?
Для додатків з бекендом: при покупці клієнт відправляє appStoreReceiptURL на сервер, сервер запитує Apple Sandbox/Production та зберігає original_transaction_id в базі. При відновленні на новому пристрої — запит до свого API по apple_id користувача. Це єдиний спосіб гарантувати «покупка на одному пристрої, доступ на іншому» в рамках одного Apple ID. Середня економія від правильної реалізації — до $10,000 на рік на підтримку.
Покроковий план впровадження StoreKit 2
- Налаштуйте продукти в App Store Connect (локалізація, ціни).
- Реалізуйте запит продуктів через
Product.products(for:).
- Додайте обробку покупки з перевіркою
VerificationResult.
- Підпишіться на
Transaction.updates для автоматичного відновлення.
- Інтегруйте серверну верифікацію через
/verifyReceipt.
- Протестуйте в Sandbox на сценарій «покупка → видалення → відновлення».
Типова помилка відновлення
// НЕПРАВИЛЬНО: відновлення тільки по кнопці
func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
// ПРАВИЛЬНО: автоматичне відновлення при старті
Task {
for await result in Transaction.currentEntitlements {
if case .verified(let transaction) = result {
// розблокувати контент
}
}
}
Процес роботи та що входить
Наша реалізація IAP під ключ включає:
- Налаштування продуктів в App Store Connect (створення, локалізація, ціни — конкретні суми розраховуються індивідуально).
- Інтеграцію StoreKit 2 з fallback на StoreKit 1 для максимальної сумісності.
- Серверну верифікацію receipt зі збереженням
original_transaction_id.
- Тестування в Sandbox та на реальних пристроях з чек-листом сценаріїв.
- Документацію по підтримці та доступам.
- Гарантію проходження App Review (дотримання параграфу 3.1.1 App Store Review Guidelines).
Тестування
В Xcode Simulator StoreKit працює через локальний .storekit файл — можна тестувати без реальних продуктів. Для device-тестування потрібен Sandbox Account в App Store Connect. Важливо перевіряти сценарій: покупка → видалення → перевстановлення → відновлення. Цей шлях ламається найчастіше. Отримайте консультацію по вашому проекту — оцінимо ризики та запропонуємо оптимальне рішення.
Терміни реалізації — від 2 до 3 днів: налаштування продуктів в App Store Connect, інтеграція StoreKit 2 з fallback на StoreKit 1, покриття тестами на Sandbox, проходження рев'ю (App Review вимагає кнопку «Restore Purchases» в інтерфейсі). Замовте інтеграцію IAP під ключ — гарантуємо коректну роботу на всіх пристроях та версіях iOS.
Монетизація мобільних додатків: IAP, підписки та рекламна медіація
Додаток з погано реалізованими покупками втрачає гроші не тому що користувачі не хочуть платити, а тому що StoreKit транзакція зависає, Receipt Validation падає з помилкою або restore purchases не працює — і користувач пише в підтримку або залишає 1 зірку. Наш досвід (більше 7 років у мобільній розробці) показує, що грамотна монетизація збільшує LTV на 30–60% вже в перші три місяці після впровадження. Отримайте консультацію з монетизації вашого додатка — проаналізуємо поточну модель і знайдемо точки зростання.
Чому StoreKit 2 — найкращий вибір для IAP?
StoreKit 2 (iOS 15+) — сучасний API з async/await та верифікованими транзакціями на стороні пристрою без сервера. Transaction.currentEntitlements повертає всі активні покупки. Ключова зміна порівняно з StoreKit 1: верифікація JWS-підпису на пристрої через VerificationResult<Transaction> — не потрібно надсилати receipt на сервер для базової перевірки.
Але сервер-сайд валідація все одно потрібна для consumable покупок та проти fraud. App Store Server API замінює старий /verifyReceipt endpoint. Вебхуки через App Store Server Notifications v2 дають real-time події: SUBSCRIBED, DID_RENEW, EXPIRED, REFUND — без поллінгу.
Типова помилка: не обробляють paymentQueue(_:updatedTransactions:) у фоні для незавершених транзакцій. Користувач купив consumable, додаток впав до finishTransaction — покупка висить у черзі, при наступному запуску відновлюється та вимагає повторної обробки на сервері. Без ідемпотентності сервера — подвійне нарахування.
Як не втратити дохід на підписках?
Підписочна модель вимагає відстеження станів: тріал → активна → grace period → expired → refunded. RevenueCat — фактичний стандарт для керування підписками в продакшні. Абстрагує StoreKit та Google Play Billing, дає unified API, webhooks, аналітику когорт та A/B тести paywall.
Альтернатива RevenueCat — власна реалізація з Adapty або Qonversion. Повністю кастомна — тільки якщо дані не повинні залишати інфраструктуру або є нестандартна логіка. Ми гарантуємо, що налаштування webhooks та обробка подій життя підписки виконується без втрат — перевірено на проектах з аудиторією понад 500 тис. DAU.
Google Play Billing Library 6+ вимагає обробки PurchasesUpdatedListener та явного виклику acknowledgePurchase() або consumePurchase() протягом 3 днів — інакше Google автоматично скасовує покупку та повертає гроші. Середня вартість такої помилки — втрата значної суми на користувача на місяць (за даними наших проектів).
Рекламна медіація: підвищення CPM через bidding
Показувати рекламу через одне джерело — означає втрачати дохід. Медіація (waterfall або bidding) запитує рекламу у декількох мереж і показує найкращу ставку. Google AdMob — основа для banner, interstitial, rewarded. Медіація через AdMob Mediation або MAX (AppLovin) — другий де-факто стандарт. MAX використовує In-App Bidding — real-time аукціон без водоспаду. На практиці MAX дає CPM на 15-30% вище класичного waterfall (залежить від гео та аудиторії). У США для rewarded відео CPM може перевищувати певну суму. При 100 000 показів rewarded відео на день перехід з waterfall на In-App Bidding може приносити додатково значну суму щоденно.
ironSource (Unity Ads) — сильна позиція в ігровому сегменті, особливо rewarded video. Mintegral — добре закриває азійську аудиторію.
Налаштування медіації вимагає ATT (App Tracking Transparency) на iOS 14+. Без requestTrackingAuthorization рекламний CPM падає в 3-5 разів для користувачів, які не погодилися. SKAdNetwork та Privacy Manifest (iOS 17) — обов'язкові вимоги, без яких рев'ю падає.
| Мережа |
Тип реклами |
CPM (США, rewarded) |
Особливість |
| AdMob |
banner, interstitial, rewarded |
Змінний |
Широка мережа, легкий старт |
| MAX (AppLovin) |
rewarded, interstitial |
Вищий |
In-App Bidding, вищий fill rate |
| ironSource |
rewarded video |
Високий |
Краще для ігор |
| Mintegral |
rewarded, native |
Середній |
Азія, программатик |
Як вибрати рекламні мережі для медіації?
Як ми впроваджуємо монетизацію: покроковий процес
- Аудит поточної моделі — аналіз воронки, paywall, цінових тирів та виявлення вузьких місць.
- Проектування моделі — вибір типу (subscription, consumable, non-consumable) та оптимізація цінових точок.
- Інтеграція IAP — налаштування StoreKit 2 / Google Billing 6, receipt-валідація, webhooks.
- Рекламна медіація — підключення 3-6 мереж, налаштування waterfall або In-App Bidding, тестування fill rate.
- Аналітика та когорти — інтеграція RevenueCat, Amplitude або Firebase для відстеження LTV.
- A/B тестування paywall — використання Remote Config для експериментів без релізу.
- Запуск та моніторинг — 2 тижні безкоштовної підтримки після запуску, фікс багів по SLA 24 години.
Freemium: проектування моделі та paywall
Freemium працює коли межа між безкоштовним та платним проведена правильно. Занадто жорсткий paywall на старті — користувач видаляє. Занадто щедрий безкоштовний тир — немає стимулу платити.
Як проєктувати paywall для freemium?
Паттерн, який працює технічно: feature flags з сервера (Remote Config у Firebase або LaunchDarkly) керують доступом до фіч. Це дозволяє A/B тестувати paywall без релізу, змінювати умови тріалу, проводити акції.
Реалізація на рівні коду: EntitlementManager — єдина точка перевірки доступу до фіч, яка знає про статус підписки, флаги та промо. Жодних перевірок isPremium розкиданих по всьому коду. Досвід показує: такий підхід знижує кількість багів з paywall на 80% (підтверджено на 30+ проектах).
Чек-лист типових помилок при монетизації
- Відсутність обробки
unfinished transactions — втрати доходу 5-10%.
- Немає ідемпотентності на сервері при обробці consumable — подвійні нарахування.
- Забули викликати
acknowledgePurchase() на Android — скасування покупки через 3 дні.
- Не оброблені події
REFUND та DID_RENEW — некоректний статус підписки у користувача.
- Paywall без A/B тестів — залишають 20-40% потенціалу монетизації.
- Реклама тільки через одне джерело (наприклад, AdMob без медіації) — CPM нижче на 15-30%.
Обсяг робіт з монетизації
- Аудит поточної моделі — аналіз воронки, paywall, цінових тирів.
- Інтеграція IAP — StoreKit 2 / Google Billing 6, receipt-валідація, webhooks.
- Рекламна медіація — налаштування MAX / AdMob, підключення 3-6 мереж, тестування fill rate.
- Налаштування аналітики — RevenueCat, Amplitude / Firebase, когортний аналіз.
- Документація — опис ентайтлментів, процедура відновлення, чек-лист рев'ю.
- Навчання команди — розбір типових помилок, рекомендації з підтримки.
- Гарантія — безкоштовна підтримка 2 тижні після запуску, фікс багів по SLA 24 години.
Терміни орієнтовно
| Етап |
Тривалість |
| Базова IAP (один store) |
1–2 тижні |
| Підписочна система + RevenueCat + paywall |
3–5 тижнів |
| Рекламна медіація (MAX + 3 мережі) |
1–2 тижні |
| Повний цикл (IAP + реклама + аналітика) |
4–8 тижнів |
Вартість розраховується індивідуально. Ми працюємо в цій сфері більше 8 років і реалізували більше 40 проектів з монетизацією — багато з них пройшли App Store Review без жодного блокування. Зв'яжіться з нами для аудиту або замовте консультацію — розповімо, які точки зростання є у вашому додатку.
Джерела: Apple StoreKit 2 Documentation, RevenueCat Best Practices, Wikipedia: Freemium.