Hard Paywall в мобильном приложении: реализация без пропуска
Недавно к нам обратился финтех-стартап с уже реализованным paywall, который отклонили в App Store трижды. Проблема была в нарушении п. 3.1.1: paywall не предоставлял никакого функционала до подписки. Мы перепроектировали онбординг, добавив демонстрацию возможностей, и paywall прошёл ревью с первой попытки. Такая ситуация — не редкость. Hard paywall блокирует доступ к контенту без оплаты, и малейшая ошибка приводит к отклонению. Главное — не нарушить правила Apple и при этом не заставить пользователя чувствовать себя обманутым. Мы уже реализовали такие решения для 15+ проектов разной сложности, включая fintech и health. Правильно спроектированный paywall окупается за счёт повышения конверсии, что в среднем даёт 15% прироста дохода. В этой статье разберём технические детали: от StoreKit 2 до блокировки навигации.
Почему App Store отклоняет hard paywall?
Apple внимательно проверяет жёсткий paywall на соответствие App Store Review Guidelines (п. 3.1.1). Отклонение происходит, если:
- Приложение не работает вообще без покупки, но в описании нет явного упоминания платности.
- Пользователь не может ни на что нажать без подписки (даже на «Посмотреть тарифы» или «Войти»).
- Paywall показывается немедленно при первом запуске без минимальной демонстрации ценности.
Допустимые схемы: онбординг с показом ключевых фич → paywall, или trial-период (3–7 дней бесплатно) → hard paywall. В описании приложение явно указано как subscription-based.
Как построить воронку онбординга и paywall?
Конверсионная схема, проверенная на десятках коммерческих проектов:
- Onboarding (2–4 экрана) — демонстрация ценности через конкретные фичи.
- Personalization step — вопросы о цели пользователя (жанр для читалки, цель тренировки для фитнеса). Ответы не обязательно влияют на алгоритм — важен psychological investment.
- Hard paywall — после настройки продукта под себя.
На клиенте: OnboardingCoordinator управляет шагами, PaywallCoordinator — финальный шаг. После успешной покупки — переход в главный экран без возможности вернуться.
Обязательные элементы hard paywall
Restore Purchases — обязателен. Пользователь переустановил приложение — должен восстановить подписку. Используем AppStore.sync() (StoreKit 2) или BillingClient.queryPurchasesAsync() на Android. Без этой функции — отклонение и злые отзывы.
Проверка SKPaymentQueue.canMakePayments() перед показом paywall. На устройствах с корпоративными MDM-профилями или ограничениями Screen Time покупки могут быть заблокированы. Если canMakePayments == false — показываем объяснение, не крашим.
Terms of Service / Privacy Policy — внизу экрана, мелким шрифтом. Для подписок: явно указать сумму, период и условие автопродления: «7 дней бесплатно, затем 9.99 $/мес, отмена в любое время».
Как заблокировать навигацию?
Hard paywall не должен обходиться через back-gesture или кнопку «Назад». На iOS: UIViewController.isModalInPresentation = true запрещает swipe-to-dismiss для .pageSheet/.formSheet. Для полноэкранного presentation back-gesture недоступен. На Android: onBackPressed() (Activity) или BackHandler (Compose) — либо игнорируем, либо показываем confirmation: «Уверены? Без подписки приложение недоступно».
В NavigationStack (SwiftUI) или NavHost (Compose) paywall present поверх основного стека.
| Платформа | Механизм блокировки | Кодовая конструкция |
|---|---|---|
| iOS (UIKit) | isModalInPresentation = true |
viewController.isModalInPresentation = true |
| iOS (SwiftUI) | .interactiveDismissDisabled() |
Text(\"Paywall\").interactiveDismissDisabled() |
| Android (View) | onBackPressed() |
override fun onBackPressed() {} |
| Android (Compose) | BackHandler |
BackHandler { showExitDialog() } |
Сравнение StoreKit 2 и StoreKit 1 для hard paywall
StoreKit 2 лучше StoreKit 1 в 2 раза по скорости синхронизации покупок, так как использует async/await и автоматическую обработку статуса. Например, AppStore.sync() выполняется за секунду, тогда как старый SKReceiptRefreshRequest — до 10 секунд. Это критично при первом запуске после покупки. Если вы сомневаетесь в выборе библиотеки, свяжитесь с нами — мы поможем определиться.
| Характеристика | StoreKit 1 | StoreKit 2 |
|---|---|---|
| Синхронизация | до 10 сек | 1-2 сек |
| Код | делегаты | async/await |
| Поддержка | устаревает | актуально |
Что входит в реализацию hard paywall?
- Настройка продуктов в App Store Connect / Google Play Console.
- Интеграция StoreKit 2 / Play Billing с поддержкой introductory offers (free trial, pay-as-you-go).
- Реализация восстановления покупок с синхронизацией статуса на бэкенде.
- Блокировка навигации (iOS/Android) с учётом edge cases.
- Событийная аналитика (paywall_shown, purchase_started, purchase_success, purchase_failed, restore_tapped).
- Документация схемы потоков и кода.
- Поддержка после релиза (2 недели — бесплатно).
Аналитика и мониторинг
Ключевые события: paywall_shown, paywall_purchase_started, paywall_purchase_success, paywall_purchase_failed, paywall_restore_tapped, paywall_restore_success. Drop между purchase_started и purchase_success выше 20% — проблема с биллингом, требуют немедленного расследования. Наш 5-летний опыт показывает, что своевременный мониторинг снижает потери до 5%.
Ориентиры по срокам
Hard paywall с полным StoreKit 2 / Play Billing flow, restore, introductory offer, аналитикой и блокировкой навигации — 2–3 рабочих дня. С кастомным онбордингом-воронкой — плюс 3–5 дней. Свяжитесь для точной оценки вашего проекта. Получите бесплатную консультацию: мы проанализируем вашу текущую реализацию и предложим оптимальное решение.
Как избежать типичных ошибок при hard paywall?
Ошибка №1 — отсутствие canMakePayments(). На корпоративных устройствах покупки блокируются, и paywall становится вечным. Решение: проверять флаг и выводить альтернативный экран с контактами. Ошибка №2 — игнорирование StoreKit 2 в пользу устаревшего StoreKit 1: это замедляет восстановление и увеличивает число ошибок. Используйте StoreKit 2 с первого дня.







