Статус подписки меняют прямым UPDATE
trial, past_due и paused выставляются из контроллеров и скриптов, поэтому никто не может сказать, почему клиент активен, а денег нет.
Выберите тему — соберём контекст
Проектируем ядро управления подписками: статусы trial, active, past_due, paused и cancelled, апгрейды и даунгрейды с пропорциональным пересчётом, места в командных тарифах, промокоды, self-service кабинет и метрики удержания.
Или сразу: +7 (962) 368-39-51 Telegram
Проблемы, с которыми приходят SaaS-команды, когда подписок становится больше, чем помещается в голове у поддержки.
trial, past_due и paused выставляются из контроллеров и скриптов, поэтому никто не может сказать, почему клиент активен, а денег нет.
Клиент переходит на старший тариф 12-го числа: без пропорционального пересчёта он платит дважды или получает месяц бесплатно.
Даунгрейд «с конца периода» записан в CRM или в комментарии к тикету, и в нужную дату его просто никто не применяет.
Приглашённые, но не принявшие приглашение пользователи, удалённые участники и места, добавленные в середине периода, ломают сумму в счёте.
Накопительные акции, скидки на первые месяцы и партнёрские условия складываются в отрицательный счёт, который нечем закрыть.
После смены цен существующие клиенты внезапно получают новые суммы, а отток и удержание невозможно разложить по причинам.
Собираем подписки как отдельный домен с явной моделью состояний, чтобы продукт, поддержка и финансы смотрели на одни и те же данные.
Конечный автомат подписки с проверяемыми переходами вместо россыпи UPDATE по таблице.
Правила смены тарифа в середине периода с предсказуемой суммой доплаты или остатка.
Управление количеством пользователей и временной остановкой подписки без потери истории.
Кабинет клиента, скидки и смена цен без ручных писем в поддержку.
Сначала описываем состояния и правила денег, потом автоматизируем — иначе код закрепит текущую путаницу.
Разбираем, какие статусы существуют фактически, кто их меняет и где состояние подписки расходится с платежами.
Описываем автомат состояний, правила пропорционального пересчёта, отложенные изменения, места и политику скидок.
Реализуем ядро подписок, связываем его с эквайрингом и очередями, собираем self-service кабинет и уведомления.
Переносим существующие подписки на новую модель прайс-листа, прогоняем пересчёты на исторических периодах, включаем дашборды удержания и оттока.
Подписка — это не поле status в таблице, а конечный автомат с ограниченным набором состояний: trial, active, past_due, paused, cancelled. Пока переходы делаются прямым UPDATE из контроллера или консольного скрипта, через полгода невозможно объяснить, почему клиент видит активный тариф, а денег за него не поступало. Мы выносим переходы в доменный сервис: каждое изменение — событие с причиной, инициатором и меткой времени, а состояние подписки на любую дату восстанавливается из журнала.
Для каждого статуса явно определяется, что он значит для продукта: доступ к функциям и лимитам, поведение мест, начислений, счетов и уведомлений. past_due — это не блокировка, а льготный период с расписанием повторных списаний; paused останавливает начисления, но сохраняет данные и настройки; cancelled переводит подписку в отключение по концу оплаченного периода, а не мгновенно.
Главный источник споров — апгрейд или даунгрейд в середине расчётного периода. Считаем пропорционально оставшимся дням и фиксируем базу периода в самом тарифе: календарный месяц или anniversary-дата подписки дают разные суммы, и выбирать это должен продукт, а не разработчик при списании. Апгрейд применяется сразу с доплатой разницы, даунгрейд встаёт в очередь отложенных изменений и вступает в силу при закрытии периода.
При изменении прайс-листа версионируем тарифы: старые подписки остаются на своих условиях, а миграция идёт отдельным сценарием с прогоном пересчётов на исторических периодах.
Разница в цене умножается на долю оставшихся дней расчётного периода, база которого задана в тарифе: календарный месяц или anniversary-дата подписки. При апгрейде подписка меняется сразу, а доплата либо списывается отдельным платежом, либо относится в credit balance и учитывается в следующем счёте. Даунгрейд по умолчанию не возвращает деньги, а вступает в силу с начала следующего периода.
Подписка переходит в past_due, и запускается сценарий dunning: повторные попытки списания по расписанию, уведомления клиенту и льготный период, в течение которого доступ сохраняется. Если оплата так и не поступила, доступ ограничивается по заранее описанным правилам, а подписка переводится в cancelled с сохранением данных для последующей реактивации.
При паузе начисления останавливаются, но данные, настройки и история подписки сохраняются, поэтому возврат клиента не требует нового онбординга. Количество мест фиксируется на момент паузы и не списывается, а при возобновлении пересчитывается пропорционально оставшимся дням текущего периода. Вместо отмены мы чаще используем заморозку на срок — это заметно снижает отток по клиентам с сезонной нагрузкой.
Мы версионируем тарифы и оставляем действующие подписки на прежних условиях, помечая их как grandfathered. Миграция выполняется отдельным сценарием с предварительным прогоном пересчётов на исторических периодах, чтобы заранее увидеть, у кого изменится сумма. Клиенты получают уведомление с датой перехода и точной новой суммой, а метрики удержания по когортам показывают эффект миграции отдельно от обычного оттока.
Смежные технические темы, с которыми эта задача обычно решается в одном проекте.
Покажем, как описать жизненный цикл, апгрейды и отложенные изменения так, чтобы смена тарифа считалась автоматически.
Cookie и аналитика
Мы используем необходимые cookie для работы сайта и форм. Аналитические cookie (Яндекс.Метрика, включая запись сессий) подключаем только с вашего согласия — от него можно отказаться. Подробнее: Политика использования cookie и обработка персональных данных.