Fintech backend-система

Pay Hub: контроль платежных событий и уведомлений

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

Проблема

В fintech-сценариях события оплаты и уведомления быстро становятся критической точкой. Как только на проекте появляется больше одного платёжного провайдера — например, Click, Payme и Uzum одновременно — команда обычно получает не одну систему, а несколько параллельных интеграций с разным форматом ответа, разной логикой webhook'ов и разными способами узнать, прошёл платёж или нет.

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

Единый API поверх нескольких платёжных шлюзов

Вместо того чтобы каждый раз встраивать в проект отдельный SDK под каждый платёжный шлюз, PayHub сводит Click, Payme, Uzum и другие способы оплаты в одну систему с единым API для обработки платежей. Для остальной части продукта — сайта, бота, админки — не важно, через какой именно шлюз прошла оплата: наружу отдаётся один предсказуемый контракт.

Это тот же принцип, что применяется в PayTutor на уровне одной mock-страницы оплаты, но здесь он вынесен на уровень отдельного backend-хаба: если завтра появится ещё один локальный платёжный провайдер, его можно подключить внутри PayHub, не переписывая интеграцию в каждом продукте, который уже им пользуется.

Технологический стек

Backend собран на Laravel 10, интерфейс демо-стенда — на JavaScript, деплой — на Cloudflare Pages с CDN и Functions. В качестве базы данных на демо-стенде используется SQLite — сознательно лёгкое временное решение для публичного показа, а не production-конфигурация с полноценным сервером БД.

Redis для кэширования и очередей отмечен в стеке отдельно, с пометкой «потом» — то есть заложен как следующий шаг для высоконагруженного сценария, но не подключён в текущей демо-версии. PHPUnit закрывает тестирование backend-логики: платёжный хаб — то место, где логические ошибки дороже всего, поэтому тестовое покрытие здесь не опциональная деталь.

Защита от дублей и расчёт на высокую нагрузку

Каждый webhook от платёжного шлюза проверяется подписью HMAC-SHA256, что закрывает вопрос: тот ли это запрос, который действительно прислал платёжный провайдер, а не кто-то, кто просто знает URL эндпоинта. Отдельно реализована защита от дублирования платежей и идемпотентность операций — если один и тот же webhook придёт повторно, система не обработает его как второй платёж.

Обработка платёжных событий сделана асинхронной и рассчитана на горизонтальное масштабирование — то есть архитектурно система готова к тому, что нагрузка вырастет не постепенно, а скачком, как часто бывает у fintech-продуктов в моменты пиковых продаж.

Публичный демо-стенд: инициировать платёж и увидеть ответ сервера

Поскольку сам PayHub — приватный backend-хаб, для публичного показа собран отдельный демо-интерфейс: форма «Инициировать платёж» с полями User ID, сумма в UZS (с минимумом в 1000 UZS), выбором платёжной системы и необязательным описанием. Рядом — блок «Статус системы» с индикатором доступности API и список реальных API-эндпоинтов: `POST /api/payments/init`, `POST /api/payments/webhook` и mock-эндпоинт `GET /api/mock/webhook` для тестирования без реального платёжного шлюза.

После инициации платежа демо-стенд показывает необработанный JSON-ответ сервера — с `transaction_id` и статусом `pending` — вместо того, чтобы прятать техническую часть за красивым экраном. Это осознанный выбор для кейса, ориентированного на техническую аудиторию: видно не картинку «платёж прошёл», а реальный контракт API, которым будет пользоваться разработчик на стороне клиента.

Тестер webhook'ов: имитация уведомления от платёжной системы

Чтобы показать вторую половину платёжного цикла — не только инициацию, но и приём уведомления об оплате, — на демо-стенде есть отдельный «Тестер Webhook'ов»: поле с ID транзакции, выбор статуса (например, Completed) и кнопка «Отправить Webhook», которая имитирует запрос от платёжной системы на mock-эндпоинт.

Рядом есть кнопка «Быстрый тест» → «Тестовый платёж», которая сама заполняет форму тестовыми данными — так весь цикл «инициировать → получить webhook → увидеть статус» можно пройти за несколько кликов, не вводя вручную тестовые ID и суммы каждый раз.

Результат

События платежей проходят по единому предсказуемому контуру: один API поверх нескольких шлюзов, подписанные и защищённые от дублирования webhook'и, асинхронная обработка с расчётом на рост нагрузки. Бизнес получает меньше ручных проверок и более прозрачную реакцию на сбои, а разработчик — понятный контракт API вместо серии частных интеграций под каждый платёжный шлюз.

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

Другие кейсы