Тема
3DS
Банк-эмитент не готов списывать деньги только потому, что кто-то знает номер карты. Ему нужно убедиться, что операцию подтверждает настоящий держатель: кодом из сообщения, входом в приложение банка или молча — если поведение выглядит достаточно привычным. Этим занимается отдельный сервис на стороне эмитента, который в документации и в нашем коде называют ACS. Для нас ACS — чужая страница, на которую мы отправляем плательщика и с которой ждём его обратно.
Кроме безопасности у 3DS есть денежный смысл: по пройденной аутентификации ответственность за спорную операцию переходит с мерчанта на эмитента. Поэтому для большинства гейтов это не опция, а обязательный шаг.
Что меняется между первой и второй версией
С точки зрения нашего кода разница не в криптографии, а в количестве шагов.
В первой версии всё просто: плательщик уходит на страницу банка по адресу, который дал нам банк, подтверждает операцию и возвращается на наш адрес, принеся с собой результат аутентификации. Мы передаём этот результат драйверу, и драйвер дожимает платёж.
Во второй версии перед подтверждением появляется технический шаг: банк хочет данные браузера и просит, чтобы браузер плательщика сам сходил на его адрес и оставил там отпечаток. Об этом визите банк уведомляет нас отдельно. Только после этого мы просим банк начать аутентификацию — и здесь возможны два исхода: банк решает, что вопросов нет, и подтверждает операцию без участия плательщика, либо всё-таки показывает ему страницу подтверждения.
Практическое следствие: во второй версии между «мы получили ответ банка» и «плательщик видит страницу банка» появляется наша собственная страница, которая всё это оркестрирует. Из-за неё транзакция может застрять на шаге, где плательщик формально ещё ничего не подтверждал.
Путь плательщика
Драйвер, сходив в банк, возвращает ядру поля обработки. Среди них либо готовый кусок HTML, который сам отправляет браузер на страницу банка, либо просто адрес этой страницы. Ядро сохраняет это на транзакции и отдаёт мерчанту ссылку вместе со статусом «в обработке» — см. Платёж картой server-to-server.
Мерчант перенаправляет плательщика по ссылке. Плательщик подтверждает операцию на странице банка и возвращается на наш адрес завершения аутентификации. Ядро фиксирует возврат, записывает его в лог взаимодействия с гейтом и передаёт результат драйверу, чтобы тот довёл платёж до конца в банке. Дальше плательщик уезжает обратно на страницу мерчанта.
Своя страница или сразу банк
По умолчанию ссылка, которую получает мерчант, ведёт не на банк, а на нашу промежуточную страницу. На ней лежит подготовленный драйвером HTML, и она делает три вещи, которых прямой редирект не умеет: отмечает на транзакции, что плательщик действительно дошёл до банка, отрабатывает технический шаг второй версии и показывает осмысленную страницу возврата, если транзакция к этому моменту уже закрыта.
Прямой редирект включается настройкой и работает, только если драйвер вернул именно адрес, а не HTML. Настройка есть и у гейта, и у счёта мерчанта, и достаточно одной из них. В этом случае мерчант получает ссылку сразу на банк: путь короче, но отметок о переходе у нас не остаётся, и разбирать «дошёл ли плательщик» приходится по данным банка.
Уведомление браузера
Технический шаг второй версии выглядит так. Наша страница скрыто обращается к адресу банка, а затем ждёт, когда банк подтвердит, что визит состоялся: уведомление приходит к нам отдельным запросом, а страница периодически спрашивает, пришло ли оно. Ожидание ограничено примерно десятью секундами — если за это время уведомления нет, страница всё равно запускает аутентификацию. Это сделано намеренно: уведомление часто теряется, и без ограничения платёж завис бы навсегда. Факт уведомления хранится в Redis недолго — ровно чтобы пережить ожидание страницы.
Адрес возврата
Адрес, на который плательщик приходит после банка, собирается из базовой части и идентификаторов транзакции и гейта. Базовая часть берётся по приоритету: сначала индивидуальный адрес из секретов гейта, затем адрес, настроенный мерчанту, и только потом общий адрес окружения. Поэтому у двух платежей одного и того же мерчанта адреса возврата могут оказаться на разных доменах — это не ошибка, а настройка, и при разборе инцидента стоит смотреть, какой именно адрес ушёл в банк.
Если плательщик не вернулся
Возврат с ACS — ненадёжный канал: вкладку закрывают, сеть отваливается, банк отвечает не туда. Поэтому на каждый платёж, ушедший на аутентификацию, ставится отложенная задача с таймаутом. Срок берётся из настройки банка, к которому относится гейт, либо из настройки мерчанта, либо равен часу по умолчанию.
Сработав, задача не закрывает платёж сразу: сначала она спрашивает банк о текущем состоянии, и только если платёж по-прежнему ждёт аутентификации, переводит его в отказ. Причина отказа зависит от того, как далеко ушёл плательщик: отдельно различаются случаи, когда он дошёл до страницы банка, когда застрял на техническом шаге второй версии и когда не перешёл вовсе. Эти категории видны на транзакции и экономят время при разборе.
Параллельно работают два независимых механизма, общих для всего платёжного пути: колбек банка, который приходит в ядро, и регулярный переспрос статуса. Именно они, а не возврат плательщика, доводят платёж до определённости — см. Очереди, таймеры и отложенные задачи.
Как это видно в транзакции
Каждому шагу соответствует своя стадия внутри статуса «в обработке»: начало аутентификации, ожидание уведомления второй версии, возврат с ACS. Значения и правила чтения — в Статусы и стадии; пересказывать их здесь незачем, потому что перечень живёт вместе с драйверами.