Продолжение: как онлайн-эквайринг устроен «под капотом»
Продолжая тему Как работает онлайн-эквайринг и что проверить заранее, захотелось разобрать эквайринг с точки зрения архитектуры и как все работает.
Первая часть статьи разобрала пользовательский путь и типичные ошибки при подключении онлайн-эквайринга. Дальше — взгляд системного аналитика и архитектора: из каких компонентов состоит платежный контур, как данные движутся между доменами и какие технологии закрывают задачи идемпотентности, безопасности и согласованности.
Три домена платежа
Любая онлайн-оплата картой построена вокруг трехдоменной модели EMV 3-D Secure 2: домен продавца (сайт и его платежный сервер), домен взаимодействия (директория карточной платежной системы) и домен эмитента (банк, выпустивший карту, со своим сервером контроля доступа — ACS). Каждый домен владеет своими данными и не обращается напрямую к компоненту, которому не доверяет — сообщения идут по строго определенной цепочке через директорию платежной системы.

Этот контур объясняет, почему магазин физически не может получить реквизиты карты в открытом виде — данные карты видит только форма банка или платежного сервера, прошедшая сертификацию PCI DSS, а сайту передается только технический статус операции.
Frictionless и challenge: два пути внутри 3-D Secure 2
Протокол 3DS2 поддерживает два сценария аутентификации. Frictionless-путь завершается без участия пользователя: сервер эмитента (ACS) оценивает риск по данным устройства, истории покупателя и контексту заказа и сразу возвращает решение. Challenge-путь запускается, когда риск признан повышенным — тогда покупатель проходит дополнительную проверку (одноразовый код, биометрия, пароль 3-D Secure).

По статистике индустрии, через challenge-сценарий проходит лишь около 5% транзакций — остальные закрываются в фоне, что и создает у покупателя ощущение «мгновенной» оплаты. Именно это несовпадение между визуальной скоростью и реальным количеством внутренних проверок и объясняет паузу, описанную в первой части статьи.
Источник истины: почему нельзя доверять браузеру
Ключевой архитектурный принцип, вытекающий из первой части: статус оплаты нельзя определять по редиректу в браузере или query-параметрам на success-странице — единственный доверенный источник — подписанное серверное уведомление (webhook) от платежного провайдера. Redirect-урлы легко подделать или потерять при закрытии вкладки, а webhook доставляется server-to-server и содержит криптографическую подпись (HMAC), которую обязательный получатель проверяет перед обработкой.

Такая архитектура прямо снимает проблему из первой части статьи — «заказ отмечен оплаченным, а подтверждения нет»: пока webhook не подтвержден и не прошел проверку подписи, заказ остается в промежуточном статусе, а не в статусе «оплачен».
Идемпотентность: как не создать два заказа из одной оплаты
Повторные нажатия кнопки оплаты, дубли webhook-уведомлений и повторы запросов при сетевых сбоях — стандартная ситуация в платежных интеграциях, и решает ее паттерн идемпотентных ключей. Клиент генерирует уникальный идентификатор операции (обычно UUID v4) один раз на логическую операцию и передает его в заголовке запроса; сервер сохраняет пару «ключ → результат» и при повторном обращении с тем же ключом возвращает закешированный ответ, не выполняя повторно бизнес-логику.
| Компонент | Что использовать как ключ | Где хранить | TTL |
|---|---|---|---|
| Запрос создания платежа | UUID v4, привязанный к заказу | Redis / Postgres с unique-constraint | 24–72 часа |
| Webhook от провайдера | provider_event_id (id события провайдера) | Таблица processed_events | Постоянно / до архивации |
| Повторная оплата после сбоя | tenant_id + provider + provider_reference | Таблица идемпотентности, привязанная к заказу | По регламенту провайдера |

Отдельное правило для webhook-обработчика: идемпотентность строится не на сумме и не на временной метке, а на собственном идентификаторе события провайдера (provider_event_id), потому что несколько попыток оплаты с одинаковой суммой — это разные операции, а не дубликаты. Это техническое объяснение куска из первой части статьи, где «оператор видит похожие записи с разными идентификаторами» — система обязана различать эти попытки по идентификатору, а не по сумме.
Возвраты и согласованность учета
Возврат — это отдельная операция, привязанная к исходной транзакции, а не отмена или откат исходного платежа; технически это означает, что учетная система должна хранить обе записи независимо, чтобы отчетность не расходилась с историей заказа. Архитектурно это реализуется через паттерн leger (журнала движения средств): каждая денежная операция — новая неизменяемая запись, а текущий статус заказа — это агрегат (сумма/статус) над цепочкой таких записей, а не поле, которое перезаписывается.

Такая схема данных исключает ситуацию, когда «на странице покупателя все выглядит аккуратно», а в отчетности сумма расходится: итоговый статус заказа всегда пересчитывается из журнала, а не хранится в таблице как статус без итсории.
Стек технологий для платежного контура
Технологический стек под задачу онлайн-эквайринга обычно складывается из нескольких слоев: прием и валидация запросов, взаимодействие с провайдером и 3-D Secure, надежная доставка и обработка webhook, и слой хранения с гарантией согласованности.
- API-шлюз / платежный сервер: обрабатывает создание платежа, генерацию idempotency-key, редирект/iframe для 3DS challenge-формы.
- Очередь сообщений (Kafka, RabbitMQ, SQS): буферизует входящие webhook-события, разделяя быстрый ACK ответа провайдеру от медленной бизнес-обработки.
- Хранилище идемпотентности: Redis (быстрый unique-check с TTL) или PostgreSQL/MySQL с unique-constraint на ключ операции.
- Проверка подписи webhook: HMAC-SHA256 по телу запроса с общим секретом, обязательна до чтения полей payload.
- Реляционная БД с ACID-транзакциями: хранение заказов, попыток платежей и leger-записей внутри одной транзакции при обновлении статуса.
- Фоновый сверщик (reconciliation job): ежедневное сравнение отчета провайдера с локальным журналом операций для выявления «зависших» статусов.

Разделение приема webhook (быстрый ACK в очередь) и его обработки — обязательный паттерн: провайдер ожидает ответ 200 в течение нескольких секунд, а обработка бизнес-логики (обновление заказа, начисление, уведомление клиента) может занимать больше времени и должна выполняться асинхронно.
Что это дает бизнесу и разработке
Разбор архитектуры объясняет технически все, что в первой части статьи описывалось на уровне симптомов:
- пауза при подтверждении — это ожидание AReq/ARes и, возможно, challenge-цикла
- дублирующиеся заказы — следствие отсутствия идемпотентного ключа на уровне заказа
- расхождение отчетности при возвратах — следствие отсутствия неизменяемого журнала операций.
Для команды разработки эта модель задает конкретный технический чек-лист: idempotency-key на создании платежа, обязательная проверка HMAC-подписи webhook, отдельная очередь для событий провайдера и leger-таблица вместо единственного поля статуса.
