Как я построил автоматический сервис защиты данных с нуля

Идея простая: человек регистрируется, оплачивает, получает ключ на почту и сразу подключается. Никакой ручной работы с моей стороны. Расскажу как это устроено под капотом.

Стек и почему именно он

Backend: FastAPI (Python)Выбрал за нативную async поддержку — платёжные вебхуки, запросы к VPN API и отправка писем должны работать параллельно без блокировок. SQLAlchemy async + PostgreSQL для хранения пользователей, подписок и платежей.

Frontend: React + Vite + TailwindSPA с несколькими страницами: лендинг, личный кабинет, оплата, помощь. Vite даёт быструю сборку, Tailwind — быструю вёрстку без написания CSS с нуля.

VPN-backend: Marzban Готовая панель управления XRay с REST API. Не стал писать управление XRay вручную — Marzban уже умеет создавать пользователей, выдавать ключи, считать трафик. Интегрируется через обычный HTTP.

Инфраструктура: Docker Compose + Nginx + CloudflareНесколько контейнеров за реверс-прокси. Cloudflare перед всем — скрывает реальный IP серверов.

Marzban интеграция

Самая интересная часть. После подтверждения оплаты backend автоматически создаёт пользователя в Marzban:

asyncdefcreate_user(self, username:str, months:int)->dict: expire_ts = _months_to_timestamp(months) payload ={"username": username,"proxies":{"vless":{"flow":"xtls-rprx-vision"}},"inbounds":{"vless":["VLESS Reality"]},"expire": expire_ts,"data_limit":0,} resp =await client.post(f"{MARZBAN_URL}/api/user", json=payload)return resp.json()

Marzban возвращает готовый vless://... ключ который пользователь вставляет в AmneziaVPN или Hiddify. Токен Marzban кешируется на 55 минут чтобы не делать лишних запросов.

Автоматическая доставка ключа

После активации платежа:

  1. Создаём пользователя в Marzban → получаем vless:// ключ
  2. Сохраняем подписку в PostgreSQL
  3. Отправляем письмо через Resend с ключом и пошаговой инструкцией

Письмо содержит три шага прямо в теле — скачать приложение, скопировать ключ, вставить. Конверсия выросла после того как добавили инструкцию прямо в email вместо просто ссылки на сайт.

Триал: 3 дня без карты

Хотел дать пробный период без привязки карты. Реализовал через верификацию email:

  1. Регистрация → письмо со ссылкой подтверждения
  2. Пользователь кликает → создаётся пробная подписка на 3 дня в Marzban
  3. Письмо с ключом приходит автоматически

Защита от злоупотреблений — поле trial_used в модели пользователя. Один email — один триал.

Подводные камни

Идемпотентность вебхуков — платёжные системы при таймауте повторяют запрос. Без проверки на дубли получал несколько одинаковых писем и задвоенные подписки. Решение: проверка статуса платежа перед активацией.

Cloudflare + IP-whitelist — после подключения Cloudflare вебхуки от платёжной системы перестали проходить проверку IP, потому что запросы приходят с IP Cloudflare. Перенёс проверку в backend через заголовок CF-Connecting-IP.

Сетевая связность в Docker — backend-контейнер не может достучаться до сервисов на localhost хоста. Решение: socat проксирует трафик через bridge-интерфейс Docker.

Итог

Полный путь от регистрации до работающего ключа занимает несколько секунд после оплаты. Ручной работы ноль.

Из неочевидного: самые сложные места — идемпотентность вебхуков и сетевая связность между контейнерами и внешними сервисами.
Здесь можете посмотреть что из этого получилось - https://data-shield.ru