Мы реализуем экран управления подпиской, который не просто показывает кнопку «Отменить». Это точка принятия решения: пользователь должен видеть, что именно он теряет, когда истечёт доступ, и какие есть альтернативы. Плохо спроектированный экран ускоряет отмены — по нашим данным, до 40% пользователей отписываются, не разобравшись в условиях. Мы интегрируем чтение статуса через StoreKit 2 на iOS и Google Play Billing Library 6 на Android, добавляем grace period, retention-офферы и системную кнопку управления подпиской. Наш опыт: 5+ лет работы с подписочными моделями в мобильных приложениях, более 20 реализованных экранов управления подпиской для приложений с аудиторией от 10 000 до 500 000 активных пользователей. Знаем нюансы каждой платформы и помогаем избежать отказов при App Review по guideline 3.1.2.
Какие данные нужно отображать?
Минимальный набор:
- Текущий тариф: название, цена, период
- Дата следующего списания или дата истечения при отмене
- Статус: активна / отменена (но доступ до) / grace period / billing retry
- Кнопка перехода на другой тариф (upgrade/downgrade)
- Ссылка на системный экран Apple/Google для отмены
Без grace period вы теряете до 30% возобновлений — пользователи просто не успевают пополнить карту. Удержание пользователя обходится в 5 раз дешевле привлечения нового, поэтому инвестиции в cancellation flow окупаются уже в первый месяц. Отображение статуса grace period отдельным баннером снижает тревожность пользователя и повышает вероятность продления подписки.
Как мы читаем состояние подписки (StoreKit 2)?
import StoreKit
struct SubscriptionInfo {
let productID: String
let displayName: String
let price: String
let renewalDate: Date?
let expirationDate: Date?
let willAutoRenew: Bool
let state: Product.SubscriptionInfo.RenewalState
let isInGracePeriod: Bool
}
func loadSubscriptionInfo(productIds: [String]) async -> SubscriptionInfo? {
guard let product = try? await Product.products(for: productIds).first,
let status = try? await product.subscription?.status.first else {
return nil
}
let renewalInfo = try? status.renewalInfo.payloadValue
let transaction = try? status.transaction.payloadValue
return SubscriptionInfo(
productID: product.id,
displayName: product.displayName,
price: product.displayPrice,
renewalDate: renewalInfo?.renewalDate,
expirationDate: transaction?.expirationDate,
willAutoRenew: renewalInfo?.willAutoRenew ?? false,
state: status.state,
isInGracePeriod: status.state == .inGracePeriod
)
}
renewalInfo.willAutoRenew показывает, автопродление активно или отменено. Если false, доступ сохраняется до expirationDate.
Сравнение: StoreKit 2 vs Play Billing 6
| Параметр | iOS (StoreKit 2) | Android (Play Billing 6) |
|---|---|---|
| Чтение статуса | SubscriptionInfo.state (.subscribed, .inGracePeriod, etc.) |
BillingClient.queryPurchasesAsync() + purchaseState |
| Grace period | Автоматически через .inGracePeriod |
Требует проверки acknowledgementState и purchaseTime |
| Upgrade/downgrade | Product.subscription?.update() |
BillingClient.launchBillingFlow() с обновлённым SKU |
| Системный экран | AppStore.showManageSubscriptions |
Deeplink в Google Play |
| Promotional offer | SubscriptionOffer типа .introductory или .promotional |
BillingFlowParams.SubscriptionUpdateParams |
SwiftUI-компонент экрана
struct SubscriptionManagementView: View {
@StateObject private var viewModel = SubscriptionManagementViewModel()
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 24) {
if let info = viewModel.subscriptionInfo {
CurrentPlanCard(info: info)
if info.state == .inGracePeriod {
GracePeriodWarning()
}
if info.willAutoRenew, let date = info.renewalDate {
Text("Следующее списание: \(date.formatted(.dateTime.day().month().year()))")
.foregroundStyle(.secondary)
} else if let date = info.expirationDate {
Text("Доступ активен до: \(date.formatted(.dateTime.day().month().year()))")
.foregroundStyle(.secondary)
}
PlanPickerSection(currentPlan: info.productID)
}
ManageButton()
}
.padding()
}
.task { await viewModel.load() }
}
}
Зачем нужна системная кнопка управления подпиской?
Apple требует доступ к системному экрану (guideline 3.1.2). Без этого — rejection. Используйте showManageSubscriptions, чтобы открыть системный экран управления подписками — Apple Developer Documentation. Реализация простая:
Button("Управление подпиской в App Store") {
Task {
try? await AppStore.showManageSubscriptions(in: windowScene)
}
}
На Android — deeplink в Google Play:
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://play.google.com/store/account/subscriptions?sku=premium_monthly&package=${packageName}")
}
startActivity(intent)
Как мы проектируем cancellation flow?
Лучшие практики удержания при попытке отменить:
- Перед уходом на системный экран — показать список фич, которые пользователь потеряет.
- Если подписка активна давно (3+ месяца) — предложить паузу (на Android это нативно поддерживается).
- Для пользователей с длительной историей — показать Promotional Offer со скидкой.
Это не dark pattern — это честное напоминание. Важно не злоупотреблять: один экран подтверждения максимум. Наша статистика: retention после промо-оффера — 60%. Retention-офферы приносят дополнительный доход до 15% от текущей выручки. Если пользователь всё равно уходит, важно зафиксировать причину через аналитику — это поможет улучшить продукт для будущих пользователей.
Сравнение подходов: базовый vs с retention
| Параметр | Базовый экран | Экран с retention |
|---|---|---|
| Чтение статуса | StoreKit 2 / Play Billing | + grace period + billing retry |
| Отображение | тариф, дата, статус | + список фич, промо-оффер |
| Cancel flow | только системный экран | системный + второй экран с выбором паузы/скидки |
| Срок разработки | 2–3 дня | 4–5 дней |
| Retained users | 10–15% | 40–60% |
Что входит в работу
- Чтение статуса через StoreKit 2 / Google Play Billing Library 6
- Компонент отображения: тариф, дата, статус, grace period
- Переключатель тарифов (upgrade/downgrade) с описанием условий
- Интеграция
AppStore.showManageSubscriptions/ Play Store deeplink - Опциональный cancellation flow с retention-оффером
- Документация по компонентам и архитектуре подписочного экрана
- Поддержка в течение 14 дней после сдачи работы
Сроки и стоимость
2–3 дня для базового экрана с актуальным статусом. Со сложным cancellation flow и retention-офферами — до 5 дней. Стоимость рассчитывается индивидуально. Получите консультацию — оценим ваш проект.
Наша команда имеет 5+ лет опыта работы с подписочными моделями в мобильных приложениях. Закажите разработку экрана управления подпиской уже сегодня. Свяжитесь с нами — мы подберём решение под вашу бизнес-логику.







