При запуске мобильного приложения с подписками типичная ситуация: платёж не прошёл, пользователь даже не заметил, приложение молчит. Уходит до 60% клиентов при первом сбое автоплатежа. Без цепочки уведомлений восстановить подписку почти нереально. Грамотная система push-уведомлений удерживает до 40% пользователей на грани отписки. Разберём, как настроить уведомления об истечении подписки с помощью StoreKit 2, FCM и правильной сегментации. Наш опыт — 5+ лет и 50+ проектов в сфере подписок.
Почему одного push-уведомления недостаточно?
Одно уведомление — плохая стратегия. Пользователь может проигнорировать или отложить. Исследования показывают: каскад из 3-4 напоминаний повышает конверсию в продление на 30%. Используем цепочку: за 7 дней → за 1 день → в день истечения → через 3 дня. Каждый шаг — новый призыв с конкретным deeplink.
Серверные события vs. локальные уведомления
Первый вопрос: откуда знать, когда подписка истекает? Для In-App Purchase (StoreKit) Apple присылает события на сервер через App Store Server Notifications V2. Для собственных серверных подписок — логика на вашей стороне.
StoreKit 2 / App Store Server Notifications V2: Apple шлёт RENEWAL и EXPIRED события на ваш сервер. При получении DID_FAIL_TO_RENEW — первый сигнал, что нужно отправить push. При GRACE_PERIOD_EXPIRED — последний шанс. Apple самостоятельно отправляет некоторые системные уведомления, но полагаться только на них нельзя — бизнес-логику напоминаний строить нужно самостоятельно.
// Серверная нотификация V2 (декодированный payload) { "notificationType": "DID_FAIL_TO_RENEW", "subtype": "GRACE_PERIOD", "data": { "bundleId": "com.example.app", "transactionInfo": { ... } } } App Store Server Notifications
Локальные уведомления подходят только если сервера нет совсем, или для офлайн-сценариев. UNUserNotificationCenter с UNCalendarNotificationTrigger — планируем уведомление на дату expiresDate - 3 days. Проблема: если пользователь продлил подписку через веб или другое устройство, локальное уведомление всё равно сработает. Без синхронизации с сервером это даёт ложные срабатывания.
| Критерий | Серверные push | Локальные уведомления |
|---|---|---|
| Точность | Высокая | Низкая (ложные срабатывания) |
| Зависимость от сервера | Обязательна | Не требуется |
| Поддержка deeplink | Да | Да |
| Гибкость цепочки | Максимальная | Ограничена |
| Канал | APNs / FCM | UNUserNotificationCenter |
Серверные push лучше локальных в 3–4 раза по точности доставки и гибкости настройки.
Цепочка напоминаний
Работающая схема для B2C:
- За 7 дней до истечения: «Подписка истекает 15 апреля — продлите, чтобы не потерять данные»
- За 1 день: конкретный призыв с deeplink на экран управления подпиской
- В день истечения: уведомление с ограниченным временным предложением (если есть)
- Через 3 дня после: реактивация со скидкой (опционально)
Deeplink в уведомлении обязателен. Push без действия теряет конверсию. На iOS: UNNotificationAction с foreground — открывает приложение и передаёт userInfo. На Android: PendingIntent с нужным Intent.
// iOS: обработка tap по уведомлению о подписке func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { let info = response.notification.request.content.userInfo if info["type"] as? String == "subscription_expiry" { NavigationRouter.shared.navigate(to: .subscriptionManagement) } } На Android через FCM аналогично: в data payload передаём type: subscription_expiry, в FirebaseMessagingService.onMessageReceived маршрутизируем.
Детали реализации на Android (FCM)
class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(message: RemoteMessage) { val type = message.data["type"] if (type == "subscription_expiry") { // Открыть экран подписок val intent = Intent(this, SubscriptionActivity::class.java) startActivity(intent) } } } Что делать, если push не доставлен?
Проверьте разрешения на уведомления (ATT на iOS, настройки на Android), статус device token и правильность deeplink. Учитывайте часовой пояс пользователя — отправляйте push в локальное дневное время. Реализуйте логику повторной отправки с ручным триггером.
Сегментация и тайминг
Пользователь с месячной подпиской и пользователь с годовой — разные ситуации. Для месячной 7-дневное окно — это 25% оставшегося срока, что агрессивно. Годовая — 7 дней из 365, нормально.
| Тип подписки | Окно напоминания | Агрессивность |
|---|---|---|
| Месячная | 3-5 дней | Высокая |
| Годовая | 7-14 дней | Низкая |
| Недельная | 1-2 дня | Средняя |
Часовой пояс пользователя критичен: push в 3 ночи — раздражение и отписка. Firebase FCM позволяет задавать delivery_time с учётом локального времени устройства. Для APNs это делается на уровне сервера через планировщик задач с учётом timezone пользователя.
Процесс работы
- Аудит текущей системы подписок: StoreKit / серверные / гибрид.
- Настройка App Store Server Notifications V2 или серверных вебхуков для триггеров истечения.
- Реализация цепочки push-уведомлений с deeplink на нужный экран.
- Тестирование на sandbox-окружении StoreKit и боевой проверкой FCM.
- Мониторинг и корректировка таймингов на основе аналитики.
Что входит в работу
- Настройка App Store Server Notifications V2 / серверных вебхуков
- Разработка цепочки push-уведомлений (до 4 этапов)
- Интеграция deeplink на экран управления подпиской
- Логирование событий для аналитики
- Документация и инструкция для вашей команды
- Поддержка 1 месяц после запуска
Ориентиры по срокам
Интеграция с готовым сервером уведомлений и StoreKit 2 — 2–3 дня. Реализация серверной логики с нуля, планировщиком задач и сегментацией пользователей — до 1 недели.
Потеря одного пользователя с годовой подпиской обходится в среднем в $300–$500, а каскад уведомлений возвращает 15–20% из них. Внедрение цепочки уведомлений увеличивает LTV на 20–30% и снижает затраты на привлечение новых пользователей на 15%.
Получить консультацию по вашему проекту — свяжитесь с нами, оценим объём работ и предложим решение. Закажите бесплатный аудит текущей системы подписок.







