PayBot — Kaspi Pay без боли

SaaS-надстройка над Kaspi Pay API: платёжные ссылки, QR и инвойсы за минуты. Архитектура, цифры и разбор боевого инцидента.

PayBot — SaaS-надстройка над Kaspi Pay API для бизнеса: платёжные ссылки, QR и инвойсы за минуты, без интеграции с банком напрямую. Клиент получает API и веб-панель, деньги идут через Kaspi на его счёт. Тарифная линейка — от базовой до Enterprise без лимитов.

Технически это FastAPI-бэкенд и Next.js-фронтенд, PostgreSQL 16 и Redis, развёрнутые в нашем k3s-кластере за Cloudflare. Образы собираются kaniko и деплоятся из внутреннего реестра; фоновые задачи опрашивают Kaspi и доставляют вебхуки. В июле 2026 сервис переехал со standalone-VPS в кластер без потери трафика.

Цифры

МетрикаЗначение
Транзакций в месяц48 000+
Мерчантов12
Uptime99.7%
Среднее время ответа API180 мс

Что сломалось и как чинили

Через несколько часов после переезда в кластер create-эндпоинты начали отдавать 502, а Enterprise-клиенты пожаловались на «лимит 1 QR в минуту», которого на их тарифе быть не должно. Причины оказались независимыми, и обе — хороший урок.

Первая: конфигурация. На старом сервере переменные окружения загружал systemd, в Kubernetes же .env монтировался файлом и до os.environ не доходил — часть параметров интеграции с Kaspi тихо откатилась на устаревшие значения по умолчанию, и Kaspi начал отвергать обновление сессий. Существующие сессии продолжали работать, поэтому падала только часть запросов — самый неприятный для диагностики режим. Лечение: закрепить критичные параметры как env контейнера в манифесте; после выката ошибки ушли в ноль.

Вторая: логика лимитов. В эндпоинтах ручного создания платежа в веб-панели cooldown по IP проверялся и ставился до загрузки данных клиента — тариф на этот момент был неизвестен, и освобождение для Enterprise просто не срабатывало. Исправили порядок: сначала загружаем клиента, затем применяем cooldown только для не-Enterprise тарифов. Отдельным хвостом позже поймали третий сценарий — вытеснение Kaspi-сессии входом с другого устройства маппилось в невнятный 502; теперь это явный 409 с сообщением «переподключите Kaspi-аккаунт». Полный разбор — в блоге.