Тема
Платёж картой server-to-server
Это базовый сценарий процессинга: мерчант присылает карточные данные своим сервером, мы проводим платёж через банк-эквайер и сообщаем результат. Все остальные флоу — платёж через нашу форму, крипто-онрамп, ARS-переводы — устроены как надстройка над этим путём и описываются как отличия от него. Прочитайте эту страницу целиком, прежде чем идти в любую другую из раздела.
Когда применяется
Мерчант принимает карту сам: у него своя платёжная форма и собственная PCI DSS-сертификация, позволяющая держать карточные данные у себя. Он отправляет нам номер карты, срок и CVV в обычном серверном запросе. Если такой сертификации нет, мерчант использует нашу hosted-форму и карту вместо него собираем мы.
Кто участвует
| Сервис | Роль в этом флоу |
|---|---|
api-gateway-pci | Единственная точка, куда попадают голые карточные данные; токенизирует карту |
tokenization-service | Хранит карту и выдаёт токены; умеет вернуть данные обратно внутренним сервисам |
superpos-core | Создаёт транзакцию, выбирает гейт, ведёт статусы, считает деньги |
antifraud | Решает, пропускать ли платёж, до похода в банк |
drivers | Говорит с конкретным банком на его языке |
superpos-callback | Доставляет результат мерчанту |
Как это выглядит целиком
Шаг за шагом
1. Мерчант отправляет запрос
Публичное описание запроса — Create direct payment. Запрос уходит на PCI-домен (chd-api), а не на обычный домен merchant API: всё, что содержит номер карты, принимает только PCI-шлюз. В теле — сумма, валюта, идентификатор заказа на стороне мерчанта, данные плательщика и карта.
Мерчант аутентифицируется одним из двух способов: заголовком Basic с парой «ключ мерчанта — секрет» либо подписью. Подпись считается по значениям полей тела, отсортированным по именам и склеенным через вертикальную черту. Проверяет подпись не сам шлюз, а сервис авторизации. Подробности — в Аутентификация и подписи.
Параллельно работает ограничение по адресам, с которых мерчанту разрешено к нам обращаться. Оно устроено в два уровня, и отклонить запрос сейчас может только внешний — подробности в Аутентификации и подписях.
2. Карта превращается в токены
PCI-шлюз сразу же отдаёт карточные данные в сервис токенизации и получает два разных токена:
- токен карты — постоянный, хранится в PostgreSQL в зашифрованном виде, одинаковый для одной и той же карты между платежами;
- токен сессии — временный, живёт в Redis около часа и содержит в том числе CVV, который нельзя хранить постоянно.
Дальше по системе едут токены и замаскированный номер карты. Голый номер за пределы PCI-контура не выходит. При этом токен обратим: ядро и драйверы запрашивают данные обратно, когда нужно отправить их в банк — см. Карточные данные и токены.
3. Ядро создаёт транзакцию
Ядро заводит транзакцию в статусе NEW, привязывает её к счёту мерчанта, считает комиссию и проверяет ограничения: лимиты по сумме и частоте, чёрные и белые списки, доступность выбранного метода. Именно transaction — центральная сущность системы; почти всё в базе данных ядра вращается вокруг неё.
4. Антифрод
До похода в банк платёж проверяет антифрод: правила, скоринг, отпечаток браузера и данные плательщика. Ответ — не «да/нет», а статус: доверенный платёж либо блокировка по правилам, по отпечатку или по скорингу. Ядро превращает блокировку в отказ с соответствующей категорией ошибки. Если внешний скоринг недоступен, система не останавливает платёж, а продолжает без него. Точки вызова перечислены в Антифрод в платёжном флоу.
5. Выбор гейта
Гейт — это настроенное подключение к конкретному банку с валютой, лимитами и секретами. У счёта мерчанта может быть либо один гейт, либо балансировщик — каскад из нескольких. Балансировщик перебирает гейты по очереди, отсеивая неподходящие валидаторами, и останавливается на первом, через который можно провести платёж. Здесь же работают A/B-эксперименты и проверочные платежи для новых карт. Если подходящего гейта нет, платёж отклоняется, не дойдя до банка. Механика — в Как выбирается банк.
6. Поход в банк
Ядро вызывает сервис драйверов по HTTP: тело запроса подписывается ключом KMS, в нём — имя драйвера, ссылка на секреты гейта и транзакция. Драйвер переводит это в формат конкретного банка и делает запрос к его API. Драйверов больше сотни, но интерфейс у всех один, поэтому ядро не знает, с кем именно говорит. Подробности — в Слой драйверов.
Транзакция переходит в статус IN_PROCESS: с этого момента мы ждём внешнюю систему.
7. Ответ мерчанту и ссылка на 3DS
Банк почти никогда не подтверждает платёж сразу: он требует, чтобы держатель карты подтвердил операцию на странице своего банка. Драйвер возвращает ядру поля обработки, среди которых — адрес или готовая HTML-форма редиректа на страницу аутентификации.
Мерчант получает ответ со ссылкой и статусом «в обработке». Ссылка ведёт либо прямо на страницу банка, либо на нашу промежуточную страницу — это настройка гейта. Промежуточная страница нужна, чтобы корректно обработать возврат плательщика и варианты 3DS второй версии.
Мерчант перенаправляет плательщика по этой ссылке. Дальше некоторое время мы не управляем процессом.
8. Возврат с 3DS
Плательщик подтверждает операцию, и его браузер возвращается на наш адрес завершения аутентификации. Ядро фиксирует возврат и передаёт результат аутентификации драйверу, чтобы тот дожал платёж в банке.
Параллельно существует второй, независимый канал: банк присылает колбек на наш адрес. Он приходит в ядро, а не в сервис колбеков — этот сервис отвечает только за исходящие уведомления мерчанту. Подробности — в Колбек от банка.
Оба канала ненадёжны: плательщик может закрыть вкладку, а колбек — не дойти. Поэтому они не единственный источник истины.
9. Переспрос статуса
Главный механизм, который доводит платёж до определённости, — регулярный опрос банка. Драйвер при ответе указывает, когда спросить в следующий раз, и ядро ставит отложенную задачу в очередь. Задача дёргает драйвер, тот опрашивает банк и возвращает текущее состояние. Так система переживает и потерянный колбек, и ушедшего плательщика.
Отдельно работают таймауты: если аутентификация не завершилась за отведённое время, платёж закрывается отказом. Все очереди и таймеры перечислены в Очереди, таймеры и отложенные задачи.
10. Финализация
Как только банк дал окончательный ответ, транзакция переходит в COMPLETE или ERROR. В этот момент происходит главное для бизнеса: пересчитываются комиссии и меняется баланс счёта мерчанта — сумма попадает в удержание, откуда станет доступной по расписанию. Изменения баланса записываются отдельно, чтобы их можно было разобрать при сверке. Как устроены удержания — в Баланс, комиссии, финализация.
Терминальные статусы не переигрываются: повторный ответ банка по уже закрытой транзакции не изменит её, кроме отдельного режима принудительной перепроверки.
11. Колбек мерчанту
Финализация ставит задачу на отправку колбека в очередь, а доставкой занимается superpos-callback. Тело подписывается тем же способом, что и запросы мерчанта к нам, — по отсортированным значениям, — чтобы мерчант мог убедиться, что уведомление пришло от нас. При неудаче доставка повторяется до одиннадцати раз с растущими паузами, а все попытки логируются. Детали — в Колбек мерчанту.
Колбек — уведомление, а не гарантия: правильная интеграция мерчанта всегда дополняет его собственным запросом статуса.
Где ломается чаще всего
| Симптом | Обычная причина | Куда смотреть |
|---|---|---|
| Отказ мгновенно, без похода в банк | Не нашёлся гейт в каскаде, сработал лимит или антифрод | Категория ошибки транзакции, логи балансировщика |
| Платёж завис в обработке | Плательщик не вернулся с 3DS, колбек не дошёл | Стадия транзакции, очередь переспроса статуса |
| У нас успех, у мерчанта — нет | Колбек не доставлен или отклонён на стороне мерчанта | Лог отправок колбеков, ответы мерчанта |
| Отказ банка без внятной причины | Ошибка маппинга в драйвере | Лог взаимодействия с гейтом, сырой ответ банка |
| Расхождение сумм | Валюта счёта и валюта платежа различаются | Расчёт комиссии, курс конверсии |
Первый вопрос при разборе любого инцидента — не «что в логах», а «в каком статусе и на какой стадии остановилась транзакция»: это сразу сужает поиск до одного шага из одиннадцати.
Куда дальше
- Статусы и стадии — что означает состояние, в котором вы нашли транзакцию.
- Платёж через нашу форму — тот же путь, но карту собираем мы.
- Почему шлюзов несколько — куда какой запрос должен приходить.