Auto-renewable subscriptions — найскладніший тип IAP. Не тому що StoreKit складний сам по собі, а тому що навколо нього виростає ціла екосистема: grace periods, billing retry, downgrade/upgrade між тирами, promo offers, introductory pricing, і нарешті — обробка churn через expirationIntent. Помилка на будь-якому етапі веде до втрат доходу. Ми впроваджуємо підписки під ключ за 3–5 днів, гарантуючи коректну логіку на всіх етапах життєвого циклу.
Життєвий цикл підписки і де він ламається
Підписка в iOS існує не тільки поки активна. Apple автоматично продовжує її за 24 години до завершення. Якщо оплата не пройшла — починається billing retry period (до 60 днів). У цей час статус підписки expired, але Apple продовжує спроби списання. Більшість додатків блокують доступ одразу після expirationDate — це невірно.
Правильна логіка: перевіряти renewalInfo.isInBillingRetryPeriod. Якщо true — даємо grace period (налаштовується в App Store Connect, зазвичай 6 днів для річних, 3 дні для місячних). Користувач із проблемою картки не повинен втрачати доступ негайно.
StoreKit 2 обробляє grace period прозоро, що знижує churn на 20–30% порівняно з ручною реалізацією на StoreKit 1. Ось приклад коректної перевірки:
for await result in Transaction.currentEntitlements { guard case .verified(let transaction) = result else { continue } if transaction.productType == .autoRenewable { let renewalInfo = try? await transaction.subscriptionStatus.first?.renewalInfo let isInGracePeriod = renewalInfo?.gracePeriodExpirationDate != nil let isRetrying = renewalInfo?.isInBillingRetryPeriod == true if transaction.revocationDate == nil && (transaction.expirationDate ?? .distantPast > .now || isInGracePeriod || isRetrying) { unlockPremium() } } } Чому важливий grace period?
Без grace period користувачі з проблемами оплати миттєво втрачають доступ. Apple дозволяє налаштувати до 16 днів відстрочки — за цей час клієнт встигає змінити картку, а ви зберігаєте його. Наш досвід показує, що 70% користувачів повертаються до оплати протягом grace period. Якщо ж блокувати доступ одразу — вони йдуть назавжди.
Тири та переходи між ними
Якщо в додатку декілька тирів (Basic, Pro, Enterprise) — потрібна subscription group в App Store Connect. Всі тири в одній групі, користувач може мати лише одну активну підписку в групі одночасно.
Upgrade (перехід на дорожчий тир) — діє одразу, Apple перераховує залишок. Downgrade — набирає чинності в наступний розрахунковий період. Crossgrade (одна ціна, інший тир) — залежить від налаштування: можна зробити immediate або deferred.
Відстежувати це на клієнті через originalTransactionID та subscriptionGroupID. На сервері — зберігати повну історію транзакцій та обробляти Apple Server Notifications v2 (App Store Server Notifications). Типи подій, які обов'язково обробляти: DID_RENEW, DID_FAIL_TO_RENEW, EXPIRED, GRACE_PERIOD_EXPIRED, REFUND.
Як обробляти переходи між тирами?
Upgrade активується негайно — Apple пропорційно перераховує вартість. Downgrade відкладається до наступного циклу. Crossgrade можна налаштувати як immediate або deferred в App Store Connect. Важливо коректно оновлювати UI, показуючи користувачеві дату набрання чинності зміни. Для цього використовуйте renewalInfo.renewalDate та offerType.
Introductory та promotional offers
Introductory pricing (перші N періодів за зниженою ціною) налаштовується в App Store Connect і автоматично застосовується для нових підписників. Проблема — користувач, який відписався і хоче повернутися, за замовчуванням не отримує інтро-ціну знову. Для цього існують promotional offers — їх можна видавати за своєю логікою (win-back кампанії).
| Тип пропозиції | Налаштування | Кому доступно | Умови застосування |
|---|---|---|---|
| Introductory | В App Store Connect | Нові підписники | Автоматично, один раз |
| Promotional | Генерація підпису на сервері | Будь-які користувачі | За вашою логікою, будь-який період |
| Promo-код | App Store Connect | Всі | Фіксована знижка, обмежена кількість |
Підпис для promotional offer генерується на сервері з приватним ключем із App Store Connect:
// На клієнті створюємо paymentDiscount let discount = SKPaymentDiscount( identifier: "winback_3months", keyIdentifier: keyID, nonce: nonce, // UUID з сервера signature: signature, // підпис з сервера timestamp: timestamp ) payment.paymentDiscount = discount Без серверного підпису promotional offer недійсний — Apple перевіряє підпис на своїй стороні.
Що входить в роботу
- Аудит поточної реалізації IAP та підписних груп
- Проектування схеми тирів і груп (subscription group) з урахуванням бізнес-логіки
- Інтеграція StoreKit 2 з підтримкою grace period, billing retry та обробки помилок
- Налаштування App Store Server Notifications (v2) на бекенді з обробкою всіх критичних подій
- Реалізація win-back логіки з використанням promotional offers
- Документування коду та інтеграція аналітики відписок
- Навчання команди роботі з Sandbox та тестуванням підписок
- Підтримка після запуску (1 місяць безкоштовно)
Процес роботи
Аудит поточної реалізації → проектування структури підписних груп і тирів → інтеграція StoreKit 2 з підтримкою grace period та billing retry → налаштування App Store Server Notifications на бекенді → реалізація логіки win-back та промо-оферів → тестування в Sandbox з імітацією завершення підписки (Sandbox скорочує періоди: місяць = 5 хвилин).
Терміни — 3–5 днів залежно від кількості тирів, наявності бекенду та вимог до аналітики відписок. Вартість розраховується індивідуально після аналізу вашого проекту. Зв'яжіться з нами — оцінимо обсяг та запропонуємо оптимальне рішення. Отримайте консультацію сертифікованого iOS-розробника з 5+ років досвіду впровадження підписок.
Ми гарантуємо: коректну обробку billing retry, grace period та всіх типів переходів, відповідність App Store Review Guidelines (Section 4.2 та 5.1), а також повну підтримку на етапі тестування та публікації.







