Тема
Почему шлюзов несколько
Запрос мерчанта может прийти на два разных публичных адреса, и это не дублирование, а требование PCI DSS. Разделение простое: всё, что содержит номер карты, принимает отдельный шлюз в отдельном контуре; всё остальное — обычный шлюз.
Два контура
| Обычный merchant API | PCI-шлюз | |
|---|---|---|
| Сервис | api-gateway-general | api-gateway-pci |
| Домен | api-gateway.smartcore.pro | chd-api.smartcore.pro |
| AWS-аккаунт | prod-general | prod-pci |
| Что принимает | Создание платежа со ссылкой на форму, проверка статуса, выплаты, онрамп, ARS | Платёж с картой, токенизация, оплата картой на нашей форме, выплата с вводом карты |
| Карточные данные | Не принимает никогда | Единственная точка входа для них |
Кто в каком контуре живёт
Разделены не только шлюзы, а весь карточный путь: контур — это отдельный AWS-аккаунт со своей сетью и своим доступом.
Здесь видно главное: в PCI-контуре живут только те сервисы, которым настоящий номер карты нужен по существу, — шлюз, который его принимает, хранилище карт, драйверы, передающие карту в банк, и выпускающий виртуальные карты card-manager. Ядро, антифрод, доставка колбеков и всё остальное работают исключительно с токенами и в аудит PCI DSS не попадают.
Граница между контурами пересекается всего дважды, и оба раза осмысленно: карта превращается в токен сразу на входе, а обратно в карту — уже в драйвере, перед самой отправкой в банк. Из этого следует практическое правило: если вашей задаче понадобился настоящий номер карты в общем контуре, почти наверняка задача решена неправильно.
Платёжная форма — исключение, которое стоит понимать. Сама страница живёт в общем контуре, но введённые карточные данные отправляет напрямую в PCI-шлюз, минуя своё окружение.
Смысл разделения в том, чтобы область аудита PCI DSS оставалась маленькой. Чем меньше кода и инфраструктуры видит настоящий номер карты, тем дешевле и надёжнее сертификация. Поэтому PCI-шлюз умеет мало: принять карту, обменять её на токены и передать дальше уже обезличенный запрос.
Что из этого следует для разработки
Новая ручка без карточных данных идёт в api-gateway-general. Это целевой публичный шлюз merchant API, на него постепенно переезжает всё остальное.
Ручка, принимающая карту, идёт в PCI-шлюз — и там же обязана сразу токенизировать данные, не передавая их дальше в открытом виде.
Исторический TypeScript-шлюз ещё держит часть операций. Возвраты, рекуррентные платежи, двухстадийные операции и часть выплат пока живут только в нём, поэтому при поиске кода конкретной ручки проверяйте оба сервиса. Новую функциональность туда не добавляют.
Что шлюз делает и чего не делает
Оба шлюза — тонкий слой. Они проверяют, что запрос пришёл от известного мерчанта, приводят его к внутреннему формату, подписывают и передают в ядро.
Бизнес-логики в шлюзе нет: он не выбирает банк, не считает комиссию, не меняет статусы и не принимает решения об отказе. Единственное исключение — токенизация карты в PCI-шлюзе, и это не бизнес-логика, а требование контура.
Если вы ловите себя на желании добавить в шлюз условие «а для такого мерчанта сделать иначе» — почти наверняка это условие должно жить в ядре.
Как мерчант аутентифицируется
Одинаково на обоих шлюзах: либо Basic с парой «ключ мерчанта — секрет», либо подпись по отсортированным значениям тела. Проверяет не шлюз, а сервис авторизации — шлюз только спрашивает у него, валидны ли данные. Отдельные служебные эндпоинты используют собственные ключи, а публичные страницы формы аутентификации не требуют вовсе. Разбор всех механизмов — в Аутентификация и подписи.