Чому інтеграція платіжних шлюзів у 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 та підтвердження особи платника;
- чи вже є акаунти в платіжних системах або їх потрібно заводити;
- як влаштований облік: чи потрібно синхронізувати оплати з бухгалтерією та складом;
- які середовища та доступи є зараз і хто на вашій стороні ухвалює рішення щодо платежів.
За цими даними ми повертаємося з оцінкою та планом: склад модулів-провайдерів, схема проходження платежу, обсяг доопрацювань щодо замовлень і повернень, строки впровадження за етапами та точки, де потрібні ваші підтвердження.
Якщо по ходу з'ясовуються неочевидні сценарії — фіксуємо їх окремо, щоб вони не спливли вже на проді.
Опишіть завдання у вільній формі: які шлюзи, які сценарії оплати, що вже працює. Цього достатньо, щоб ми повернулися з оцінкою, планом і строками впровадження.







