🛡

Tech Poly VPN: биллинг VPN-подписок в Telegram

собственный продукт: оплата по СБП, выдача ключей в Marzban, резервный бот во ВКонтакте

Telegram-бот продаёт подписки на VPN: оплата по СБП, выдача и продление ключей в панели Marzban, промокоды, рефералы, корпоративные группы и акты сверки без ручной работы админа. Python, aiogram 3, MariaDB.

В продакшене · собственный продукт
12 ч
пробный доступ по умолчанию: пользователь получает ключ сам, без администратора
2 канала
продажа и выдача ключей в Telegram, резервная выдача конфигов во ВКонтакте
1-го числа
акты сверки корпоративным клиентам формируются и рассылаются автоматически

Задача

VPN-доступ продаётся подписками, и каждый шаг легко превращается в ручную работу: выдать пробный ключ, принять оплату, сверить чек, продлить срок в панели, напомнить об истечении, посчитать скидку. Ошибка в любом звене означает либо клиента без доступа, либо доступ без оплаты.

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

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

Решение

Tech Poly VPN (бот polybot) — собственный продукт студии. Это асинхронный Telegram-бот, который продаёт подписки, выдаёт ключи VLESS через панель Marzban и сам ведёт учёт денег и сроков. Что реализовано:

  • вход по инвайт-коду или корпоративному промокоду и пробный доступ на несколько часов (по умолчанию 12);
  • заявки на оплату по СБП с ручной проверкой чека администратором и атомарным подтверждением без двойного начисления;
  • создание и продление пользователя в Marzban, откат заявки в ожидание, если панель не ответила;
  • цены по группам, промокоды двух типов (процент и дни), реферальная скидка с потолком;
  • корпоративный контур: группы, роль реселлера, ключи сотрудников без привязки к Telegram, журнал начислений, акт сверки;
  • уведомления об истечении подписки с «тихими часами» пользователя;
  • резервный канал во ВКонтакте: выдача конфигов при недоступном Telegram;
  • мониторинг трассы, ежедневные резервные копии, рассылки, админ-панель, статистика и лог действий;
  • скрытая команда /version только для администраторов.

Как это устроено

Marzban API: бот как источник истины по деньгам

Marzban — панель управления VPN-узлом: она создаёт пользователей и отдаёт трафик. Бот управляет ею через REST API, а сам хранит деньги, сроки, скидки и отчётность. Срок подписки лежит в двух местах: expire_date в базе бота и expire в Marzban, который реально отключает трафик. Запись идёт всегда в одну сторону: сначала Marzban возвращает фактическую дату, и только она записывается в базу. Поэтому бот не «считает подписку активной», когда трафик уже закрыт. В поле заметки пользователя в панели бот пишет @username, чтобы администратор видел людей, а не безликие идентификаторы.

Идемпотентный платёж: блокировка строки при подтверждении

Идемпотентность — свойство операции, при котором повтор не меняет результат. Здесь оно нужно, чтобы два администратора, одновременно нажавшие «Подтвердить», не начислили оплату дважды. Бот открывает транзакцию, читает платёж через SELECT ... FOR UPDATE и проверяет статус под этой блокировкой. Если статус уже не «ожидает», транзакция откатывается, а администратор видит, кто и когда обработал заявку. Гонка исключена на уровне базы данных, а не «договорённостью в коде».

Откат при сбое панели и скидка после выдачи

Если платёж уже помечен оплаченным, а Marzban не ответил, вызывается отдельная функция возврата заявки в ожидание. Скидка списывается только после успешного ответа панели, поэтому сорвавшийся платёж не сжигает накопленную скидку. Цена фиксируется в журнале начислений на дату операции: изменение тарифа группы не переписывает историю и не ломает уже выставленные акты.

Корпоративные группы и акты сверки

Роль реселлера вынесена в отдельную таблицу, а не в поле пользователя: директор клиента может вообще не пользоваться сервисом. Реселлер выпускает ключи сотрудникам с меткой вроде «Бухгалтерия, ПК-2», регистрация в боте не нужна. Журнал начислений только пополняется, записи не переписываются. По нему бот считает период и закрывает его, а акты рассылает автоматически 1-го числа. Ручной вариант — команда /act.

Резервный бот ВКонтакте

В расширенном стеке рядом с Telegram-ботом работает бот ВКонтакте на LongPoll (получение сообщений без публичного адреса-приёмника). Аккаунт привязывается одноразовым кодом с ограниченным сроком, есть учёт использованных пробных периодов и восстановление доступа по старой ссылке. Оба бота используют общие модули клиента Marzban и политик повторов. Клиенту выдаётся ссылка на подписку, а не «сырой» ключ; в админке у пользователя есть QR-код.

Эксплуатация: мониторинг, резервные копии, повторы запросов

Задачи планировщика APScheduler работают по московскому времени. Мониторинг проверяет TCP-порт узла каждые 5 минут и оповещает администратора только после нескольких подряд неудачных проверок: одиночное сетевое моргание его не будит. Резервная копия в JSON создаётся ежедневно, хранится с ротацией и может загружаться в Google Drive. В расширенном стеке повторы запросов к базе, Marzban, Telegram и ВК идут с нарастающей паузой, а контейнеры останавливаются мягко.

Результат

  • Пробный ключ, покупка и продление проходят без ручного вмешательства, кроме подтверждения чека.
  • Повторное подтверждение платежа вторым администратором не начисляет оплату дважды.
  • Сбой панели не оставляет платёж без ключа: заявка возвращается в очередь.
  • Компания получает ключи сотрудникам без Telegram-аккаунтов и акт сверки по журналу начислений раз в месяц.
  • Клиент, которому недоступен Telegram, получает конфиг через резервный канал ВКонтакте.
  • Автоматических тестов в репозитории нет, приёмка ручная.

Технологии: что и зачем

  • Python и aiogram 3 — асинхронный Telegram-бот: меню, платежи, админ-панель.
  • MariaDB (InnoDB) — подписки, платежи, промокоды, журнал начислений; транзакции и блокировка строк дают защиту от двойного начисления.
  • Marzban API — создание, продление и подписка пользователей VLESS; сама панель и узлы разворачиваются отдельно.
  • APScheduler — проверка истекающих подписок, мониторинг, резервные копии, акты сверки.
  • VK API — резервный канал выдачи конфигов.
  • Google Drive (PyDrive2) — необязательное хранилище резервных копий.
  • Docker — образ бота с заданным часовым поясом; в расширенном стеке общий docker-compose.

Статус

Версия 1.0.1 от 30 сентября 2026 года: очищенный код бота с версией в логе при старте. Бот работает в продакшене как собственный продукт. Расширенный стек с ВК-ботом и общим docker-compose ведётся в отдельном репозитории: там указаны polybot 3.5.0 и vkbot 1.2.0. Дальше по списку доработок из README: автотесты, шаблон окружения для ВК-бота, вынос платёжных реквизитов из кода в переменные окружения и отдельный инструмент миграций схемы.

Вопросы по проекту

Как продавать VPN через Telegram-бота с оплатой по СБП?
Пользователь нажимает «Купить», платит по СБП и присылает чек. Чек уходит администраторам с кнопками «Подтвердить» и «Отклонить». После подтверждения бот сам создаёт или продлевает пользователя в панели Marzban и отправляет ключ со сроком действия.
Может ли платёж начислиться дважды, если два админа нажмут одновременно?
Нет. Статус платежа читается внутри транзакции с блокировкой строки в базе (SELECT ... FOR UPDATE). Второй администратор получает сообщение, кто и когда уже обработал заявку, а подписка не продлевается повторно.
Что будет, если панель Marzban не ответила после подтверждения платежа?
Заявка возвращается в статус ожидания, деньги не теряются, а накопленная скидка списывается только после успешной выдачи доступа. Пользователь не остаётся с оплатой без ключа.
Как организовать корпоративный VPN для сотрудников, у которых нет Telegram?
Для компании заводится корпоративная группа со своей ценой и роль реселлера. Реселлер выпускает ключи сотрудникам с человекочитаемой меткой, регистрация в боте им не нужна. Все начисления пишутся в журнал, по которому бот 1-го числа месяца формирует акт сверки.
Что делать, если Telegram недоступен?
Есть резервный канал выдачи конфигов через бота ВКонтакте. Аккаунт привязывается одноразовым кодом, там же доступен пробный период. Резервный бот входит в расширенный стек с общим docker-compose.
Где хранятся данные и что происходит при сбоях?
Подписки, платежи и журнал начислений лежат в MariaDB, бот работает в Docker. Резервные копии делаются ежедневно локально и, если включено, на Google Drive, а финальная копия создаётся при штатной остановке. Мониторинг трассы сообщает администратору только после нескольких неудачных проверок подряд.

Нужно похожее?

Расскажите о задаче — покажем, как решали такое, и прикинем объём работ.