El vibe-coding ha transformado radicalmente el desarrollo de productos digitales. Hoy en día, añadir pagos con tarjeta, Apple Pay o Google Pay a su sitio web ya no exige contratar a un desarrollador backend especializado ni descifrar complejos protocolos criptográficos. Solo necesita articular claramente la lógica de negocio a su agente de IA (Codex, Antigravity, Cursor o Claude Code) y proporcionarle el contexto técnico adecuado.
Esta guía detalla el ciclo completo de integración de la pasarela de pagos Monobank Acquiring en su proyecto: desde los requisitos de cumplimiento bancario y la configuración del portal para empresas, hasta la generación de una pasarela protegida con verificación criptográfica de firmas ECDSA en webhooks, gestión de condiciones de carrera en frontend y pruebas automatizadas de seguridad.
Versión en vídeo: Si prefiere el aprendizaje visual, consulte el vídeo práctico paso a paso en YouTube →, donde se muestra todo el proceso en pantalla desde la primera instrucción hasta el cobro real de fondos.
Paquete de habilidades para agentes: Para maximizar la precisión en la generación de código, descargue el paquete oficial de conocimientos para su asistente de IA:
Descargar archivo completo monobank-acquiring.zip (38 KB) →
1. Arquitectura Hosted Checkout y ciclo de vida del pago
Monobank Acquiring opera bajo el modelo Hosted Checkout (el procesamiento del pago se realiza en una página segura del banco). Esto elimina la necesidad de costosas y complejas certificaciones PCI DSS en su servidor, ya que el cliente introduce sus datos bancarios directamente en el dominio cifrado de Monobank.
La documentación oficial de Monobank para herramientas de IA está disponible en monobank.ua/api-docs/acquiring/dev/ai-tools/docs--ai-prompts →. Guarde este enlace como fuente canónica de especificaciones técnicas.
1.1. Flujo básico de pago
- 1. Creación de factura (
invoice/create): El cliente selecciona un producto o tarifa. El backend emite una peticiónPOSTa la API de Monobank con el importe, concepto y URLs de retorno. El banco devuelve un identificador únicoinvoiceIdy un enlacepageUrl. - 2. Redirección a la pasarela de pago: El cliente es redirigido a
pageUrl, completando el pago cómodamente mediante Apple Pay, Google Pay, la aplicación de Monobank o introduciendo su tarjeta. - 3. Recepción de Webhook: Tras procesar el pago, Monobank envía automáticamente una petición
POSTa suwebHookUrlcon el estado de la transacción y una firma digital criptográfica. - 4. Confirmación de estados terminales: El servidor debe gestionar dos estados definitivos:
success(pago confirmado, desbloqueo de acceso o envío del pedido) yfailure(error o rechazo del pago por la entidad emisora).
El estado expired (caducidad del enlace de pago) no emite un webhook. Si el usuario cierra la pasarela sin abonar el importe, controle el abandono mediante sondeos periódicos (polling).
2. Requisitos legales y lista de verificación de cumplimiento web
Aunque el código esté perfectamente escrito, los departamentos de seguridad y prevención de blanqueo de Monobank no activarán el terminal en producción si el sitio web carece de la documentación legal exigida por la normativa vigente y los esquemas Visa/Mastercard.
2.1. Prerrequisitos bancarios
- Cuenta corporativa activa en Monobank (Autónomo o Sociedad): La pasarela solo se vincula a cuentas profesionales. Por ley, no se permite recibir pagos comerciales en tarjetas personales estándar.
- Epígrafes de actividad económica pertinentes: El registro mercantil o fiscal debe contemplar el comercio electrónico o la prestación de servicios digitales (por ejemplo, venta online, consultoría informática o formación).
2.2. Lista de comprobación previa a la moderación
Asegúrese de que su web incluye los siguientes apartados (habitualmente en el pie de página):
- Términos y Condiciones / Contrato de Adhesión: Objeto del servicio o producto, momento de perfeccionamiento del contrato, derechos y deberes.
- Política de Privacidad: Información precisa sobre el tratamiento de datos personales conforme al Reglamento General de Protección de Datos (RGPD) o leyes locales aplicables.
- Política de Devolución y Envíos: Plazos y condiciones de desistimiento (garantía legal de 14 días para consumidores) o política de cancelación de suscripciones digitales.
- Datos identificativos completos en el footer: Razón social, NIF / CIF / Código de registro fiscal, domicilio legal, teléfono y correo electrónico de atención al cliente.
- Precios transparentes y descripción: Cada botón de compra debe indicar un importe fijo y una descripción inequívoca del producto adquirido.
Si el portal carece de aviso legal, datos fiscales o utiliza precios ambiguos («a consultar»), Monobank denegará la activación del terminal de producción.
3. Creación del terminal web en Monobank Empresas
Para comunicarse con la API se necesita una clave secreta de autenticación: X-Token. Se genera de forma gratuita en el panel de control para empresas.
3.1. Pasos para abrir la caja virtual
- Autenticación: Acceda a web.monobank.ua → e inicie sesión escaneando el código QR con la app móvil de Monobank.
- Acceso a Kasa: En el menú lateral izquierdo, seleccione «Каса» (Caja).
- Añadir instrumento: Pulse «+ Додати інструмент» (+ Añadir instrumento) y seleccione la opción «Оплати на сайті (власна розробка)» (Cobros en web - Desarrollo propio).
- Registro del terminal: Asigne un nombre identificativo (por ejemplo,
Mi Tienda OnlineoPasarela Principal) y confirme con «Підключити» (Conectar). - Generación del token: Abra el terminal creado, diríjase a la pestaña «Інтеграції / API Ключі» (Integraciones / Claves API), haga clic en «Створити токен» (Crear token) y copie la clave secreta.
📍 Ruta en el panel:
web.monobank.ua→Каса→+ Додати інструмент→Оплати на сайті (власна розробка)
Creación del instrumento de cobro en Monobank Kasa📍 Obtención de la clave API:
Каса→Su Terminal→Інтеграція→Створити X-Token
Ventana modal de creación y copia del token X-Token3.2. Reglas fundamentales de seguridad para tokens
- Nunca incluya el token en código cliente:
X-Tokenotorga control total para gestionar fondos y emitir reembolsos. Jamás lo exponga en HTML/JavaScript ni lo suba a repositorios públicos de GitHub. - Variables de entorno estrictas: Guarde el token únicamente en el servidor dentro del archivo
.envbajo el nombreMONOBANK_TOKENo en la sección de secretos de su plataforma de hosting (Vercel, Render, Railway, Replit, Lovable). - Token de pruebas para desarrollo: Monobank proporciona un token de pruebas en api.monobank.ua → para simular transacciones de ensayo sin transferir fondos reales.
4. Prompts oficiales de Monobank para agentes de IA
El equipo de Monobank ha diseñado un catálogo de instrucciones predefinidas para modelos de IA, optimizadas para sus endpoints actuales.
Página de documentación de herramientas de IA de MonobankTrampa frecuente: importes en céntimos (kópeks): La API de Monobank expresa todas las cantidades en unidades monetarias mínimas. $100\text{ UAH} = 10,000\text{ céntimos}$. Si envía amount: 100, el cliente solo abonará 1 UAH.
4.1. Prompt básico para creación de facturas
Copie este prompt y péguelo en su asistente de IA:
4.2. Prompt para el controlador de Webhooks
Sin webhook, su servidor no podrá confirmar el pago si el usuario cierra el navegador nada más completarse la transacción:
5. Paquete de habilidades monobank-acquiring: optimización del agente
Un prompt simple produce un resultado funcional pero básico (aproximadamente 6.8 de 10): el botón responde, pero carece de verificación de firmas criptográficas, protección contra alteración de precios y gestión de latencias.
Para alcanzar calidad de grado empresarial (9.8–10 puntos), añada el paquete de habilidades monobank-acquiring a la raíz de su proyecto.
Auditoría de integración de pagos antes y después de aplicar la habilidad5.1. Anatomía del paquete de habilidades
| Archivo | Contenido y función operativa |
|---|---|
SKILL.md | Manifiesto central: flujo base, autenticación X-Token, esquemas de datos y códigos de error 400, 403, 429, 500. |
quickstart.md | Guía rápida: tutorial paso a paso para crear facturas y configurar sondeos de reserva con snippets curl. |
invoice.md | Ciclo de vida de facturas: endpoints para crear, consultar, cancelar e invalidar enlaces. |
webhook.md | Seguridad criptográfica: validación matemática exacta de firmas ECDSA SHA-256 en cabeceras x-sign. |
payment.md | Pagos directos: cargos recurrentes mediante token, pagos síncronos y verificación 3D Secure. |
wallet.md | Tokenización de tarjetas (Wallet): almacenamiento seguro de métodos de pago en el banco para compras en 1 clic. |
fiscal.md | Facturación y fiscalización: estructura de cesta basketOrder, cálculo de impuestos, descuentos y descarga de facturas en PDF. |
statement.md | Extractos y analítica: consulta del registro de transacciones con liquidación de comisiones bancarias. |
merchant.md | Datos de comercio: obtención de claves públicas, gestión de subcomercios y cajeros. |
examples/ | Servidores de referencia: ejemplos completos en 6 lenguajes (Node.js, Python, Go, PHP, C#, Java). |
6. Implementación práctica: arquitectura con precios dinámicos
Cada negocio presenta casuísticas distintas: tarifas cerradas de suscripción, comercios electrónicos con carritos variables o botones de contribución puntual.
El error más peligroso de principiante consiste en fijar el importe en el cliente o enviar el precio dentro de la petición POST del navegador.
6.1. Principio de seguridad: Precio dinámico en backend
-
Jamás confíe en el importe enviado por el navegador: Si el frontend envía
{ price: 1000 }, cualquier atacante puede interceptar la petición y sustituirlo por{ price: 1 }para obtener el servicio por una fracción de su valor. -
El servidor como única fuente de verdad (SSOT): La interfaz solo remite un identificador de producto (
productId), tarifa (planId: "pro") o array de cesta (items: [{ id: "book_1", qty: 2 }]). -
Conversión automática a céntimos: El backend obtiene el valor real de su base de datos o configuración y lo multiplica por 100:
$$\text{amount} = \text{Math.round}(\text{realPrice} \times 100)$$
6.2. Prompt universal para adaptar cualquier proyecto
Envíe esta instrucción a su agente de IA (Codex, Antigravity, Cursor o Claude Code) para que examine el proyecto y conecte la pasarela de forma blindada:
Agente de IA analizando la estructura del proyecto y generando el endpoint seguro6.3. Plantillas de servidor con precio dinámico
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: "Plan Básico", priceUah: 490 }, plan_pro: { title: "Plan Profesional", priceUah: 990 }, plan_vip: { title: "Plan VIP", priceUah: 2490 }, }; export async function POST(req: Request) { try { const { productId } = await req.json(); // 1. Validación: el precio se determina exclusivamente en el servidor const product = PRODUCTS_CATALOG[productId]; if (!product) { return NextResponse.json({ error: "Producto o plan no encontrado" }, { 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. Llamada a la API de Monobank 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, // Hryvnia ucraniana (ISO 4217) merchantPaymInfo: { reference: orderReference, destination: `Pago de: ${product.title}`, comment: `Pedido ${orderReference}`, }, redirectUrl: `${siteUrl}/payment-result?ref=${orderReference}`, webHookUrl: `${siteUrl}/api/payment/webhook`, validity: 3600, // Validez de 1 hora }), }); const data = await response.json(); if (!response.ok) { return NextResponse.json({ error: data.errText || "Error del banco al crear la orden" }, { status: response.status }); } return NextResponse.json({ checkoutUrl: data.pageUrl, invoiceId: data.invoiceId }); } catch (error) { return NextResponse.json({ error: "Error interno al iniciar el pago" }, { status: 500 }); } }
7. Tratamiento seguro de Webhooks y firma criptográfica ECDSA
El punto más sensible de cualquier integración financiera es la validación de notificaciones. Un atacante podría remitir una petición HTTP simulada a /api/payment/webhook anunciando una compra supuestamente abonada.
Para neutralizar este vector, Monobank firma cada notificación mediante el algoritmo asimétrico ECDSA (curva secp256r1 / SHA-256) a través de la cabecera x-sign.
7.1. Por qué JSON.stringify invalida la firma
Peligro crítico con rawBody: La verificación ECDSA exige el flujo exacto de bytes emitido por el servidor de Monobank. Si parsea el JSON y vuelve a serializarlo mediante JSON.stringify(req.body), el orden de las claves o los espacios en blanco cambian. Como consecuencia, el hash SHA-256 diferirá y la validación fallará invariablemente.
7.2. Implementación de verificación en Next.js y Express
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 }, // Caché de la clave pública durante 24h }); 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. Lectura del cuerpo original sin mutaciones en formato texto const rawBody = await req.text(); try { const pubKey = await getMonobankPubKey(process.env.MONOBANK_TOKEN!); // 2. Validación de la firma ECDSA SHA-256 const verifier = crypto.createVerify("SHA256"); verifier.update(rawBody); const isValid = verifier.verify(pubKey, Buffer.from(signature, "base64")); if (!isValid) { console.error("Webhook rechazado: firma x-sign no válida"); return new NextResponse("Invalid signature", { status: 400 }); } // 3. Parseo del JSON exclusivamente tras validar la firma criptográfica const payload = JSON.parse(rawBody); const { invoiceId, status, amount, reference } = payload; if (status === "success") { // Registrar la entrega del pedido con protección contra duplicados (idempotencia) console.log(`Pedido ${reference} (${invoiceId}) completado por ${amount / 100} UAH`); } return new NextResponse("OK", { status: 200 }); } catch (error) { console.error("Error al procesar el webhook:", error); return new NextResponse("Internal verification error", { status: 500 }); } }
8. Pruebas locales de Webhooks (Localhost y Cloudflare Tunnels)
Al ejecutar la aplicación en http://localhost:3000, Monobank no puede enviar notificaciones porque su entorno local carece de una IP pública enrutada.
Dado que el banco exige endpoints públicos con protocolo HTTPS, recurra a un túnel seguro durante el desarrollo.
8.1. Despliegue inmediato del túnel (gratuito y sin registro)
bash# Túnel HTTPS instantáneo al puerto 3000 sin instalar binarios npx untun@latest tunnel --port 3000
Obtendrá un dominio público efímero:
https://your-tunnel-name.trycloudflare.com
8.2. Configuración en desarrollo
En el endpoint de creación de orden, defina:
Los cobros de prueba impactarán directamente en su terminal local, facilitando la depuración de firmas en tiempo real.
9. UX de la página de retorno y resolución de condiciones de carrera
Cuando un cliente completa el abono en Apple Pay o en la app de Monobank, es devuelto a redirectUrl (/payment-result?ref=...) de forma inmediata.
No obstante, la notificación HTTP de Monobank a su servidor puede demorarse entre 1 y 2 segundos por latencia de red. Si la página de destino consulta inmediatamente la base de datos, podría mostrar erróneamente: «Pedido pendiente de pago», alarmando al usuario.
9.1. Patrón arquitectónico de mitigación
- Estado de espera inicial: La página abre con un mensaje neutral:
«Confirmando pago con el banco...»y un indicador visual de carga. - Sondeo corto (Short Polling): El frontend realiza hasta 5 consultas a intervalos de 1.5 segundos (
/api/orders/check-status?ref=...), aguardando a que el webhook marque el pedido comosuccess. - Mecanismo de escape: Si tras 8 segundos no hay confirmación definitiva, se informa: «Pago en proceso de liquidación. El acceso se activará en 1-2 minutos».
9.2. Componente React para la página de confirmación
10. Prestaciones avanzadas: Facturación integrada, Retención y Wallet
Monobank Acquiring cubre escenarios comerciales avanzados:
Página de pago alojada de Monobank con Apple Pay y tarjetas10.1. Facturación electrónica automática (pRRO Checkbox)
La fiscalización de cobros es obligatoria para la mayoría de profesionales que operan en Ucrania.
Monobank incluye una integración nativa y sin coste con Checkbox:
- Activación en un clic: En el panel de control del terminal active «Фіскалізація через Checkbox». El banco emite y suscribe las facturas oficiales automáticamente con su certificado digital.
- Sin código adicional para productos estándar: Las facturas se generan partiendo del campo
destination. - Desglose de cesta mediante API: Si comercializa productos con tipos impositivos diferenciados, adjunte el array
basketOrderenmerchantPaymInfo:
10.2. Autorización previa en dos fases (Hold)
Idóneo para productos físicos dependientes de control de inventario:
- Retención: Asigne
paymentType: "hold"al crear la orden. El dinero queda congelado en la tarjeta del comprador hasta 9 días. - Captura definitiva (Finalize): Llame al endpoint
/api/merchant/invoice/finalizepara liquidar el importe total o parcial. - Liberación: Si no hay stock, invoque
/api/merchant/invoice/cancelpara liberar los fondos sin comisiones.
10.3. Tokenización y suscripciones recurrentes (Wallet)
Para suscripciones SaaS periódicas, transmita saveCardData: true en el primer pago. Tras completarse, el webhook notificará un identificador walletId, que servirá para procesar cobros recurrentes desatendidos.
11. Matriz de seguridad y pruebas automatizadas (Vitest / Jest)
El procesamiento de pagos no tolera errores. Ejecute esta suite de validación antes del lanzamiento:
| Prueba | Vector evaluado | Comportamiento esperado |
|---|---|---|
| 1. Protección contra alteración de precio | El cliente envía productId: "vip", pero inyecta amount: 100 (1 UAH) | El servidor ignora el importe del cliente y aplica la tarifa oficial (2490 UAH = 249.000 céntimos). Devuelve HTTP 400 ante IDs inexistentes. |
| 2. Bloqueo de peticiones sin firma | Se recibe un POST en /api/payment/webhook sin la cabecera x-sign | Bloqueo fulminante con código HTTP 400 Bad Request. Sin alteraciones en BD. |
| 3. Rechazo de firmas fraudulentas | Un atacante envía una firma manipulada con status: "success" | crypto.verify(SHA256, ...) resulta falso. Retorno HTTP 400/401. Pedido no entregado. |
| 4. Idempotencia en webhooks duplicados | Monobank reintenta la entrega del webhook success por congestión de red | El producto o acceso se asigna una única vez. Los duplicados devuelven HTTP 200 OK sin duplicar entregas. |
| 5. Resistencia a condiciones de carrera | El webhook llega antes de que concluya el registro inicial en la base de datos | El handler emplea UPSERT o resuelve el registro sin producir errores 500. |
| 6. Prevención de saturación de peticiones | Fallback de consulta /api/merchant/invoice/status | Intervalo mínimo de sondeo de 15 segundos para evitar bloqueos HTTP 429 Too Many Requests. |
11.1. Suite de pruebas automatizadas (monobank-acquiring.test.ts)
12. Lista de verificación técnica final antes del lanzamiento
Compruebe su módulo de cobros contra estos 10 puntos antes de habilitar el tráfico de producción:
- Cumplimiento legal del sitio: El pie de página enlaza a Condiciones del Servicio, Privacidad, Devoluciones e identificación fiscal con NIF/CIF.
- Importes en céntimos: Valores de
amountmultiplicados por 100 ($1\text{ UAH} = 100\text{ céntimos}$) medianteMath.round. - Aislamiento de credenciales: Clave API almacenada en
.envcomoMONOBANK_TOKENe ignorada en.gitignore. - Precios desde backend (SSOT): El cliente solo envía el identificador del producto; los importes se calculan en el servidor.
- Lectura del cuerpo en bruto: El webhook valida texto o búferes binarios puros (
req.text()oreq.rawBody), sin serialización intermedia viaJSON.stringify. - Criptografía robusta: Firma verificada contra la clave pública del banco mediante ECDSA SHA-256.
- Dirección HTTPS pública: Endpoint de webhook accesible en un dominio con certificado SSL válido (comprobado vía Cloudflare Tunnel o ngrok).
- Almacenamiento idempotente: Las notificaciones duplicadas no duplican la entrega de productos o accesos.
- Mitigación de condición de carrera:
/payment-resultcuenta con estado de carga transitorio y sondeo corto. - Pago real de prueba de 1 a 5 UAH: Transacción completada con tarjeta bancaria real para ratificar la liquidación en cuenta.