Почему интеграция платежных шлюзов в Medusa.js ломается на проде
Интеграция платежных шлюзов в Medusa.js почти всегда проходит гладко на тестовом контуре и ломается на проде — потому что тестовый провайдер отвечает мгновенно и никогда не повторяет запросы. В реальности вебхуки платежей приходят повторно, с задержкой, иногда вне очереди, а провайдер может прислать событие об успехе уже после того, как заказ отменён по таймауту.
Medusa.js хранит статус заказа отдельно от транзакции платёжного провайдера, и если связать их «на глазок» — по времени или по сумме — получаешь рассинхрон статусов заказа: клиент видит «оплачено», а в системе заказ висит неоплаченным, или наоборот.
Второй типовой сбой — дубли списаний. Провайдер не гарантирует однократную доставку вебхука: при сетевом сбое он повторит событие, и без идемпотентности платежа обработчик проведёт оплату дважды. Лечится это не «проверкой в коде», а ключом идемпотентности на стороне провайдера плюс журналом обработанных событий в базе Medusa: повторный вебхук с тем же идентификатором должен быть распознан и проигнорирован до любой бизнес-логики.
Третий сбой — потеря платежа при таймауте. Пользователь ушёл на страницу провайдера, оплатил, но редирект не вернулся — и заказ остаётся в подвешенном состоянии, хотя деньги списаны. Здесь работает только асинхронная сверка: фоновый процесс периодически опрашивает провайдера по «зависшим» транзакциям и доводит статус до финального, а не полагается на возврат браузера.
Все три сбоя — про одно: поток событий от провайдера и состояние заказа в Medusa.js должны сходиться детерминированно, а не «обычно работает». Разбираем, как это закрывается на уровне архитектуры, а не заплатками в обработчике.
Что даёт корректно собранный платёжный контур
Корректно собранный платёжный контур в Medusa.js — это не «подключить провайдера и забыть», а управляемый слой между заказом, провайдером и складом. Когда он выстроен правильно, доля успешных оплат перестаёт зависеть от капризов одного шлюза: маршрутизация по типу карты и региону отправляет платёж туда, где он пройдёт с первой попытки, а не туда, куда настроено по умолчанию.
Устойчивость к сбоям провайдера закладывается на уровне обработки вебхуков. Мы делаем идемпотентную обработку событий с ключом по идентификатору платежа и журналом состояний — повторный вебхук от провайдера не создаёт второй заказ и не списывает деньги дважды. Это устраняет целый класс двойных списаний, из-за которых обычно и приходят разбираться: клиент видит два холда, поддержка вручную сверяет выписки, а деньги возвращаются через дни.
Скорость возврата средств — прямое следствие того же журнала. Отмена и возврат инициируются из админки одной операцией, статус синхронизируется с провайдером автоматически, а расхождения попадают в отчёт, а не всплывают в жалобах. Реакция на сбой провайдера сокращается до переключения на резервный маршрут без остановки продаж.
Проверить контур можно по нескольким признакам: доля успешных оплат держится на стабильном уровне без ручных доработок, повторные вебхуки не ломают заказы, возврат проходит предсказуемо, а сбой одного провайдера не роняет оформление заказа целиком. Если хотя бы один пункт не выполняется — контур собран частично, и это видно по нагрузке на поддержку.
Способы подключения платежей: что выбрать под задачу
В Medusa.js платёжный провайдер — это модуль с фиксированным набором методов: инициализация платежа, авторизация, подтверждение (capture), возврат и обработка оповещений от банка. Способ подключения выбирают не по удобству интеграции, а по тому, насколько бизнес-логика оплаты отличается от типового сценария «корзина → оплата → заказ».
Если сценарий стандартный, разумнее взять готовый модуль провайдера: он уже покрывает холдирование, частичный возврат и повторную авторизацию, а обновления приходят вместе с ядром. Собственный провайдер-модуль оправдан, когда нужны нестандартные шаги — оплата по частям, мультивалютные сплиты, привязка карты к аккаунту без списания, свои правила ретраев. Внешняя платёжная прослойка (собственный сервис между магазином и банком) добавляет ещё один узел, зато изолирует магазин от смены эквайринга: провайдер меняется в одном месте, витрина и логика заказов не трогаются.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Готовый модуль провайдера | Типовой сценарий оплаты, один-два эквайринга, нужен быстрый запуск | Логика ограничена методами модуля; нестандартные шаги придётся обходить снаружи |
| Собственный провайдер-модуль | Нестандартные сценарии: сплиты, частичные списания, привязка карт, свои ретраи | Разработка и поддержка на нашей стороне; при мажорных обновлениях ядра модуль нужно проверять |
| Внешняя платёжная прослойка | Несколько эквайрингов, частая смена провайдера, единые правила для нескольких витрин | Дополнительный узел в цепочке оплаты; нужен мониторинг и разбор расхождений по статусам |
На практике выбор почти всегда упирается в то, сколько сценариев оплаты живёт дольше года. Если один — берём готовый модуль и не плодим сущности. Если оплата становится частью продукта, считаем стоимость владения собственного модуля против прослойки: первый дешевле в запуске, вторая — в эксплуатации при смене эквайринга.
Разберём вашу схему оплаты и предложим способ подключения с оценкой по срокам и поддержке.
Этапы внедрения платежного шлюза в Medusa.js
Работа идёт от аудита текущего платежного контура, а не от установки модуля: сначала фиксируем, как заказы, корзины и статусы оплаты живут в вашей инсталляции Medusa.js, какие провайдеры уже подключены и где рвётся связь между оплатой и изменением статуса заказа. Дальше — последовательность, в которой каждый следующий шаг опирается на результат предыдущего.
Как устроен процесс
- Аудит платежного контура. Разбираем текущие модули оплаты, схему заказов и корзин, обработку возвратов и частичных оплат. На выходе — карта мест, где статус заказа расходится с фактическим состоянием платежа.
- Выбор и подключение провайдера. Определяем, идёт ли интеграция через готовый модуль или через собственный сервис оплаты, и настраиваем его под ваш сценарий: одностадийная оплата, захват средств отдельно от авторизации, повторные списания.
- Настройка вебхуков. Прописываем приём уведомлений от шлюза, проверку подписи и идемпотентную обработку событий — повторный вебхук не должен создавать второй платёж или дублировать заказ.
- Проверка сценариев оплаты. Прогоняем успешную оплату, отказ, отмену, возврат полный и частичный, оплату с задержкой подтверждения. Каждый сценарий сверяем по статусу заказа и по записи в платежном журнале.
- Обработка сбоев. Настраиваем поведение при недоступности шлюза: очередь повторных попыток, понятный статус для покупателя, отсутствие «зависших» заказов в промежуточном состоянии.
- Тестовый и рабочий контуры. Прогоняем весь набор сценариев в тестовом режиме провайдера, затем переключаем на рабочие ключи и проверяем первые реальные операции под наблюдением.
- Передача регламентов. Отдаём документацию по конфигурации, схему прохождения платежа и инструкцию по действиям при сбое — чтобы поддержка закрывала инциденты без нас.
Порядок этапов не меняется местами: настройка вебхуков без аудита даёт дубли платежей, а проверка сценариев без тестового контура переносит ошибки на реальные деньги. Если у вас уже есть частично работающая интеграция — начнём с аудита и покажем, какие шаги нужно переделать, а какие можно оставить.
Что заказчик получает на руки после внедрения
После внедрения у команды остаётся не «работающий код у нас на ноутбуке», а комплект артефактов, по которому платежи можно развивать и поддерживать без нас. Каждый пункт — это то, что реально передаётся в репозиторий и во внутреннюю документацию заказчика.
- Конфигурация провайдеров — модули платежей в Medusa.js с раздельными профилями для тестового и рабочего окружения, ключи только через переменные окружения, переключение провайдера без правок бизнес-логики.
- Схема потоков оплаты — диаграммы по каждому сценарию: обычная оплата, подтверждение, частичный возврат, отмена и повторная попытка. Видно, где создаётся корзина, где фиксируется заказ и в какой момент заказ считается оплаченным.
- Политика обработки вебхуков — описанные правила приёма событий от шлюза: проверка подписи, идемпотентность по идентификатору события, порядок обработки повторных доставок, поведение при недоступности сервиса и очередь на догон пропущенных уведомлений.
- Обработка отказов и таймаутов — сценарии, когда шлюз не ответил или вернул ошибку: повторные попытки с задержкой, фиксация статуса «в обработке», ручное подтверждение и понятный лог для разбора инцидента.
- Документация по платежам — регламент добавления нового провайдера, описание полей конфигурации, матрица статусов заказа и инструкция для поддержки: что смотреть при расхождении статуса между шлюзом и системой.
- Регламент сверки — порядок сопоставления транзакций шлюза с заказами, частота проверки и перечень сигналов, по которым запускается разбор расхождений.
- Доступы и роли — разграничение прав на ключи и настройки платежей, порядок ротации секретов и передачи доступа при смене ответственного.
Этого набора достаточно, чтобы подключить нового провайдера или разобрать спорную транзакцию силами внутренней команды, не возвращаясь к авторам интеграции.
Кейс: как мы устранили двойные списания и подняли долю успешных оплат
Клиент — сервис подписок на базе Medusa.js — пришёл с классической проблемой роста: доля успешных оплат держалась на уровне 71%, а каждое третье обращение в поддержку было про «деньги списались, но заказ не оформился». Причина оказалась не в одном шлюзе, а в архитектуре: подтверждение оплаты обрабатывалось синхронно в запросе пользователя, и при таймауте ответа провайдера клиент повторял запрос — отсюда двойные списания.
Что изменили
Мы развели поток оплаты на два независимых контура. Инициализация платежа теперь только создаёт запись со статусом «ожидает» и возвращает идентификатор операции, а финальный статус приходит отдельным уведомлением от провайдера и обрабатывается идемпотентно — повтор одного и того же события не создаёт второе списание. Ключом идемпотентности стал идентификатор операции провайдера, а не сессия пользователя, поэтому ретраи со стороны клиента и повторные вебхуки схлопываются в одну транзакцию.
Второй контур — сверка. Раз в несколько минут фоновый процесс сверяет «зависшие» операции со статусом у провайдера и доводит их до финального состояния, если уведомление потерялось. Это убрало класс сбоев, когда оплата прошла, а заказ остался неоформленным.
Метрики до и после
| Показатель | До | После |
|---|---|---|
| Доля успешных оплат | 71% | 96% |
| Обращения в поддержку по платежам | ~40 в неделю | единичные |
| Время реакции на сбой провайдера | ручной разбор, часы | автоматический довод, минуты |
| Двойные списания | регулярные | устранены |
Отдельно настроили маршрутизацию: при недоступности основного шлюза трафик уходит на резервный, а не роняет оформление заказа. Время реакции на сбой провайдера сократилось с ручного разбора до автоматического доведения операции — инженер подключается только к аномалиям, которые сверка не смогла закрыть сама.
Чем отличается идемпотентная обработка вебхуков от повторной оплаты
Платёжный шлюз доставляет вебхук по принципу «пока не получит подтверждение» — при таймауте, сетевой ошибке или перезапуске сервиса одно и то же уведомление приходит повторно. Это штатное поведение, а не сбой: шлюз не знает, обработали вы событие или нет, поэтому повторяет доставку. Повторная доставка вебхука и повторное списание — разные события. Первое означает, что уведомление об уже прошедшей оплате пришло второй раз; второе — что с клиента реально списали деньги ещё раз. Смешение этих понятий в коде и порождает двойные заказы, дубли писем и повторные списания.
Идемпотентная обработка — это свойство обработчика, при котором повторный вызов с тем же идентификатором события не меняет состояние системы. В архитектуре обработки платежей ключ идемпотентности — это идентификатор события от шлюза, а не номер заказа: один заказ может породить несколько легитимных событий (авторизация, подтверждение, возврат). Обработчик в одной транзакции проверяет, не зафиксировано ли событие ранее, и только потом меняет статус заказа и запускает побочные действия.
Практический вывод для Medusa.js: обработчик вебхука должен быть атомарным и опираться на уникальный индекс по идентификатору события в базе. Тогда повторная доставка просто вернёт «уже обработано» и не тронет заказ, а повторное списание станет невозможным на уровне схемы данных, а не на уровне проверок в коде. Отдельно фиксируем порядок событий: уведомления могут прийти не по порядку, поэтому статус заказа обновляем по правилам перехода, а не «как пришло».
Если этого слоя нет, защита строится на ручных сверках и разборе обращений — а каждая повторная доставка превращается в потенциальный инцидент. Мы проектируем обработку так, чтобы повтор стал безопасным по умолчанию: уникальность события, атомарная транзакция, журнал обработанных уведомлений и корректные переходы статусов.
Частые технические сомнения по интеграции платежей
Интеграция платежей редко бывает «поставил и забыл»: провайдеры меняют требования, появляются новые способы оплаты, а бизнес просит возвраты и повторные списания. Ниже — технические сомнения, которые чаще всего всплывают на созвонах, и то, как мы их закрываем в коде.
Насколько сложно сменить платёжного провайдера, если интеграция уже работает?
Платёжный слой в Medusa.js изолирован: провайдер подключается как отдельный модуль, а логика заказа, корзины и статусов остаётся нетронутой. Смена сводится к регистрации нового модуля, переносу ключей доступа и маппингу статусов платежа на внутренние состояния заказа. Мы держим адаптеры так, чтобы бизнес-логика не знала о конкретном шлюзе — тогда переход занимает дни, а не недели, и старые транзакции остаются доступны для сверки и возвратов.
Как тестировать оплату, не проводя реальных списаний?
Каждый провайдер даёт тестовый контур с отдельными ключами и песочницей. Мы прогоняем полный сценарий: успешная оплата, отказ банка, таймаут, повторная попытка, отмена на стороне клиента. Отдельно проверяем асинхронные уведомления — именно на них чаще всего ломается обработка статусов, когда ответ приходит с задержкой или дублируется.
Как устроены возвраты и частичные возвраты?
Возврат инициируется из админки или через событие заказа, дальше уходит запрос к провайдеру и фиксируется результат. Мы обрабатываем полный и частичный возврат, сохраняем связь с исходной транзакцией и не даём вернуть больше, чем было оплачено. Если провайдер поддерживает асинхронные возвраты, статус подтягивается по уведомлению, а не по опросу — заказ не зависает в промежуточном состоянии.
Что с безопасностью и доступом к платёжным данным?
Ключи доступа хранятся в переменных окружения и не попадают в репозиторий, вебхуки проверяются по подписи, а данные карт через наш код не проходят — их обрабатывает сам провайдер. Это снимает большую часть требований по защите платёжной информации с вашей стороны.
Если ваш случай не попал в список — опишите задачу, вернёмся с оценкой и планом работ.
Обсудим вашу задачу по интеграции платежных шлюзов в Medusa.js
Чтобы оценка по интеграции платежных шлюзов в Medusa.js была предметной, а не «плюс-минус», на старте нужно снять несколько вводных. Medusa — headless-платформа: платежи здесь подключаются как отдельный модуль-провайдер, поэтому итоговый объём работ зависит не только от числа шлюзов, но и от того, как у вас устроены заказы, возвраты и статусы оплат.
Что уточняем на первом разговоре:
- какие шлюзы и способы оплаты нужны (карты, кошельки, рассрочка, оплата по счёту) и в каких странах принимаете платежи;
- сценарии: одностадийная или двухстадийная оплата (авторизация + списание), частичные возвраты, отмена, повторные попытки;
- как сейчас обрабатываются вебхуки и что происходит с заказом, если платёж завис или отвалился;
- требования к 3-D Secure и подтверждению личности плательщика;
- есть ли уже аккаунты в платёжных системах или их нужно заводить;
- как устроен учёт: нужно ли синхронизировать оплаты с бухгалтерией и складом;
- какие окружения и доступы есть сейчас и кто на вашей стороне принимает решения по платежам.
По этим данным мы возвращаемся с оценкой и планом: состав модулей-провайдеров, схема прохождения платежа, объём доработок по заказам и возвратам, сроки внедрения по этапам и точки, где нужны ваши подтверждения. Если по ходу выясняются неочевидные сценарии — фиксируем их отдельно, чтобы они не всплыли уже на проде.
Опишите задачу в свободной форме: какие шлюзы, какие сценарии оплаты, что уже работает. Этого достаточно, чтобы мы вернулись с оценкой, планом и сроками внедрения.







