Кальтер / Блог

SaaS-прототип с ИИ: аккаунты, тарифы и что не считать готовым

Разбери SaaS-прототип на примере сервиса обработки заявок: отдели аккаунт, организацию, подписку и права на функции, а оплату выдавай только после подтверждения от сервера.

Материал подготовлен с помощью ИИ; источники и расчёты проверены редакцией ZASLON.

SaaS требует связать аккаунт, тарифные права и серверную проверку доступа; на схеме: Аккаунт, Проверка сессии, Тарифные права.
Редакционная схема ZASLON. Условная иллюстрация механизма; не рыночные данные и не интерфейс сервиса.

У SaaS недостаточно сделать экран «Тариф активен». В сервисе для обработки заявок отдела эксплуатации должны согласованно работать аккаунт пользователя, организация-клиент, роль, подписка и разрешение на конкретную функцию. Даже если интерфейс выглядит законченным, он ничего не доказывает о списании денег или доступе к данным.

Четыре понятия, которые легко смешать

Представим компанию с четырьмя сотрудниками. У каждого свой user_id; все принадлежат к tenant_id одной организации. Подписка относится к организации, а не к последнему вошедшему сотруднику. subscription_status сообщает состояние отношений с биллингом. entitlement отвечает на вопрос, разрешена ли конкретная возможность — например, экспорт CSV или пять активных операторов. Роль (owner, manager, viewer) отвечает, кто из пользователей может пригласить коллегу, менять настройки или просматривать обращения.

Это четыре уровня правил, а не одно поле isPremium:

Слой Учебный пример Проверка при запросе
Пользователь Сотрудник вошёл в систему Валидна ли серверная сессия?
Tenant Организация north-office К какому tenant принадлежит пользователь?
Роль manager может менять очередь Разрешено ли действие для этой роли?
Entitlement В тарифе разрешён экспорт Включена ли эта функция для tenant и не истекла ли подписка?

Ни tenant_id, присланный из браузера, ни скрытая кнопка тарифа не являются полномочием. Сервер должен извлечь пользователя из подтверждённой сессии, найти его членство в tenant и проверять разрешение на каждом запросе к данным/действию.

Состояния, а не один переключатель

Для демонстрации используй такой жизненный цикл: trialing → active → past_due → canceled. Подпиши, что именно меняется в каждом переходе: после успешного подтверждения первой оплаты активируется нужный entitlement; при неудаче оплаты появляется состояние для обработки ошибки; при отмене будущая возможность может остаться доступной до оплаченного периода — если именно так устроена политика продукта. Не выдумывай для этого универсальное правило: сверяй бизнес-политику с провайдером и условиями своего предложения.

Если выбран Stripe, его руководство по webhooks для подписок перечисляет события вроде invoice.paid, invoice.payment_failed и изменения статуса подписки; в примере провайдера доступ можно выдавать после invoice.paid и проверки subscription.status == active. Документация отдельно предупреждает, что события приходят не обязательно по порядку и могут дублироваться. Поэтому обработчик должен проверять подпись по сырому телу запроса, сохранять уже обработанные event IDs и выдерживать повторную доставку. См. также проверку Stripe webhook-событий. Документация Stripe Entitlements описывает entitlement как условие выдачи или отзыва доступа к функциям продукта; конкретное соответствие тарифов возможностям задаётся в приложении. Это пример интеграции со Stripe, а не общая спецификация всех платежных систем.

Checkout завершён в браузере — или оплата подтверждена?

Страница возврата ?success=true означает лишь, что браузер вернулся по URL. Она не удостоверяет транзакцию: такой URL можно открыть вручную, а сетевой callback может задержаться. Для реального Stripe-подключения сервер принимает подписанный webhook и сопоставляет customer/subscription с аккаунтом вашего приложения. Только после проверки ожидаемого статуса, суммы/товара и организации обновляет запись биллинга. Сами секреты webhook не должны попадать в frontend.

Для учебного прототипа безопасно ограничиться тестовым режимом и явно подписанным demo-планом, который не принимает реальных платежей. Переход в production требует настройки настоящего платежного аккаунта, правил возврата/налогов, серверного endpoint, мониторинга, обработки повторов и проверки ошибок — одной кнопки Checkout недостаточно.

Проверка на примере организации

Создай два tenant (north-office, south-office) и пользователей с разными ролями. Затем проходи матрицу не только через интерфейс:

  1. Войти manager из north-office и открыть собственную заявку — разрешено.
  2. Поменять ID заявки на известный ID south-office в прямом API-запросе — отказ без раскрытия содержимого.
  3. Открыть экспорт, которого нет в entitlement тарифа — серверный отказ, даже если вручную вызвать endpoint.
  4. Отправить success-редирект checkout без webhook — право не появляется.
  5. Передать webhook с некорректной подписью — событие не меняет биллинг.
  6. Повторно доставить один корректный event ID — второй раз не выдаётся новый период.
  7. Провести renewal/payment failure/cancellation в тестовой среде и сравнить серверную запись с данными провайдера.

Это проверочный план, а не заявленный результат испытания. В таблице хранится источник истины: provider customer/subscription IDs, текущий статус, период действия, tenant и дата последней обработанной версии события. Для каждой сверки сохраняй audit event без токенов и платёжных реквизитов.

Сначала попроси ИИ построить схему сущностей и переходов, потом отдельно endpoint оплаты и policy-check. Попроси перечислить негативные проверки до генерации кода. При моделировании серверных таблиц пригодится руководство о схеме БД; детали аутентификации и прав отдельно разобраны в статье про роли и доступ.