Вайбкодинг кардинально изменил разработку цифровых продуктов. Сегодня, чтобы добавить на сайт прием платежей через Apple Pay, Google Pay или банковские карты, больше не нужно нанимать бэкенд-разработчика или самостоятельно разбираться в криптографических протоколах. Достаточно уметь четко сформулировать бизнес-логику своему AI-агенту (Codex, Antigravity, Cursor или Claude Code) и предоставить ему правильный контекст технической документации.
В этом руководстве разобран полный жизненный цикл подключения интернет-эквайринга Monobank к вашему проекту: от прохождения комплаенса банка и настройки кабинета предпринимателя до генерации защищенного платежного шлюза с криптографической верификацией подписей ECDSA в вебхуках, обработки состояния гонки в интерфейсе и набора обязательных автотестов.
Видеоверсия руководства: Если вы предпочитаете наглядный формат, посмотрите подробное практическое видео на YouTube →, где весь процесс показан на живом экране от первого промпта до реального списания средств.
Пакет агентских скилов: Для максимальной точности генерации кода скачайте официальный пакет знаний для вашего AI-ассистента:
Скачать полный архив monobank-acquiring.zip (38 KB) →
1. Архитектура Hosted Checkout и жизненный цикл платежа
Monobank Acquiring работает по схеме Hosted Checkout (оплата на защищенной странице банка). Это означает, что вашему сайту не требуется сложная и дорогая сертификация безопасности PCI DSS, поскольку платежные данные карт пользователь вводит непосредственно на защищенном домене банка.
Официальная документация Monobank для AI-инструментов доступна по адресу monobank.ua/api-docs/acquiring/dev/ai-tools/docs--ai-prompts →. Сохраните эту ссылку как канонический первоисточник технических требований банка.
1.1. Базовый платежный сценарий
- 1. Создание счета (
invoice/create): Клиент на вашем сайте выбирает товар или тариф. Серверный обработчик отправляетPOST-запрос к Monobank API с суммой, назначением платежа и обратными адресами. Банк возвращает уникальныйinvoiceIdи ссылкуpageUrl. - 2. Редирект на платежную страницу: Клиент перенаправляется на
pageUrl, где оплачивает заказ через Apple Pay, Google Pay, приложение Monobank или ввод данных банковской карты. - 3. Получение Webhook: После завершения оплаты Monobank автоматически отправляет
POST-запрос на ваш заранее настроенныйwebHookUrlсо статусом транзакции и криптографической цифровой подписью. - 4. Фиксация статуса (Terminal States): Ваш сервер обязан реагировать на два финальных статуса:
success(платеж успешен, открываем доступ или отправляем заказ) иfailure(ошибка или отклонение платежа банком).
Статус expired (истекло время действия ссылки) не генерирует вебхук. Если клиент закрыл платежную страницу и не оплатил, проверять состояние заказа необходимо через резервный опрос API (polling).
2. Юридические предпосылки и чек-лист комплаенса сайта для ФЛП
Даже если код оплаты написан безупречно, служба безопасности и финансового мониторинга Monobank не активирует боевой прием платежей, если сайт не соответствует требованиям законодательства Украины и правилам платежных систем Visa/Mastercard.
2.1. Банковские условия
- Открытый счет ФЛП или юрлица в Monobank: Эквайринг подключается исключительно к предпринимательским счетам. На личную черную или белую карту физлица принимать коммерческие платежи по закону запрещено.
- Подходящие КВЭДы: В регистрационных данных ФЛП должны быть указаны коды деятельности для интернет-торговли или услуг (например,
47.91— Розничная торговля через интернет,62.01/62.02— Компьютерное программирование и консультации,85.59— Другие виды образования).
2.2. Обязательный чек-лист страниц перед подачей на модерацию
Перед тем как подать заявку на активацию интернет-терминала, убедитесь, что на вашем сайте есть следующие разделы (обычно размещаются в футере):
- Публичная оферта (Договор публичной оферты): Описание предмета договора, момента заключения сделки, прав и обязанностей сторон.
- Политика конфиденциальности (Privacy Policy): Четкое описание того, какие данные клиентов собираются и как они защищаются согласно закону «О защите персональных данных».
- Условия возврата средств и доставки: Порядок возврата товара или средств в течение 14 дней по Закону Украины «О защите прав потребителей», а для цифровых услуг/подписок — правила отказа от услуги.
- Полные реквизиты предпринимателя в футере: Наименование ФЛП или ООО, ИНН / ЕГРПОУ, юридический адрес, контактный номер телефона и рабочий e-mail службы поддержки.
- Прозрачные цены и описание: Каждая кнопка оплаты должна иметь фиксированную цену в гривнах (UAH) и понятное описание того, за что именно платит клиент.
Если на сайте нет оферты, реквизитов ФЛП или вместо цен стоят заглушки «по договоренности», служба безопасности Monobank отклонит регистрацию терминала на этапе проверки.
3. Создание веб-терминала в кабинете Monobank Бизнес
Для взаимодействия с платежным API вам необходим персональный ключ доступа — X-Token. Он генерируется бесплатно внутри личного кабинета предпринимателя.
3.1. Пошаговый алгоритм открытия кассы
- Авторизация: Перейдите на web.monobank.ua → и войдите с помощью QR-кода через приложение Monobank на смартфоне.
- Переход к кассе: В левом навигационном меню выберите раздел «Касса».
- Добавление инструмента: Нажмите кнопку «+ Добавить инструмент» и выберите вариант «Оплаты на сайте (собственная разработка)».
- Регистрация терминала: Введите название проекта (например,
Мой сайтилиОсновной терминал) и подтвердите операцию кнопкой «Подключить». - Генерация API-токена: Откройте созданный терминал, перейдите во вкладку «Интеграции / API Ключи», нажмите «Создать токен» и скопируйте сгенерированный ключ.
📍 Навигация в кабинете:
web.monobank.ua→Касса→+ Добавить инструмент→Оплаты на сайте (собственная разработка)
Создание платежного инструмента в кабинете Monobank Kasa📍 Получение API-ключа:
Касса→Ваш терминал→Интеграция→Создать X-Token
Модальное окно создания и копирования токена доступа X-Token3.2. Железные правила безопасности токенов
- Никакого хардкода в клиентском коде:
X-Tokenдает прямой доступ к управлению вашими финансами и возвратами. Его категорически запрещено хранить в открытом JavaScript/HTML или публиковать в открытых репозиториях GitHub. - Использование переменных окружения: Храните токен исключительно в файле
.envна сервере под именемMONOBANK_TOKENили в разделе Secrets вашей хостинг-платформы (Vercel, Render, Railway, Replit, Lovable). - Тестовый токен для разработки: Для начальных экспериментов банк предоставляет отдельный тестовый токен на странице api.monobank.ua →, который позволяет симулировать транзакции без списания реальных средств.
4. Официальные AI-промпты Monobank для вайб-кодеров
Команда Monobank разработала набор официальных системных промптов для AI-агентов. Их главное преимущество — точное соответствие актуальным эндпоинтам банка.
Страница официальной документации Monobank для AI-инструментовГлавная ловушка новичков — сумма в копейках: Monobank API принимает все суммы исключительно в минимальных единицах валюты (копейках). $100\text{ грн} = 10,000\text{ копеек}$. Если вы передадите amount: 100, клиент заплатит всего 1 гривну.
4.1. Базовый промпт создания платежа
Скопируйте этот промпт и отправьте его в чат вашего AI-ассистента:
4.2. Промпт для настройки Webhook-обработчика
Без вебхука сервер не узнает о факте успешной оплаты, если клиент закроет браузер сразу после списания средств:
5. Пакет скилов monobank-acquiring: прокачка агента
Если ограничиться только коротким промптом, AI-ассистент напишет базовый код (примерно 6.8 из 10): кнопка сработает, но код не будет содержать проверки криптографических подписей, защиты от подмены цены и обработки сетевых ошибок.
Чтобы получить продакшен-уровень (9.8–10 баллов), в корень проекта добавляется специализированный пакет скилов monobank-acquiring.
Аудит платежной интеграции: сравнение безопасности до и после использования скила5.1. Анатомия и структура пакета скилов
| Файл скила | Что содержит и за какие задачи отвечает |
|---|---|
SKILL.md | Центральный манифест: базовый флоу, авторизация X-Token, типы данных и обработка ошибок 400, 403, 429, 500. |
quickstart.md | Быстрый старт: пошаговый туториал создания инвойса и фолбек-поллинга с готовыми curl-командами. |
invoice.md | Жизненный цикл счетов: эндпоинты создания инвойса, проверки статуса, отмены и инвалидации ссылок. |
webhook.md | Криптографическая защита: математически точная верификация ECDSA SHA-256 подписи из заголовка x-sign. |
payment.md | Прямые платежи: списание по сохраненному токену карты, синхронные транзакции и 3DS-проверка. |
wallet.md | Токенизация карт (Wallet): безопасное сохранение платежного средства клиента в хранилище банка для оплат в один клик. |
fiscal.md | Чеки и пРО: описание структуры товарной корзины basketOrder, расчет налогов, скидок и экспорт чеков в PDF. |
statement.md | Выписки и аналитика: получение реестра успешных операций за выбранный период с расчетом банковских комиссий. |
merchant.md | Данные мерчанта: получение публичного ключа банка, управление субмерчантами и кассирами. |
examples/ | Готовые серверы: рабочие примеры серверов на 6 языках (Node.js, Python, Go, PHP, C#, Java). |
6. Практическая реализация: динамический платежный флоу
В каждом проекте своя структура: цифровые консультации с фиксированными тарифами, интернет-магазин с динамической корзиной или простой лендинг с кнопкой подписки.
Главная ошибка новичков — жестко «зашивать» сумму (например, 1000 грн) прямо в клиентский код кнопки или в тело POST-запроса из браузера. Этот подход создает критическую уязвимость безопасности.
6.1. Принцип безопасности: динамическая цена вместо хардкода
-
Никогда не доверяйте сумме из браузера: Если клиентский JavaScript отправляет на сервер
{ price: 1000 }, злоумышленник через DevTools или Postman может подменить это значение на{ price: 1 }и приобрести товар за 1 гривну. -
Сервер — единственный источник правды (Single Source of Truth): Фронтенд передает на бэкенд только идентификатор товара (
productId), выбранный тариф (planId: "pro") или массив идентификаторов корзины (items: [{ id: "book_1", qty: 2 }]). -
Автоматическая конвертация в копейки: Сервер извлекает актуальную стоимость из конфигурационного файла или базы данных и самостоятельно умножает ее на 100:
$$\text{amount} = \text{Math.round}(\text{realPrice} \times 100)$$
6.2. Универсальный AI-промпт для адаптации под любой проект
Скопируйте этот промпт и отправьте его в чат вашего AI-агента (Codex, Antigravity, Cursor или Claude Code). Агент сам просканирует файлы вашего сайта, найдет существующие кнопки и цены и построит надежную интеграцию:
Работа AI-агента в IDE с анализом структуры и созданием динамического эндпоинта6.3. Архитектурный шаблон бэкенда создания инвойса
typescript// app/api/checkout/create-invoice/route.ts import { NextResponse } from "next/server"; const PRODUCTS_CATALOG: Record<string, { title: string; priceUah: number }> = { plan_starter: { title: "Тариф Starter", priceUah: 490 }, plan_pro: { title: "Тариф Pro", priceUah: 990 }, plan_vip: { title: "Тариф VIP", priceUah: 2490 }, }; export async function POST(req: Request) { try { const { productId } = await req.json(); // 1. Валидация: цена формируется исключительно на бэкенде const product = PRODUCTS_CATALOG[productId]; if (!product) { return NextResponse.json({ error: "Выбранный товар или тариф не найден" }, { status: 400 }); } const amountInKopecks = Math.round(product.priceUah * 100); const orderReference = `order_${productId}_${Date.now()}`; const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://mysite.com"; // 2. Запрос к Monobank API const response = await fetch("https://api.monobank.ua/api/merchant/invoice/create", { method: "POST", headers: { "X-Token": process.env.MONOBANK_TOKEN!, "Content-Type": "application/json", }, body: JSON.stringify({ amount: amountInKopecks, ccy: 980, // Гривна (ISO 4217) merchantPaymInfo: { reference: orderReference, destination: `Оплата: ${product.title}`, comment: `Заказ ${orderReference}`, }, redirectUrl: `${siteUrl}/payment-result?ref=${orderReference}`, webHookUrl: `${siteUrl}/api/payment/webhook`, validity: 3600, // Счет действует 1 час }), }); const data = await response.json(); if (!response.ok) { return NextResponse.json({ error: data.errText || "Ошибка банка при создании счета" }, { status: response.status }); } return NextResponse.json({ checkoutUrl: data.pageUrl, invoiceId: data.invoiceId }); } catch (error) { return NextResponse.json({ error: "Внутренняя ошибка инициализации платежа" }, { status: 500 }); } }
7. Безопасная обработка вебхуков и криптографическая подпись ECDSA
Самая ответственная часть любой финансовой интеграции — верификация уведомлений об оплате. Злоумышленник может отправить поддельный HTTP-запрос на ваш адрес /api/payment/webhook с фальшивым сообщением об успехе.
Чтобы предотвратить это, Monobank подписывает каждый вебхук с помощью асимметричного алгоритма ECDSA (кривая secp256r1 / SHA-256) и передает сигнатуру в HTTP-заголовке x-sign.
7.1. Почему JSON.stringify ломает верификацию подписи
Критическая ловушка rawBody: Для проверки подписи ECDSA требуется строго оригинальный поток байтов, который отправил сервер Monobank. Если вы попытаетесь распарсить JSON и снова вызвать JSON.stringify(req.body), порядок ключей, пробелы или переносы строк изменятся. Это приведет к другому хешу SHA-256, и проверка подписи гарантированно вернет ошибку!
7.2. Реализация верификации вебхука
typescript// app/api/payment/webhook/route.ts import { NextResponse } from "next/server"; import crypto from "crypto"; let cachedPubKey: string | null = null; async function getMonobankPubKey(token: string): Promise<string> { if (cachedPubKey) return cachedPubKey; const res = await fetch("https://api.monobank.ua/api/merchant/pubkey", { headers: { "X-Token": token }, next: { revalidate: 86400 }, // Кешируем публичный ключ на 24 часа }); const data = await res.json(); cachedPubKey = `-----BEGIN PUBLIC KEY-----\n${data.key}\n-----END PUBLIC KEY-----`; return cachedPubKey; } export async function POST(req: Request) { const signature = req.headers.get("x-sign"); if (!signature) { return new NextResponse("Missing x-sign header", { status: 400 }); } // 1. Получаем сырое неизмененное тело запроса в виде текста const rawBody = await req.text(); try { const pubKey = await getMonobankPubKey(process.env.MONOBANK_TOKEN!); // 2. Верифицируем подпись ECDSA SHA-256 const verifier = crypto.createVerify("SHA256"); verifier.update(rawBody); const isValid = verifier.verify(pubKey, Buffer.from(signature, "base64")); if (!isValid) { console.error("Вебхук отклонен: недействительная подпись x-sign"); return new NextResponse("Invalid signature", { status: 400 }); } // 3. Парсим данные только после успешной проверки криптографии const payload = JSON.parse(rawBody); const { invoiceId, status, amount, reference } = payload; if (status === "success") { // Активируем заказ в базе данных с проверкой дубликатов (идемпотентность) console.log(`Заказ ${reference} (${invoiceId}) оплачен на сумму ${amount / 100} грн`); } return new NextResponse("OK", { status: 200 }); } catch (error) { console.error("Ошибка обработки вебхука:", error); return new NextResponse("Internal verification error", { status: 500 }); } }
8. Локальное тестирование вебхуков (Localhost & Cloudflare Tunnels)
Частая проблема вайб-кодеров: при запуске сервера на http://localhost:3000 банк не может доставить вебхук, так как локальная машина не имеет публичного IP-адреса в интернете.
Monobank отправляет вебхуки исключительно на публичные адреса с действующим протоколом HTTPS. Чтобы протестировать полный цикл на своем компьютере, пробросьте безопасный туннель.
8.1. Быстрый запуск туннеля (без регистрации и бесплатно)
bash# Мгновенный HTTPS-туннель на 3000 порт без установки утилит npx untun@latest tunnel --port 3000
После запуска вы получите временный публичный HTTPS-адрес:
https://your-tunnel-name.trycloudflare.com
8.2. Настройка вебхука для локального теста
В коде создания инвойса во время разработки укажите полученный адрес:
Теперь при тестовой оплате банк отправит реальный вебхук прямо на ваш локальный сервер в терминале, и вы сможете убедиться, что подпись x-sign успешно проходит проверку.
9. UX страницы возврата и решение состояния гонки (Race Condition)
Когда клиент оплачивает счет через приложение Monobank или Apple Pay, банк возвращает его на адрес redirectUrl (/payment-result?ref=...) мгновенно.
Однако сетевой вебхук от сервера банка к вашему серверу может задержаться на 1–2 секунды из-за сетевых маршрутов. Если страница результата сразу проверит статус в базе данных, она рискует показать: «Заказ не оплачен», что вызовет панику у клиента (деньги с карты списаны, а сайт сообщает об отсутствии оплаты).
9.1. Инженерный паттерн решения гонки
- Начальное состояние загрузки: Страница открывается с нейтральным статусом:
«Подтверждаем оплату в банке...»и анимированным индикатором. - Короткий поллинг (Short Polling): Фронтенд делает до 5 быстрых запросов к собственному API каждые 1.5 секунды (
/api/orders/check-status?ref=...), ожидая, пока вебхук изменит статус заказа в базе наsuccess. - Фолбек: Если за 8 секунд вебхук не дошел, клиент видит сообщение: «Платеж принят в обработку. Как только банк подтвердит транзакцию, доступ откроется автоматически».
9.2. Готовый React-компонент страницы результата
10. Расширенные возможности: встроенное пРО, холдирование и Wallet
Monobank Acquiring предоставляет полный спектр инструментов для сложных бизнес-моделей:
Финальная страница оплаты Monobank Hosted Checkout с Apple Pay и картами10.1. Программное РО: бесплатный встроенный Checkbox в Кассе Monobank
Для большинства украинских ФЛП 2-й и 3-й групп обязательная фискализация онлайн-продаж является юридическим требованием.
Главное преимущество Monobank для предпринимателей — бесплатная встроенная интеграция с сервисом пРО Checkbox:
- Включение в один клик: В кабинете
web.monobank.uaперейдите в настройки созданного терминала и активируйте переключатель «Фискализация через Checkbox». Monobank сам бесплатно создает кассу и подписывает чеки вашим КЭП. - Автоматическая фискализация: Если у вас стандартный каталог товаров с одинаковой ставкой, вам даже не нужно менять код создания платежа — чек автоматически формируется по полю
destinationв назначении платежа. - Расширенная фискализация через API: Если вы продаете товары с разными ставками НДС или подакцизные позиции с кодами УКТ ВЭД, передавайте массив
basketOrderвнутри объектаmerchantPaymInfo:
10.2. Двухэтапная оплата (Hold)
Если вы продаете физические товары, которые могут отсутствовать на складе, используйте режим холдирования:
- Блокировка: Передайте
paymentType: "hold"при создании инвойса. Средства замораживаются на карте покупателя на срок до 9 дней. - Списание (Finalize): После проверки наличия товара вызовите
/api/merchant/invoice/finalize. Можно списать как полную сумму, так и меньшую (например, если одной позиции не оказалось). - Отмена: Если товара нет, вызовите
/api/merchant/invoice/cancel— платеж отменяется без каких-либо банковских комиссий для покупателя.
10.3. Сохранение карт и подписки (Wallet)
Если вы запускаете SaaS-сервис с регулярной ежемесячной подпиской, передайте saveCardData: true при создании первого инвойса. После успешной оплаты в вебхуке вернется walletId. Используйте этот токен для последующих автоматических списаний без повторного ввода данных карты клиентом.
11. Матрица безопасности и автоматические тесты (Vitest / Jest)
Оплата — это зона максимальной финансовой ответственности. Ошибка в обычной кнопке вызывает раздражение, но ошибка в платежном шлюзе приводит к прямым материальным убыткам или блокировке кассы банком.
Перед переходом в боевой режим запустите тестовую матрицу безопасности:
| Тест безопасности | Что именно проверяется | Ожидаемое поведение системы |
|---|---|---|
| 1. Защита от изменения цены (Price Tampering) | Клиент отправляет productId: "vip", но пытается подсунуть в запрос amount: 100 (1 грн) | Сервер игнорирует поле amount от клиента, берет реальную цену из конфига (2490 грн = 249 000 коп). Если productId подделан — возвращает HTTP 400. |
| 2. Блокировка без подписи | На эндпоинт /api/payment/webhook поступает POST-запрос без заголовка x-sign | Запрос немедленно блокируется с ответом HTTP 400 Bad Request. Никакой обработки заказа не происходит. |
| 3. Блокировка фальшивых подписей | Злоумышленник отправил фейковую подпись в x-sign со статусом status: "success" | Проверка crypto.verify(SHA256, ...) возвращает false. Сервер возвращает HTTP 400/401, доступ или товар не выдаются. |
| 4. Идемпотентность вебхука (Duplicate Delivery) | Monobank отправил одинаковый вебхук success дважды или трижды подряд из-за сетевого лага | Доступ или товар активируются только один раз. Повторный вебхук не вызывает дублирования выдачи, возвращая банку HTTP 200 OK. |
| 5. Устойчивость к состоянию гонки (Race Condition) | Вебхук от банка пришел раньше, чем завершился начальный HTTP-запрос создания инвойса в базе | Обработчик вебхука устойчив к отсутствию записи в БД (использует UPSERT или создает заказ на лету). |
| 6. Соблюдение рейт-лимитов (Rate Limit Buffer) | При сбое вебхука запускается поллинг статуса /api/merchant/invoice/status | Запросы выполняются с паузой не менее 15 секунд, что защищает терминал от блокировки HTTP 429 Too Many Requests. |
11.1. Готовый набор автоматических тестов для вашего проекта
Попросите своего AI-агента добавить следующий тестовый набор в проект (monobank-acquiring.test.ts):
12. Финальный инженерный чек-лист перед запуском
Проверьте свой платежный модуль по этим 10 пунктам перед переключением на боевые платежи:
- Комплаенс сайта: В футере добавлены ссылки на Публичную оферту, Политику конфиденциальности, Условия возврата и полные реквизиты ФЛП с ИНН.
- Суммы в копейках: Все значения
amountумножены на 100 ($1\text{ грн} = 100\text{ коп}$) черезMath.round. - Безопасность ключа: Токен вынесен в
.envпод именемMONOBANK_TOKENи добавлен в.gitignore. - Цены из бэкенда (SSOT): Клиент передает только идентификатор товара, сумма берется исключительно из базы или конфига.
- Обработка rawBody: Вебхук верифицирует оригинальный текстовый или бинарный буфер (
req.text()илиreq.rawBody), избегая повторной сериализации черезJSON.stringify. - Криптографическая защита: Вебхук верифицирует подпись
x-signчерез публичный ключ банка алгоритмомSHA256ECDSA. - Публичный URL для вебхука: Сервер доступен извне через действительный SSL-сертификат HTTPS (для локальных тестов подходит Cloudflare Tunnel или ngrok).
- Идемпотентность: Повторный приход одного и того же вебхука не вызывает повторную выдачу товара или подписки.
- UX страницы результата: На
/payment-resultнастроен лоадер ожидания и короткий поллинг статуса, чтобы защитить пользователя от состояния гонки. - Реальный платеж на 1–5 грн: Проведена успешная тестовая транзакция реальной картой, проверено списание средств и отображение в кабинете банка.