Продолжение: как онлайн-эквайринг устроен «под капотом»

Продолжая тему Как работает онлайн-эквайринг и что проверить заранее, захотелось разобрать эквайринг с точки зрения архитектуры и как все работает.

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

Три домена платежа

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

Три домена 3-D Secure 2

Этот контур объясняет, почему магазин физически не может получить реквизиты карты в открытом виде — данные карты видит только форма банка или платежного сервера, прошедшая сертификацию PCI DSS, а сайту передается только технический статус операции.

Frictionless и challenge: два пути внутри 3-D Secure 2

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

Frictionless и challenge: два пути внутри 3-D Secure 2

По статистике индустрии, через challenge-сценарий проходит лишь около 5% транзакций — остальные закрываются в фоне, что и создает у покупателя ощущение «мгновенной» оплаты. Именно это несовпадение между визуальной скоростью и реальным количеством внутренних проверок и объясняет паузу, описанную в первой части статьи.

Источник истины: почему нельзя доверять браузеру

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

webhook от платежной системы

Такая архитектура прямо снимает проблему из первой части статьи — «заказ отмечен оплаченным, а подтверждения нет»: пока webhook не подтвержден и не прошел проверку подписи, заказ остается в промежуточном статусе, а не в статусе «оплачен».

Идемпотентность: как не создать два заказа из одной оплаты

Повторные нажатия кнопки оплаты, дубли webhook-уведомлений и повторы запросов при сетевых сбоях — стандартная ситуация в платежных интеграциях, и решает ее паттерн идемпотентных ключей. Клиент генерирует уникальный идентификатор операции (обычно UUID v4) один раз на логическую операцию и передает его в заголовке запроса; сервер сохраняет пару «ключ → результат» и при повторном обращении с тем же ключом возвращает закешированный ответ, не выполняя повторно бизнес-логику.

КомпонентЧто использовать как ключГде хранитьTTL
Запрос создания платежаUUID v4, привязанный к заказуRedis / Postgres с unique-constraint24–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-таблица вместо единственного поля статуса.