Lago для биллинга подписок: тарифы усложняются — и начинается интересное
Установка Lago занимает вечер: docker compose up, пара запросов к API — создать billable metric, план с фиксированной ценой, подписать клиента, отправить тестовое событие — и вот уже в интерфейсе лежит первый корректно рассчитанный счёт. Ощущение, что биллинг подписок закрыт раз и навсегда, возникает быстро и обманчиво. Реальная сложность начинается через несколько месяцев, когда простой тариф превращается в план с несколькими метриками использования, клиент посреди периода переходит на тариф выше, платежи должны реально списываться с карты, а не рисоваться в тестовом режиме, и кто-то из клиентов требует частичный возврат за неиспользованные дни. Быстрый старт Lago ничего из этого не описывает — не потому что документация плохая, а потому что это разные по сложности задачи, и вторая начинается ровно там, где заканчивается первая.
Содержание
- С чем Lago действительно легко: быстрый старт и простая модель
- Модель Lago изнутри: события, метрики, начисления
- Когда один тариф превращается в несколько метрик
- Апгрейд и даунгрейд посреди периода: proration в Lago
- Интеграция с реальным платёжным шлюзом
- Возвраты и корректировки: edge-кейсы, которые быстрый старт не готовит
С чем Lago действительно легко: быстрый старт и простая модель
Lago — open-source платформа биллинга подписок и usage-based тарификации: то, что в SaaS-мире закрывают Stripe Billing или Chargebee, только на своей инфраструктуре и без процента с оборота в пользу третьей стороны. Под капотом — Rails API, Postgres как основное хранилище, Redis под очереди и кэш, плюс Clickhouse в конфигурациях с высоким потоком событий (набор сервисов стоит сверять с актуальным docker-compose.yml — у проекта он меняется; всё ниже актуально на конец августа 2026 года, и версии стоит перепроверить перед установкой).
Минимальный рабочий цикл выглядит так:
git clone https://github.com/getlago/lago.git
cd lago
docker compose up -d
После поднятия стека — организация, API-ключ, и три сущности, из которых строится вся модель: billable metric (что измеряем, например код события api_call с типом агрегации count_agg), plan (фиксированная часть amount_cents плюс список начислений charges, каждое привязано к своей metric) и subscription — привязка клиента (external_customer_id) к плану.
Событие использования отправляется POST-запросом:
curl -X POST https://your-lago-instance/api/v1/events \
-H "Authorization: Bearer $LAGO_API_KEY" \
-d '{"event": {"transaction_id": "evt_001",
"external_customer_id": "cust_123", "code": "api_call"}}'
На плоском фиксированном тарифе без метрик использования всё работает предсказуемо: клиент подписан, в конце периода Lago сам генерирует счёт на фиксированную сумму. Именно этот сценарий демонстрирует быстрый старт — и он создаёт ложное ощущение, что дальше будет так же просто.
Модель Lago изнутри: события, метрики, начисления
Прежде чем говорить, что усложняется, зафиксируем словарь.
Событие (event) — атомарный факт использования: кто, что и сколько. У него есть code (какую метрику затрагивает), external_customer_id и произвольные properties для числовых значений агрегации (например, {"duration_seconds": 42}).
Billable metric — правило агрегации событий с одним code за период. Типы агрегации, с которыми реально придётся работать:
| Тип агрегации | Что считает | Типичный кейс |
|---|---|---|
count_agg | количество событий | число API-вызовов |
sum_agg | сумма значения из properties | суммарный трафик в ГБ |
max_agg | максимальное значение за период | пиковое число активных подключений |
unique_count_agg | число уникальных значений поля | количество уникальных активных пользователей |
weighted_sum_agg | сумма, взвешенная по времени действия значения | занятое хранилище, которое меняется в течение периода |
Charge — то, как метрика превращается в деньги внутри плана: модель цены (standard — фиксированная цена за единицу, graduated — ступенчатая с разными ценами по диапазонам, volume — цена всего объёма определяется диапазоном, в который он попал, package — цена за пакет из N единиц, percentage — процент от суммы). Перепутать graduated с volume на этапе настройки — частая ошибка: диапазоны настраиваются визуально похоже, а итоговая сумма считается принципиально по-разному.
Это плоская, понятная модель, пока у плана одна метрика и одна модель цены. Проблемы начинаются, когда бизнес растёт и от одного тарифа требуют больше, чем он изначально должен был делать.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPS под LagoКогда один тариф превращается в несколько метрик
Типичный путь SaaS: плоский тариф → добавление лимитов (сиденья, API, хранилище) → каждый лимит превращается в метрику с ценой сверх включённого объёма. В Lago это план с несколькими charges, каждый на свою billable metric:
{
"plan": {
"name": "Growth",
"amount_cents": 4900,
"interval": "monthly",
"charges": [
{ "billable_metric_code": "api_call", "charge_model": "graduated",
"graduated_ranges": [
{ "from_value": 0, "to_value": 10000, "per_unit_amount": "0" },
{ "from_value": 10001, "to_value": null, "per_unit_amount": "0.001" }
]
},
{ "billable_metric_code": "storage_gb", "charge_model": "package",
"package_size": 10, "amount_cents": 500 }
]
}
}
На бумаге это выглядит как «просто добавить ещё один charge» — на практике здесь несколько граблей, которые быстрый старт не показывает, потому что там был один charge и тестировать было нечего:
- Комбинаторика тестирования растёт нелинейно. Три метрики с ветвлением по диапазонам — это не три отдельных теста, а сетка сочетаний: мало API-вызовов при большом хранилище, обе метрики у верхней границы в день закрытия периода. QA-чеклист на одну метрику не масштабируется на три простым умножением — растёт число пограничных случаев, которые нужно проверять руками или скриптом.
- Lago не считает метрики друг относительно друга. Если тарифная логика требует «бесплатных вызовов API, пока не превышено хранилище X» — кросс-метрической формулы в модели биллинга нет. Такую логику нужно решать до отправки события: на стороне приложения посчитать, к какой метрике и с каким значением отнести факт использования, и уже это отправлять в Lago как единственный источник истины.
- Free units и minimum commitment взаимодействуют неочевидно. Первый диапазон
graduatedс нулевой ценой — бесплатный лимит, но если на план наложенminimum_commitment(гарантированная минимальная сумма счёта), клиент, не выбравший лимит целиком, всё равно платит минимум — разницу нужно объяснять в интерфейсе своего продукта, потому что счёт Lago сам её не объясняет. - Группировка метрики по измерениям (grouped charges) — например, разная цена
api_callпоproperties.region— добавляет уровень конфигурации внутри charge. Забытое полеregionв части событий тихо ломает агрегацию именно для этой подгруппы клиентов, и заметно это обычно только при сверке счёта постфактум.
Практический вывод: как только план получает вторую метрику, стоит завести отдельный staging-инстанс с тестовыми клиентами и прогонять сценарии скриптом, который отправляет события пачками и сверяет итоговый счёт с расчётом, сделанным независимо — иначе ошибка агрегации всплывает не на тесте, а в реальном счёте клиента.
Апгрейд и даунгрейд посреди периода: proration в Lago
Смена тарифа не в начале периода, а посередине — место, где быстрый старт молчит совсем: в демо-сценарии клиент подписался один раз и получил один счёт.
Апгрейд по умолчанию — немедленный и пропорциональный. Когда подписка переключается на план дороже через API (PATCH /subscriptions/:id с новым plan_code), Lago завершает текущую подписку и создаёт новую с того же момента, рассчитывая prorated-сумму за оставшиеся дни старого плана и добавляя пропорциональную часть нового — это отражается в следующем счёте. Логика рабочая, но два нюанса ловят в проде регулярно:
- Проверяйте параметр
billing_timeплана —anniversary(период от даты подписки клиента) илиcalendar(все клиенты выставляются в календарные даты, например 1 числа). Апгрейд между планами с разнымbilling_timeсдвигает дату следующего счёта не туда, куда интуитивно ожидается — это стоит проверить в песочнице до того, как увидит первый живой клиент. - Proration честно считается по фиксированной части плана, но usage-based charges за текущий незакрытый период по умолчанию учитываются целиком, а не пропорционально: если клиент много использовал старый тариф и в середине месяца перешёл на новый, эта часть посчитается по цене старого плана. Логично с точки зрения учёта, но неочевидно, если не свериться заранее с документацией именно по этому поведению.
Даунгрейд по умолчанию откладывается до конца периода, а не применяется немедленно — Lago не выставляет отрицательный счёт (возврат разницы) автоматически. Разумно с точки зрения избежания споров о деньгах, но саппорт должен заранее знать: жалоба клиента «даунгрейд не сработал сразу» — это не баг, а осознанное поведение платформы, которое стоит объяснять на уровне интерфейса своего продукта.
Тестировать биллинговые периоды сложно без встроенных тестовых часов. В отличие от некоторых SaaS-платформ биллинга с функцией «сдвинуть время и посмотреть, как сработает закрытие периода», в самостоятельно развёрнутом Lago для проверки границы периода придётся либо заводить тестовые подписки с коротким интервалом, либо манипулировать датами через API/базу в тестовом окружении. Без этого первая проверка proration происходит на реальном клиенте в конце реального месяца — слишком поздно, чтобы быстро откатить ошибку конфигурации.
Интеграция с реальным платёжным шлюзом
Lago сам не проводит платежи — он считает, что клиент должен, и генерирует счёт. Списывает деньги подключённый платёжный провайдер (среди интеграций Lago — Stripe, GoCardless, Adyen через раздел payment providers). Разница между «счёт посчитан правильно» и «деньги реально списаны» — отдельный контур, и именно здесь быстрый старт с тестовыми ключами расходится с боевым запуском.
Что меняется при переходе с тестового режима на боевой:
- Тестовые ключи не ловят реальные отказы. В sandbox-режиме карты почти всегда «проходят» по заранее известным тестовым номерам. В проде впервые видите разнообразие отказов: недостаточно средств, карта заблокирована банком, нужна дополнительная аутентификация (3-D Secure/SCA) — на каждый случай нужна логика: повторить попытку, уведомить клиента, приостановить доступ, запустить dunning-цепочку.
- Webhook работает в обе стороны, и обе стороны нужно защищать. Lago шлёт вебхуки о событиях (счёт создан, платёж прошёл/не прошёл) — нужна проверка подписи, иначе кто угодно отправит поддельное «платёж прошёл». Провайдер, в свою очередь, шлёт вебхуки о статусе платежа обратно — и если интеграция не идемпотентна, повторная доставка события (at-least-once — нормальное поведение очередей, а не баг) приводит к двойной обработке платежа. Подробнее о том, почему повтор запроса ломает деньги — в статье про идемпотентность в биллинге.
- Валюта и локальные особенности платежей. Проверьте заранее, как провайдер и Lago обрабатывают конвертацию и какие валюты вообще поддерживает связка — несовпадение валюты плана и того, что провайдер принимает у клиента, всплывает только на попытке первого реального списания.
- Ручное вмешательство неизбежно. Даже с настроенной интеграцией часть счетов требует ручной обработки: карта отклонена окончательно, платёж завис в статусе «в обработке». Нужен хотя бы регламент для операционной команды — Lago покажет статус счёта, но не примет решение за вас.
Если нужны не полноценные usage-based подписки, а просто выставление счетов и приём разовых или регулярных платежей без агрегации метрик, посмотрите на инструмент попроще — Invoice Ninja закрывает счета, клиентов и оплату без модели событий, и для части сценариев это буквально меньше движущихся частей.
Возвраты и корректировки: edge-кейсы, которые быстрый старт не готовит
Быстрый старт показывает путь «событие → счёт → оплата». Реальная эксплуатация регулярно идёт в обратную сторону: счёт нужно скорректировать после того, как деньги уже (или почти) списаны.
- Ошибочно посчитанное использование. Баг в продукте задублировал события — счёт может быть уже выставлен клиенту к моменту, когда это заметили. Прямое редактирование прошедшего счёта не предусмотрено — правильный путь это кредит-нота (credit note) на сумму ошибки, корректирующая баланс, но не отменяющая автоматически сам факт списания у провайдера, если оно уже прошло. Возврат реальных денег — отдельное действие, которое кредит-нота сама не делает; проверяйте это поведение под свою интеграцию заранее.
- Частичный возврат за неиспользованный период. Клиент отменяет подписку на 10-й день из 30 и просит вернуть разницу — логика не встроена как «один клик»: нужно посчитать пропорциональную сумму и оформить кредит-ноту или возврат через провайдера отдельно. Если сценарий частый, стоит написать небольшой сервис поверх API Lago, а не решать вручную каждый раз.
- Купоны и скидки задним числом. Применение купона на уже сформированный счёт работает иначе, чем на будущий — нужно понимать точный порядок операций (кредит-нота либо купон до генерации следующего счёта), иначе клиент увидит скидку не там, где ожидал.
- Предоплаченные балансы (wallets). Если модель предполагает баланс, из которого списывается использование, в Lago есть сущность wallet с top-up — но сочетание wallet и usage-based charges у одного клиента требует отдельного тестирования: порядок списания (сначала баланс, потом карта, или наоборот) нужно проверить на реальном сценарии.
- Спорные события требуют аудита, а не веры на слово. Когда клиент оспаривает сумму, нужна возможность показать сырые события, из которых она сложилась — не удаляйте и не перезаписывайте их даже после оплаты: это единственный источник правды для разбора спора спустя недели или месяцы.
Ни один из этих кейсов не баг Lago — это нормальная сложность биллинга, которая не помещается в happy path быстрого старта. Прежде чем расширять тарифную сетку, стоит трезво прикинуть экономику самостоятельного размещения такой системы — статья про экономику self-hosted разбирает критерии, по которым решают, держать ли критичный для денег сервис у себя.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPS под LagoНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Lago подходит для простого SaaS с одним фиксированным тарифом?
Да, в этом сценарии он справляется без всех сложностей выше — просто плоский план без charges. Но если usage-based тарификация уже видна в дорожной карте, разумнее заложить архитектуру событий с самого начала, а не переделывать позже под нагрузкой.
Можно ли протестировать proration без ожидания реального месяца?
Встроенного «ускорения времени» в Lago нет. На практике тестируют короткими интервалами на тестовом окружении или прямой манипуляцией датами через API/базу в изолированном стенде — но не в продакшене.
Lago сам возвращает деньги клиенту при отмене подписки?
Нет — он может сформировать кредит-ноту, корректирующую баланс внутри системы, а фактический возврат через провайдера — отдельное действие, инициируемое самостоятельно или через свою автоматизацию.
Что если вебхук от провайдера о статусе платежа придёт дважды?
Зависит от вашего кода, а не от Lago — если обработчик не идемпотентен, повторная доставка приводит к двойному списанию или задвоенным уведомлениям. Стандартная задача при интеграции с любым платёжным webhook, а не особенность именно Lago.
Стоит ли начинать с составных тарифов сразу?
Не обязательно — сложная модель с самого начала означает больше багов и дольше тестирование там, где клиентам пока нужен только простой тариф. Разумнее простая модель на старте с архитектурой событий, допускающей добавление метрик позже.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →