Что такое MonoPay/плата от mono и как выглядит интеграция
Короче: вы создаете счет на своем сервере через API monobank, вы получаете invoiceId и pageUrl в ответ, вы отправляете пользователя заплатить и фиксируете результат в соответствии с событием, которое monobank отправляет на ваш webHookUrl. Сам monobank прямо рекомендует рассматривать вебхук как основной механизм получения статусов, а метод проверки статуса использовать только как "план Б" на случай рассинхронизации.
Основные точки подключения:
-
Подключение эквайринга в офисе компании и получение токена (X-Token).
-
Создать счет (
/api/merchant/invoice/create) с суммой, описанием иredirectUrlиwebHookUrl. -
Оплата на
pageUrl(карта/Apple Pay/Google Pay/платеж в приложении — зависит от сценария). -
Вебхук от monobank об изменении статуса счета.
-
Проверка подписи вебхук (ECDSA, заголовок
X-Sign). -
Подтверждение в вашей системе: вы меняете статус заказа/подписки/аккаунта.
-
Резервная проверка статуса (
/api/merchant/invoice/status), если необходимо.
Получение регистрации и подключения: что делать перед написанием кода
Чтобы подключить эквайринг, вам необходимо быть зарегистрированным как ИП и иметь открытый единственный счет ИП в моно.
Далее происходит авторизация в веб-кабинете и заполнение информации о бизнесе (вид деятельности, ссылка на сайт/социальные сети), после чего вы выбираете и подключаете платежные инструменты.
Это важно: если эквайринг не подключен, у вас просто не будет токена для API (он «становится доступным после подключения эквайринга»).
Получение документации содержит четкие этапы получения токена:
- войдите в веб-офис;
- перейдите на вкладку нужного способа оплаты и нажмите «Настроить»;

- Теперь вы можете создать токен.

Затем вы передаете этот токен в заголовке X-Token для запросов API.
Тестовая среда
монобанк предоставляет возможность работать в тестовой среде: для этого нужен тестовый токен, а данные "карты" могут быть любыми действительными (с действительным номером по алгоритму Luna) - финансовая авторизация будет не состоится, но интеграцию вы прогоните.
Архитектура интеграции: что хранить в базе данных
Чтобы контролировать интеграцию (а не разбираться потом, почему "деньги пришли, но заказ не оплачен"), советую сохранять как минимум: платежи / счета
-
id(внутренний) -
order_id(или другой идентификатор компании) -
invoice_id(из monobank) -
сумма(в минимальных единицах, например копейках) -
ccy(ISO 4217, по умолчанию 980) -
статус(создано/обработка/успех/истекло/сбой и т. д.) -
created_at,updated_at -
modified_date(с monobank — критично для правильного порядка веб-перехватчиков) -
raw_last_payload(необязательно, но полезно для отладки)
Почему важна modifiedDate?
monobank предупреждает, что веб-перехватчики не гарантируются по отдельности, и теоретически status=success может предшествовать status=processing. Следовательно, «правильная истина» — это событие с большей modifiedDate.
Также подсчитывается количество повторных попыток: серверная часть сбора данных делает до 3 попыток веб-перехватчика POST, пока не получит 200 ОК. Это означает: ваш обработчик веб-перехватчика должен быть идемпотентом (получение того же состояния не должно нарушать работу системы).
Счет/создание: ключевые параметры и пример запроса
Конечная точка
Официальный метод создания учетной записи:
POST https://api.monobank.ua/api/merchant/invoice/create
Заголовки
-
X-Token: <токен из кабинета> -
необязательно:
X-Cms/X-Cms-Version— если вы делаете модуль для CMS (полезно, если интеграция упакована в виде плагина).
Тело запроса: минимальные требования
Обычно используется минимальный минимум:
-
сумма— сумма в минимальных единицах (для гривны — копейки) -
ccyпо умолчанию равен 980 -
merchantPaymInfo— информация о «человеческом» платеже:ссылка,назначение,комментарий, корзинаbasketOrder(особенно актуально для электронной коммерции) -
redirectUrl— куда вернуть клиента после завершения платежа (успех или ошибка) -
webHookUrl— куда monobank будет отправлять статус при изменении (кромеexpired) -
срок действия— время жизни аккаунта в секундах (по умолчанию 24 часа) -
PaymentType—дебетилиудержание(если требуется удержание с дальнейшей финализацией)
В ответ вы получаете:
-
invoiceId -
pageUrl(ссылка на оплату)
Пример для PHP 8.x (cURL)
$token = getenv('MONO_X_TOKEN');
$payload = [
'amount' => 19900, // 199.00 UAH -> в копейках
'ccy' => 980,
'merchantPaymInfo' => [
'reference' => 'ORDER-100045', // твой уникальный референс
'destination' => 'Оплата заказа #100045',
'comment' => 'Спасибо за покупку',
'basketOrder' => [
[
'name' => 'Підписка PRO, 1 місяць',
'qty' => 1,
'sum' => 19900,
'total' => 19900,
'unit' => 'шт.',
'code' => 'SUB-PRO-1M',
],
],
],
'redirectUrl' => 'https://example.com/pay/return',
'webHookUrl' => 'https://example.com/pay/mono/webhook',
'validity' => 3600,
'paymentType' => 'debit',
];
$ch = curl_init('https://api.monobank.ua/api/merchant/invoice/create');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Token: ' . $token,
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new RuntimeException("Monobank create invoice failed: HTTP $httpCode; body=$response");
}
$data = json_decode($response, true);
$invoiceId = $data['invoiceId'];
$pageUrl = $data['pageUrl'];
// 1) сохраняешь invoiceId в БД вместе с order_id
// 2) редиректиш клиента на $pageUrl
header('Location: ' . $pageUrl, true, 302);
exit;
Семантика полей и сама конечная точка соответствуют документации: сумма в минимальных единицах, redirectUrl, webHookUrl, а ответ содержит invoiceId и pageUrl.
Возврат пользователя: redirectUrl
После оплаты пользователь будет перенаправлен на ваш redirectUrl (GET). Страницы часто делаются здесь:
-
“Платеж прошел успешно, заказ обрабатывается”
-
"Ошибка платежа, попробуйте еще раз"
Но это принципиально важно: не обязательно подтверждать оплату только на основании факта редиректа.
Редирект — это клиентский UX, который можно прервать, заменить, запустить или пользователь может просто закрыть вкладка.
На стороне сервера необходимо обработать:
<ул>вебхук со статусом (с проверкой подписи) илирезервное копирование: запрос статуса счета.
Вебхук: прием событий и проверка подписи (ECDSA, X-Sign)
Как появляются вебхуки
monobank отправляет POST на webHookUrl при изменении статуса (кроме expired), тело запроса идентично ответу метода "Статус аккаунта", а в заголовках присутствует X-Sign — подпись тела вебхука по стандарту ECDSA.
Также важны две особенности:
- до 3 попыток доставки до
200 ОК; - события могут происходить не в хронологическом порядке, сосредоточьтесь на
modifiedDate.
Получение открытого ключа
В примере из документации указано, что публичный ключ для проверки необходимо получить через конечную точку https://api.monobank.ua/api/merchant/pubkey.
Практический пример проверки PHP
Ниже приведен пример, адаптированный к реальному обработчику веб-перехватчика. Логика:
-
прочитать необработанное тело;
-
возьмите
X-Sign; -
подтвердить через
openssl_verify; -
если ок - парсим JSON и обновляем статус в базе с учетом
modifiedDate.
function getMonoPubKeyPem(string $token): string
{
$ch = curl_init('https://api.monobank.ua/api/merchant/pubkey');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-Token: ' . $token],
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code !== 200) {
throw new RuntimeException("Cannot fetch monobank pubkey: HTTP $code; body=$resp");
}
// В документации примеры работают с base64-представлением PEM.
// Здесь допустим, что API возвращает base64-строку, которую нужно decode.
return base64_decode(trim($resp));
}
$token = getenv('MONO_X_TOKEN');
$rawBody = file_get_contents('php://input');
$xSign = $_SERVER['HTTP_X_SIGN'] ?? '';
if ($xSign === '') {
http_response_code(400);
echo 'Missing X-Sign';
exit;
}
$signature = base64_decode($xSign);
$pubKeyPem = getMonoPubKeyPem($token);
$pubKey = openssl_get_publickey($pubKeyPem);
if ($pubKey === false) {
http_response_code(500);
echo 'Invalid pubkey';
exit;
}
// OPENSSL_ALGO_SHA256 – как в примерах документации
$ok = openssl_verify($rawBody, $signature, $pubKey, OPENSSL_ALGO_SHA256);
if ($ok !== 1) {
http_response_code(401);
echo 'Invalid signature';
exit;
}
$payload = json_decode($rawBody, true);
if (!is_array($payload) || empty($payload['invoiceId'])) {
http_response_code(400);
echo 'Bad payload';
exit;
}
$invoiceId = $payload['invoiceId'];
$status = $payload['status'] ?? 'unknown';
$modifiedDate = $payload['modifiedDate'] ?? null;
// Далее - твоя бизнес-логика:
// 1) найти запись в БД по invoiceId
// 2) сравнить modifiedDate (не обновлять более старым payload)
// 3) если status=success -> отметить заказ как оплаченный
// 4) вернуть 200 ОК, чтобы monobank не ретра webhook
http_response_code(200);
echo 'OK';
Важные детали (они из документации):
X-Sign— подпись ECDSA тела веб-перехватчика;modifiedDate— основной критерий того, какой статус является релевантным;- Тело вебхука соответствует структуре «статус счета», которая содержит
invoiceId,статус, суммы, причины ошибок и т. д.
Поля статуса и ответа: что на самом деле использовать в бизнес-логике
Метод статуса счета возвращает полезный набор полей, в том числе:
-
invoiceId -
статус(например,создано) -
failureReasonиerrCode(когда платеж не прошел) -
сумма,ccy,finalAmount -
Дата создания,Дата модификации -
ссылка,назначение -
payInfo(маскированная карта,rrn,tranId, и т. д. — полезно для животных) -
cancelList, другие объекты
В примере из документации эти поля непосредственно видны в образце ответа.
Практическая рекомендация:
для большинства сценариев электронной коммерции вы используете как минимум:
-
статус -
invoiceId -
modifiedDate -
finalAmount(если для вас возможны частичные списания/комиссии/удержания)
И failureReason / errCode — чтобы показать человеку адекватное сообщение («Недостаточно средств», «Неверный CVV» и т. д.) и не отклонить платеж в «волшебство, которое не работает».
Проверка статуса платежа (счет/статус) как «страховка»
Конечная точка:
-
GET /api/merchant/invoice/status?invoiceId={invoiceIdс заголовкомX-Token
monobank прямо говорит: не используйте этот сервис как основной механизм после транзакции; лучше, чем вебхуки. Но для случаев «вебхук не пришел» или «был простой на стороне продавца» это подходящий инструмент для восстановления согласованности.
Пример PHP
$token = getenv('MONO_X_TOKEN');
$invoiceId = $_GET['invoiceId'];
$url = 'https://api.monobank.ua/api/merchant/invoice/status?invoiceId=' . urlencode($invoiceId);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-Token: ' . $token],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new RuntimeException("Monobank status failed: HTTP $httpCode; body=$response");
}
$data = json_decode($response, true);
// Далее: сверяешь data['status'], data['modifiedDate'], data['finalAmount'] и обновляешь БД
Готовые модули и интеграции: когда лучше не писать код с нуля
Если ваш сайт уже работает на популярной CMS/конструкторе, иногда лучшим решением будет не изобретать велосипед, а предоставить готовую интеграцию.
Официальная страница готовых интеграций
монобанк имеет централизованную страницу, где готовые интеграции и платежные модули собраны для различных систем ("по нажатию на логотип партнера, можно перейти к инструкции по настройке").
Это «минимальный пользовательский» вариант: вставил токен в настройках — и он работает.
Пример: WooCommerce/WordPress
Существует официальный плагин для WordPress/WooCommerce (Monobank WP Payment Plugin), который позиционируется как официальный модуль для подключения к Интернету эквайринг.
Пример: OpenCart
В экосистеме OpenCart также есть модули для MonoPay (включая общедоступные каталоги модулей и форумы).
Когда модуль — лучший выбор
Выберите модуль, если:
-
вам необходимо «быстрый старт оплаты» без пользовательской логики;
-
вы не планируете сложные сценарии (удержание, токенизация, разделение платежей, выставление счетов);
-
вам нужна поддержка обновлений «внутри CMS».
Когда требуется интеграция API
Напишите интеграцию через API, если:
-
у вас собственный бэкенд (Laravel/CI/Symfony/Node/Go и т. д.) и нетипичная логика платежей;
-
требуются сложные сценарии (например, удержание/финализация, токенизация, несколько продуктов с собственными правилами);
-
вам нужен полный контроль: собственные повторные попытки, сверки, аудит событий, логика защиты от мошенничества.
Практический контрольный список «сделано и забыто»: как обеспечить интеграцию качества производства
-
Токены хранятся только в секретах/ENV (не в коде, не в репозитории).
-
referenceсделайте его уникальным на вашей стороне (например,ORDER-). Это очень помогает при сравнении. -
Конечная точка веб-перехватчика:
-
должен ответить быстро
200 ОК; -
должен быть идемпотентным;
-
необходимо подтвердить
X-Sign.
-
-
Логика обновления статуса:
-
не доверяйте порядку поступления событий;
-
всегда сравнивайте
modifiedDateи не перезаписывайте «новый» статус со «старым».
-
-
Выполните задание по сверке (корона/очередь): раз в N минут проверяйте "висящие" счета через
invoice/status(только для тех, у кого еще не было вебхук). Это соответствует назначению метода status как резервного. -
Перед запуском в рабочей версии:
-
запустить тестовую среду с тестовым токеном;
-
убедитесь, что ваши URL-адреса доступны из Интернета и не заблокированы WAF/Cloudflare;
-
проверьте, что «успех» действительно преобразует заказ в «оплаченный» только на сервере (вебхук/статус), а не «путем перенаправления».
-
Интеграция MonoPay (плата моно) хорошо вписывается в современную архитектуру «счет → страница оплаты → вебхук → подтверждение в базе данных», но качество решения определяется тремя вещами:
-
правильное подключение и управление токенами в офисе компании;
-
правильная обработка веб-перехватчиков (подпись +
modifiedDate+ идемпотентность); -
имейте резервный механизм выверки через
счет/статусна случай редкой десинхронизации.