惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
腾讯CDC
Jina AI
Jina AI
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
小众软件
小众软件
M
MIT News - Artificial intelligence
MyScale Blog
MyScale Blog
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
月光博客
月光博客
L
LangChain Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
C
Check Point Blog
U
Unit 42
人人都是产品经理
人人都是产品经理

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Написать биллинг за неделю: туториал на два года
Лидия Захарова · 2026-05-29 · via Все публикации подряд на Хабре

Написать биллинг за неделю: туториал на два года

Простой

5 мин

9.9K

Меня зовут Лидия, я основатель LBX биллинга для SaaS-продуктов. Мы провели десятки внедрений: от команд, которые только запускают монетизацию, до сервисов с несколькими тысячами активных подписчиков. Видели, как команды наступают на одни и те же грабли. Некоторые с хрустом. Вот четыре ситуации, которые встречаются чаще всего.

Грабли №1. «У нас простая логика, напишем сами»

ЮKassa и CloudPayments поддерживают рекуррентные платежи. Настраивается периодичность, дата списания в месяце, сумма, количество повторений. На первый взгляд есть всё необходимое.

Но рекуррент — это только механизм списания. Он не знает, что клиент перешёл на другой тариф в середине месяца и сумма должна измениться. Не знает, что один пользователь подключился первого числа, другой семнадцатого, и периоды у них разные. Не знает, что делать, если платёж не прошёл: ждать три дня, заблокировать доступ или дать льготный период. Не знает, что клиент попросил паузу на месяц.

Всю эту логику нужно строить поверх платёжной системы самостоятельно.

Льготный период уже отдельная история. Заблокировать юзера немедленно при первой просрочке, значит рисковать его потерять. Карта могла устареть, платёж завис на стороне банка, бухгалтер был в отпуске. 

Грабли №2. «Пока клиентов немного — справляемся вручную»

На старте большинство команд ведут учет подписок и оплат вручную. Счёт сформировали в банке или 1С, отправили клиенту, дождались оплаты, отметили в таблице. Двадцать клиентов занимает пару часов в неделю. Раздражает, но еще терпимо.

При пятидесяти клиентах час превращается в несколько дней. Счёт нужно не только сформировать, но и отследить — оплатили или нет. Платежи разносить вручную: открыть банк, найти входящий перевод, сопоставить с клиентом по ИНН, обновить статус. Если платёж пришёл с некорректным назначением или частично, то разбирать отдельно.

Параллельно копятся исключения. Один клиент договорился о скидке, бухгалтер помнит, что ему выставлять меньше. Другой платит не первого, а двадцатого, так договорились при подключении. Третьему по какой-то причине счёт уходит на квартал вперёд. Каждое такое исключение живёт в голове конкретного человека или в заметке в Notion. Когда этот человек уходит в отпуск или увольняется, исключения теряются. Клиент получает счёт на неправильную сумму. Или не получает вовсе.

Грабли №3. «Автоматизировали — и думали, что готово»

Команда дорастает до автоматизации: счета генерируются и отправляются сами, входящие платежи разносятся по ИНН автоматически. Ощущение, что проект биллинга наконец закрыт.

Но к этому моменту исключения переехали из голов людей в код. Для одного клиента в базе захардкожена спеццена. Для другого — отдельное условие в логике расчёта периода. Для третьего написан отдельный обработчик, про который знает только тот разработчик, который его писал.

Формально, это уже не ручная работа. Но это хрупкая конструкция: изменить условия для пользователя, значит идти в код. Добавить новый тип исключения, ещё один if где-то в обработчике. Разобраться, почему конкретному клиенту выставляется именно такая сумма уже становится задачей разработчика, а не бухгалтера.

Грабли №4. «Добавим позже»

Хотим запустить новый тариф — нужно писать код.
Хотим добавить опцию — нужно писать код.
Хотим скорректировать цену — нужно писать код, тестировать, выкатывать.

Разработчики заняты продуктом. Новый тариф откладывается на следующий спринт. Потом ещё на один. «Добавим позже» в большинстве случаев означает «никогда».

То же самое с архитектурными решениями по биллингу. Переделать модель данных на работающей системе с живыми пользователями это один из самых болезненных технических проектов. Не потому что сложно технически, а потому что цена ошибки высока. Каждый раз, когда команда говорит «сделаем нормально позже», она повышает стоимость этого «позже».

Собственно туториал

Ни одни из этих граблей не уникальны - это стандартная эволюция биллинга в большинстве SaaS команд. Рекуррент в платёжной системе, таблица с оплатами, исключения в коде проходят почти все. Отличается только момент, когда это начинает мешать.

Хорошая новость: грабли стандартные, и о них можно знать заранее.

Разделить тарифный план и условия конкретного клиента. Публичный тариф и то, на что фактически подписан пользователь, разные сущности в модели данных. Скидка, индивидуальная цена, нестандартный расчётный период хранятся в клиентском договоре, а не как исключение в логике тарифа. Это делает все отклонения от стандарта явными и видимыми без чтения кода.

Хранить историю событий, а не только текущее состояние. Не «клиент на тарифе PRO», а «клиент перешёл на тариф PRO 15 марта в 14:32». Без событийной истории перерасчёт за прошлый период невозможен, разбор спорной ситуации невозможен, аудит невозможен. Добавить это задним числом — отдельное приключение.

Не хранить расчётный период как фиксированный объект. Он должен вычисляться на основе истории: дата подключения, смены тарифа, паузы, апгрейды в середине цикла. Если есть полная история, то период всегда можно пересчитать корректно. Если нет, то при любом нестандартном кейсе система начинает выдавать неверные суммы, которые сложно объяснить клиенту и ещё сложнее исправить задним числом.

Предусмотреть состояния подписки. Минимальный набор: активна, ожидает оплаты, заблокирована, отменена. Каждое состояние имеет свою логику доступа, начислений и автоматических действий системы. Состояние «активен / неактивен» это не покрывает.

Обеспечить идемпотентность денежных операций. Одно и то же списание не должно выполняться дважды при сетевом сбое или перезапуске. Каждая транзакция должна иметь уникальный идентификатор, повторный вызов возвращать тот же результат без повторного списания. Это базовое требование к надёжности, которое на старте кажется излишним, но его лучше заложить сразу.

Управление тарифами без изменений кода. Добавление тарифа, изменение цены, запуск новой опции, всё это должно настраиваться через интерфейс, а не через задачу разработчику. Это снимает зависимость коммерческих решений от бэклога и позволяет тестировать ценообразование в спокойном режиме.

Разделить выставление счёта и факт оплаты. Счёт формируется в начале расчётного периода. Оплата фиксируется, когда деньги фактически поступили. Между ними есть состояние ожидания с собственной логикой и сроками. Без этого разделения корректный учёт задолженностей и работа с просрочками существенно усложняются.

Итог

Биллинг редко попадает в список приоритетных задач. Есть продукт, есть метрики, есть задачи, которые видны пользователям. Биллинг не в этом списке, пока не начинает мешать. Именно поэтому архитектурные решения по нему так часто откладываются, и именно поэтому потом возвращаются в виде технического долга с высоким приоритетом.

Я ни разу не встречала команду, которая пожалела, что нормально заложила биллинг с самого начала. Зато команд, которые пожалели об обратном, хватает.

А как у вас? Писали ли биллинг сами? интересно услышать, что оказалось неожиданно сложным и на каком этапе стало понятно, что задача переросла первоначальную оценку в пару спринтов. Или, наоборот, написали и всё до сих пор работает.