У софта для оркестрации платежей девять задач. Вести каждый платёж по правилам, которые можно прочитать. Повторять те отказы, которые стоит повторять. Хранить карточные токены так, чтобы вы могли уйти с ними. Подписывать и защищать от дублей каждый вызов API. Доставлять вебхуки с честными повторами. Переводить коды провайдеров в один словарь статусов. Сводить деньги с платежами после расчёта. Показывать, почему выбран этот провайдер. Держать тестовый трафик подальше от боевого.

Это и есть чек-лист. Дальше каждый пункт превращён в вопрос, который вы зададите на демо, плюс цифры из живого кода как линейка для сравнения.

Одна оговорка про цифры. Они взяты из кода, в котором я работаю каждый день, и из его бета-среды, а не из боевого парка серверов — боевого парка пока нет. Это ориентир, как звучит слово «конкретно», а не отраслевой бенчмарк.

Если вы ещё решаете, нужен ли вам такой класс софта, сначала прочитайте что такое оркестрация платежей, а сюда вернитесь на этапе выбора.

Что такое софт для оркестрации платежей

Это софт между вашим checkout и несколькими провайдерами. Он решает, куда уйдёт каждый платёж, и отчитывается по всем на одном языке.

Два слова тут часто путают. Оркестрация — это практика. Софт для оркестрации — то, что вы покупаете или пишете ради неё. Платформа оркестрации — тот же софт как сервис, где связки с провайдерами уже написаны.

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

Нужна ли вам платформа оркестрации платежей? Нет, если у вас один провайдер, один рынок и нет жалоб. Причины перехода скучные и конкретные: второй эквайер, рынок, где нынешний слаб, или вопрос совета директоров про стоимость платежа. Если ни одного из них нет, вложите силы в checkout.

Четвёртая группа редко попадает в презентации вендоров: софтверные компании, которые встраивают платежи в свой продукт. Вертикальному SaaS, который начал платить своим пользователям, маршрут нужен с первого дня. Вместе с ним токены. И сверка. А платёжной команды у него нет. Для них чек-лист ниже — не гид покупателя, а техзадание.

До демо: что должно быть готово с вашей стороны

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

Возьмите на первый звонок четыре вещи:

  • Список провайдеров со ставкой по договору на каждый тип карт, а не рекламную ставку.
  • Отказы за прошлый месяц, сгруппированные по коду причины, топ-3 по количеству.
  • Разрез объёма по стране и бренду карты. Локальный дебет и зарубежный кредит везде ведут себя по-разному.
  • Операции, которыми вы реально пользуетесь. Возвраты, частичные возвраты, холды, выплаты, чарджбеки, рекуррент.

Последний пункт решает больше, чем маршрут. В контракте адаптеров, который я веду, девять операций: создать платёж, завершить 3DS, списать холд, отменить, вернуть, спросить статус, принять вебхук, выплатить на карту, выплатить на счёт. Каждый коннектор обязан ответить на все девять, даже когда провайдер за ним такого не умеет. Из пяти подключённых эквайеров выплату на карту делают четыре, а выплату на IBAN — только один. Ещё один не умеет ни того, ни другого и отвечает честным «не поддерживается».

Такая асимметрия — норма, а не наше невезение. Просите у вендора ту же матрицу: операция за операцией, провайдер за провайдером. Одна колонка «поддерживается» — тревожный знак.

Девять вещей, которые софт обязан уметь

1. Вести маршрут по правилам, которые читает человек

Правила маршрута решают, кому достанется платёж. Весь набор вы должны прочитать вслух за минуту.

Движок из этих примеров грузит правила в рамках одного юрлица, и правила умеют жить по расписанию: правила «только по выходным» во вторник в наборе просто нет. Правило BLOCK останавливает платёж до выбора провайдера. Правило ROUTE называет пул провайдеров, и этот пул становится списком кандидатов. Ни одно ROUTE не совпало? Тогда кандидат — любой провайдерский аккаунт юрлица.

Копировать тут стоит другое: место, где ловится конфликт. Совпасть может только одно правило ROUTE, и проверка стоит в момент сохранения, а не в момент платежа. Пересечение правил — ошибка конфигурации, а ловить такое надо на конфигурации.

Спросите на демо: создайте два правила внахлёст и сохраните. Если оба сохранились, спросите, какое победит в три часа ночи. Ответ «первое» означает, что вы получили проблему порядка, которую никто не задокументирует.

2. Повторять те отказы, которые стоит повторять

Каскад на второго провайдера после отказа звучит как бесплатные деньги. Он не бесплатный.

Часть отказов окончательна. Краденая карта остаётся краденой и у следующего провайдера. Вы заплатите за второй запрос, добавите секунды в checkout и получите вторую строку в отчёте. А часть эмитентов читает повторные попытки по одной карте как признак фрода.

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

Сложное — в маппинге под ним. Посмотрите на четыре реальных кода из тех же коннекторов:

ПровайдерКодЧто значитНаивное чтение
UPC131Нужна дополнительная аутентификация (SCA)отказ
UPC290Банк-эмитент недоступенотказ
PayLink406Операция выполняется, спросите позжеотказ
PayLink400Нужна проверка 3DSотказ

Ни один из четырёх не отказ. Два значат «клиенту есть что доделать», один — «банк лежит, попробуй другого», один — «спроси меня позже». Прочитаете их как отказ — завалите живые платежи. Отправите все в повтор — будете дёргать эмитентов попытками, которые не могут пройти.

Что спросить: покажите список повторов как данные, а не как фразу. Какие коды в нём, кто его правит и нужен ли релиз ради правки.

3. Отдавать токены, с которыми можно уйти

Токен карты — это ссылка вместо номера карты, чтобы сам номер лежал в одном месте.

Судьбу токенов решают два вопроса. Где лежит номер карты и что будет с токенами, если вы уйдёте.

В P26M номер карты живёт ровно в одном сервисе. Всё остальное — запись платежа, отчёты, маршрут — носит UUID. Номер зашифрован ключом AES-256-GCM96 внутри HashiCorp Vault, и ключ сам ротируется каждые 2160 часов: это 90 дней, криптопериод, который мы для него задали. Ключ не покидает Vault, поэтому дамп базы — это груда шифротекста.

Хранение — лёгкая половина. Вендоры пропускают вторую: жизненный цикл. Хранилище, которое умеет только создать и прочитать токен, — половина хранилища. Тут есть ещё справка по токену, заморозка, разморозка, удаление. Каждый такой вызов сверяется с реестром: какому мерчанту какая операция над каким токеном разрешена.

Три вопроса здесь. Можно ли заморозить один токен, не удаляя его? Можно ли выгрузить токены, если договор кончится? И на чьё имя выпущены сетевые токены — Visa и Mastercard — на ваше или вендора? Ответ на последний и есть весь вопрос про привязку к вендору, одной фразой.

4. Подписывать весь запрос, а не только тело

Этот пункт не виден на слайде и дорого стоит в проде.

Прежняя схема подписи, ещё наша, покрывала тело запроса и время. Выглядела нормально. Не была: подпись для POST /payments/{A}/refund оставалась годной для POST /payments/{B}/void с тем же телом. Метод, путь, query-строка, UUID ресурса — всё это лежало вне подписи.

Замена подписывает каноническую строку из восьми полей: версия, HTTP-метод, путь, канонический query, SHA-256 тела, время, nonce, плюс ключ идемпотентности. Окно по времени — 300 секунд в обе стороны. Nonce помнится вдвое дольше, отдельно по каждому ключу API, а повтор возвращается как HTTP 409 с кодом SIGNATURE_REPLAYED.

Ключ идемпотентности внутри подписанной строки — та деталь, которую стоит проверить. Если ключ лежит вне подписи, он превращается в свободный параметр подписанного запроса.

Сама идемпотентность проста и легко ломается незаметно. Здесь она по желанию: пришлёте заголовок Idempotency-Key — ответ ляжет в кэш на 24 часа, не пришлёте — запрос отработает как обычно. В ключ кэша входит UUID ключа API. Я точно знаю, что бывает, когда это разделение ломается: переименование заголовка едва не сломало его. Все мерчанты схлопнулись бы в одну корзину, и ответ одного мог бы вернуться другому.

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

5. Доставлять вебхуки как очередь, а не как надежду

Вебхуки — способ вашим системам узнать, что платёж изменился. На демо обычно показывают, как один приходит. Спросите, что будет, когда ваш эндпоинт упадёт.

Раньше здесь был один HTTP POST с таймаутом 10 секунд, прямо внутри запроса, который ждёт плательщик. Два последствия, оба замерены. Медленный эндпоинт добавлял до 10 секунд к ответу checkout, а подписки обрабатывались подряд: три подписки — до 30 секунд. Повторов не было вовсе: при неуспехе писалась строка в лог, и уведомление пропадало.

То, что пришло на замену, — форма, которую стоит ждать от любого серьёзного продукта:

СвойствоЗначение
Попытки1 + 4 повтора
Паузы10 с → 1 мин → 5 мин → 15 мин
Таймаут HTTP на попытку10 с
После последнего провалазапись в dead-letter, статус exhausted
Журнал по каждой попыткеномер попытки, HTTP-статус, длительность в мс, адрес

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

Сломайте это нарочно: направьте вебхук на эндпоинт, который отдаёт 500, и попросите журнал доставок. Нет журнала — нет способа ответить на вопрос «ушло или нет» во время инцидента, а его задают всегда.

6. Держать один словарь статусов — и слова провайдера

У каждого провайдера свой диалект, и диалекты больше, чем кажется.

Один провайдер из той пятёрки документирует 66 кодов результата. Коннектор сводит их к четырём состояниям. Коды 100, 101, 102 — успех. Коды 400 и 401 — ожидание, 406 — в обработке. Всё, что начинается на 2, 3 или 4, — отказ, а пятисотые — ошибка. Другой отвечает 22 разными строками статуса, и в десяти из них есть слово «wait»: ждём 3DS, ждём наличные в терминале, ждём скан QR. Схлопните эти десять в одно «в ожидании» — и поддержка больше не скажет клиенту, что делать дальше.

Ловушка из третьего коннектора, из тех, что встречаешь только в проде. Слово hold приходит в двух его ответах. В статусе заказа оно значит, что деньги захолдированы. В ответе на списание — что списание отклонено. Одно слово, обратный смысл, одна буква разницы в коде, который его читает. Поэтому маппинг статусов пишется под операцию, а не под слово.

И честное про код, о котором эта статья. Те самые коды PayLink 101 и 102 значат «прошло, но на часть суммы». Сегодня маппер кладёт их в ту же корзину, где лежит обычный успех. Поэтому в записи платежа остаётся запрошенная сумма. Я про это знаю. Ищите такой же шов в любом продукте, который смотрите: спросите, где в записи видно частичное списание, и посмотрите, придёт ответ из демо или из роадмапа.

Попросите показать запись платежа, где рядом лежат ваш нормализованный статус и сырой код провайдера. Если сырой код не хранится, каждый будущий спор — гадание.

7. Сверять деньги, а не только платежи

Оркестрация сводит вид на платежи. Вид на деньги она сама не сводит, и в этом зазоре финансовая команда теряет вечера.

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

  • Сопоставление идёт по order reference внутри назначения платежа, плюс сумма, плюс валюта. Живые платежи в приоритете перед завершёнными.
  • Если reference пуст, матчер откатывается на сумму и валюту в окне ±30 минут вокруг времени зачисления.
  • Если такой откат нашёл больше одного живого кандидата, он останавливается и оставляет событие несопоставленным. Два клиента, платящие по 500 UAH с разницей в минуту, — не редкость, а угадывание тут означает закрыть чужой счёт.
  • Повторная доставка той же банковской транзакции ловится ограничением уникальности по источнику и внешнему id и помечается как duplicate до того, как что-то тронет платёж.
  • Зачисление, пришедшее после того, как платёж истёк, открывает его заново — но только внутри grace-окна в 30 минут. За окном событие остаётся в очереди сверки для человека.
  • Всё остальное ложится в один из пяти статусов, включая manual для тех, что оператор свёл руками.

Ничего хитрого тут нет. Это бухгалтерия, записанная один раз вместо ежемесячного спора. В софте она живёт не из красоты, а из объёма: правило, которое человек верно применяет на тридцати событиях в день, перестаёт применяться верно на трёхстах.

Вторая половина редко доезжает до демо, и живёт в ней как раз ваш финотдел. Пять провайдеров — это пять файлов расчётов, пять расписаний, пять названий для возврата и пять способов удержать комиссию до зачисления. Спросите, что слой делает с этими файлами, а не только с платежами. Ответ «мы отдаём API, импорт вы напишете сами» — честный ответ, но теперь вы знаете, что проект ваш.

Требуйте экран: несопоставленное зачисление и место, где человек его разбирает. У любой настоящей сверки есть ручной путь. Продукт без него просто ещё не встречался с банковской выпиской.

8. Показывать свою работу

Когда платёж пошёл не так, у вас один вопрос: почему он ушёл туда.

Ответ требует записи на каждый вызов провайдера. Такая запись держит запрос и ответ, HTTP-код, время выполнения в миллисекундах. И — то, что чаще всего пропускают, — id правила маршрута, выбравшего аккаунт, плюс полный набор сработавших правил. Поэтому «почему этот платёж ушёл туда» — это выборка, а не расследование.

Со стороны API каждый запрос получает UUID, который возвращается в заголовке ответа, а весь обмен уходит в отдельный структурированный аудит-канал. Канал появился потому, что требование PCI DSS 10.2.1 просит журнал доступа к компонентам системы, и он же выручает при обычной отладке. Чувствительное вырезается до записи: номера карт, разумеется, но ещё и capability-токены внутри URL, потому что лог с утёкшим токеном превращает архив логов в связку запасных ключей.

Засеките время. Дайте мне id платежа и покажите на одном экране все его вызовы и правило, которое выбрало провайдера. Посчитайте клики. В ночь инцидента у вас будет три минуты.

9. Разделять песочницу и прод

Звучит как галочка, но галочкой не является.

Кандидаты из чужой среды выбрасываются для любого типа операции, а не только для карточных платежей. Если после фильтра не осталось никого, платёж падает с «нет провайдера», а не берёт тихо тестовый аккаунт. Тихая подмена хуже отказа: отказ виден сегодня, подмена находится в конце месяца.

Ещё одна проверка: попробуйте увести боевой платёж на тестовый аккаунт провайдера и покажите ошибку. Получилось — значит барьер это ярлык, а не правило.

Параметры и значения: что просить в цифрах

Вендоры отвечают на прилагательные прилагательными. Просите цифры и сравнивайте вот с этими — не потому, что эти правильные для вас, а потому, что вендор, который не может назвать свои, о них не думал.

ПараметрОпорное значениеПочему это важно
Окно здоровья провайдера1 час, пересчёт каждые 5 минКороче — реакция на шум, длиннее — пропустите падение сейчас
Формула здоровья70% успешность, 30% время ответаМеньше 1 с — полный балл, 10 с — ноль
Порог здоровья40 из 100Ниже провайдер выпадает из маршрута
Балл без свежего трафика50, нейтральноТихий провайдер не значит сломанный
Все провайдеры больныФильтр снимается, платёж всё равно уходитЧистота вместо выручки — плохой размен
Таймаут на вызов провайдера30 сОдному нужно два вызова на платёж, считайте вдвое
Повторы вебхука5 попыток примерно за 21 минСм. таблицу пауз выше
Кэш идемпотентности24 ч, отдельно по ключу APIДольше — счёт за память, короче — теряется страховка
Окно свежести подписи±300 сЗапас на расхождение часов против окна replay
Лимит запросов60 в минуту на мерчантаСпрашивайте и число, и поведение на всплеске
Жизнь сессии checkout30 минСлишком короткая рубит живых клиентов посреди оплаты
Полей учётных данных на провайдераот 3 до 10Труд подключения у провайдеров разный

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

Типичные ошибки и как поймать их рано

Покупка роутера, когда нужен сейф. Если настоящая беда в том, что карточные данные размазаны по трём системам, правила маршрута не помогут. Сначала данные, маршрут после — он проще.

Вера в таблицу возможностей. «Поддерживается» в маркетинговой таблице и «поддерживается» в ветке кода, которая работает в три ночи, — разные слова. Одна знакомая мне таблица возможностей ставит wallet-метод всем пяти эквайерам, а криптограмму кошелька прямо на нашем checkout принимают двое: остальные трое поддерживают кошелёк на своей платёжной странице. Спросите, какой источник читает код маршрута.

Маршрут по плоской ставке. Реальная стоимость зависит от бренда, подтипа карты, суммы, метода. Провайдер, который дешевле на локальном дебете, часто дороже на зарубежном кредите. Сортировка по одному проценту из договора будет уверенно выбирать не того весь день и рисовать вам опрятный отчёт.

Забыть, что одна интеграция — одна зависимость. Вы убрали пять интеграций и добавили одну штуку, через которую идёт каждый платёж. Обычно это верный размен. Но всё же размен: спросите про процесс релизов, про историю аптайма и про то, дотянетесь ли вы до провайдера напрямую, если слой лежит.

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

Считать, что комплаенс переходит по наследству. Сертификат вендора покрывает вендора. Ваша зона зависит от того, где в вашей схеме течёт карточный номер. Требование PCI DSS 12.8 ждёт от вас списка сторонних поставщиков услуг и свидетельств их статуса. Поэтому просите их аттестацию и матрицу ответственности письменно — на этапе выбора, а не на этапе аудита.

Сценарий демо в шесть шагов

Два часа с живой системой скажут больше, чем месяц презентаций. Идите по порядку и пишите заметки прямо в комнате.

  1. Подключите тестового провайдера сами. Не общий демо-аккаунт, а свежий, и креды вводите вы. Засеките время. Это ближайший превью вашей будущей интеграции.
  2. Проведите один карточный платёж. Потом откройте запись и найдите: сырой код провайдера, нормализованный статус, правило маршрута и время ответа.
  3. Сломайте нарочно. Направьте вебхук на URL, отдающий 500. Посмотрите на повторы. Попросите журнал доставок и запись dead-letter.
  4. Повторите запрос. Отправьте один и тот же вызов дважды с одним ключом идемпотентности. Потом ещё раз — с подписью от другого эндпоинта. Второй обязан упасть.
  5. Заставьте маршрут выбрать неправильно и почините без релиза. Добавьте правило, гонящее всё на дорогого провайдера, дождитесь эффекта и снимите его. Замерьте, за сколько правка доходит до боевого трафика.
  6. Задайте вопрос про сверку. Покажите несопоставленное входящее зачисление и экран, где человек его разбирает.

Оцените шесть шагов честно в тот же день, пока следующий звонок не смешал ответы. Всё, что вы не смогли сделать сами и в комнате, — это то, чего вы потом будете ждать.

Как понять, что заработало

Покупка — не финиш. Четыре проверки в первом квартале скажут, окупает ли слой своё место.

Состав отказов поменял форму. Не итоговый процент, а состав. Если те же коды причин идут в прежних долях, маршрут ничего не делает. Настоящее ограничение тогда выше по потоку.

Инцидент провайдера прошёл скучно. Когда один эквайер деградирует впервые, с выручкой не должно случиться ничего, кроме строки на дашборде. Если пришлось собирать совещание, автоматики ещё нет.

Финансы перестали ходить к разработке за цифрами. Одобрения по странам, стоимость платежа, возвраты по провайдерам — это отчёты, а не тикеты.

Кто-то поменял правило маршрута без релиза. Если через три месяца ответ звучит как «их никто не трогал», то либо дефолты идеальны, либо у слоя нет владельца. Второе бывает чаще.

Есть ещё одна проверка, самая неудобная. Попробуйте уйти. Не по-настоящему — на бумаге. Выпишите, что вы унесёте: токены, мандаты, договоры с провайдерами, историю операций. Короткий список значит, что вы купили не софт, а аренду зависимости. Как этот ящик устроен внутри — те самые детали, из которых берутся ответы выше, — разбираем в материале что такое слой оркестрации платежей, а разницу между шлюзом и слоем — в статье оркестрация платежей против платёжного шлюза.

Чего этот софт для вас не сделает

Стоит сказать прямо, потому что категорию продают так, будто она чинит всё.

Он не починит плохой процент одобрений сам по себе. Роутер не лечит причину, он только переносит попытку. Маршрут уводит платежи туда, где их скорее одобрят. Если все ваши провайдеры отказывают одному и тому же трафику по одной причине, маршрут просто размажет отказы. Лечение живёт в коде причины.

Ваша PCI-зона не сдвинется от того, что появился слой. Её решает то, как карточные данные текут через ваш checkout. Слой, который пропускает через себя сырые номера карт, оставит вас ровно там же, где вы были.

Слабый эквайер останется слабым. Если у провайдера плохое покрытие на рынке, роутер перед ним покрытия не добавит. Он только упростит отправку этого трафика в другое место.

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

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