К блогу

Подписки и YooKassa

Монетизация в шаблоне построена вокруг подписок, а не разовых платежей. Пользователь выбирает план, оплачивает через YooKassa, получает доступ к функциям тарифа (включая квоту запусков AI-агентов), а система следит за сроком действия и инициирует продление.

Все тарифы описаны в одном месте — app/lib/config/plans.ts. Там задаются id, tier, цена в рублях, интервал (day, month, year), количество включённых agentRunsIncluded и список фич для UI. Есть Daily (для тестирования вебхуков), Pro и Enterprise с месячной и годовой оплатой. Годовая цена считается автоматически со скидкой YEARLY_DISCOUNT_PERCENT.

Пользовательский путь начинается на странице Premium или Billing. При нажатии «Оформить» создаётся платёж в YooKassa через API: сумма, описание, metadata с userId и planId. Пользователь перенаправляется на страницу оплаты провайдера.

После успешной оплаты YooKassa отправляет webhook на /api/yookassa/webhook. Обработчик проверяет подпись и IP-адрес отправителя (в production критично — принимайте только запросы с адресов YooKassa), находит платёж в БД и активирует подписку: записывает tier, дату окончания, связывает с пользователем.

Параллельно срабатывает аналитика: события checkout_started и payment_succeeded фиксируют воронку конверсии. В админке на /admin/analytics видно, сколько пользователей дошли до оплаты и сколько завершили её.

Продление — отдельная история. Подписка имеет expiresAt. Loader в защищённом layout _app.tsx при каждом запросе проверяет: не истекла ли подписка, не нужно ли инициировать повторный платёж. Cron-задача через pg-boss делает то же самое в фоне для пользователей, которые давно не заходили.

При продлении создаётся новый платёж с сохранённым способом оплаты (если пользователь согласился на автосписание). Если платёж не прошёл — подписка переходит в grace period или деактивируется, в зависимости от вашей политики (настраивается в billing.server.ts).

Страница /billing показывает текущий план, дату следующего списания, историю платежей и остаток квоты агентов. Компонент PricingPlans переиспользуется и на маркетинговой странице Premium — не дублируйте разметку.

Для локальной разработки заведите тестовый магазин в личном кабинете YooKassa. Тестовые карты и сценарии описаны в документации провайдера. Webhook на localhost можно пробросить через ngrok или аналог — без этого вы не увидите полный цикл активации подписки.

Тариф Daily (20 ₽ в день) специально добавлен для отладки: короткий интервал, дешёвая сумма, быстрая проверка cron-продления. Используйте его в staging, а в production оставьте Pro и Enterprise.

Безопасность: никогда не храните полные данные карт — YooKassa берёт это на себя. В вашей БД только id платежа, сумма, статус и metadata. Логи webhook не должны содержать secretKey.

Если вы меняете провайдера оплаты, интерфейс в billing.server.ts изолирует YooKassa-специфику. Замените адаптер, сохранив контракт: createPayment, handleWebhook, renewSubscription.