Traducción de
README.md, que es el original. Si discrepan, manda el inglés. La política está en docs/en/translation.md.
Payment Emulator Lab es un emulador de pasarelas de pago con estado y varios proveedores, para desarrollar integraciones. Una aplicación en pruebas puede usar APIs HTTP con la forma de cada proveedor, páginas de checkout alojadas y webhooks firmados, sin credenciales reales, sin tarjetas reales, sin dinero real y sin el sandbox del proveedor.
El proyecto se rige por contratos a propósito. Las rutas, los campos, los estados, los errores y las firmas de cada proveedor viven en el borde; el estado del pago, la idempotencia, la contabilidad, las trazas y la entrega de webhooks se implementan una sola vez en un núcleo neutral. Una forma cómoda que un proveedor real no usa es un defecto aquí, aunque haga el emulador más fácil de escribir.
#Qué trae
- Mercado Pago Orders: crear, consultar, añadir una transacción, procesar, capturar, cancelar y devolver total o parcialmente.
- Stripe PaymentIntents y Refunds, con la separación create/confirm, la captura manual, las peticiones con formulario que manda el SDK y errores con la forma de Stripe.
- PayPal Orders v2 y Payments v2: OAuth local, aprobación, autorización, captura, anulación y devoluciones para una unidad de compra y una captura final.
- Escenarios deterministas para éxito, rechazo, latencia, fallos HTTP, webhooks duplicados o con firma inválida, y pagos que se mueven en un plazo.
- Aislamiento por credencial de aplicación, más aplicaciones que caducan para integración continua.
- Un libro mayor por partida doble para captura, comisiones, reserva,
liquidación, retiro, devolución y contracargo. El dinero se guarda como
biginten unidades mínimas y los saldos se derivan de asientos inmutables. - Un outbox transaccional de webhooks con transporte de entrega intercambiable —PostgreSQL, MongoDB o Redis/BullMQ tras un mismo puerto— y firmas propias de cada proveedor.
- Un catálogo de bancos: cada uno es dueño del prefijo con el que empiezan sus números de cuenta, que se comprueba una vez, cuando el número todavía existe.
- Una consola de operador en Nuxt y un checkout de comprador aparte, también en Nuxt. Cada navegador habla con su propio servidor Nitro; ninguno recibe credenciales del backend.
- Personas con rol
admin,merchantocustomer, alcance por fila sobre las aplicaciones, sesiones revocables y acceso opcional del cliente en el checkout.
Los límites exactos de fidelidad están en los informes de fidelidad. En particular, no se emulan los esquemas OpenAPI completos de los proveedores, Stripe Elements ni la superficie entera de producto de Mercado Pago o Stripe.
#La arquitectura de un vistazo
Código del diagrama
flowchart LR
AUT[Aplicación en pruebas] -->|API del proveedor + token de aplicación| API[API NestJS]
Operador[Navegador del operador] --> Console[Consola Nuxt / Nitro]
Comprador[Navegador del comprador] --> Checkout[Checkout Nuxt / Nitro]
Console -->|sesión de persona| API
Checkout -->|enlace público; sesión de cliente opcional| API
API --> Gateway[Orquestación de pasarela]
Gateway --> Plugin[Plugin del proveedor]
Gateway --> Payments[Pagos canónicos]
Payments --> Ledger[Libro mayor por partida doble]
Payments --> Outbox[Outbox de webhooks]
API --> PG[(PostgreSQL)]
Worker[Worker de webhooks y relojes] --> PG
Worker --> Transport[(Transporte: PostgreSQL, MongoDB o Redis)]
Transport --> Callback[Callback en la lista de permitidos]
Es una arquitectura modular que delega en un runtime, no DDD. Los
controladores declaran superficies HTTP, los servicios son dueños del
comportamiento, el EntityManager de MikroORM se usa directamente, y lo
compartido vive en apps/api/src/core/ o en el módulo que lo posee. Las
fronteras y los flujos están en Arquitectura.
#Tecnología
| Tecnología | Papel |
|---|---|
| Node.js 22+ / TypeScript | Runtime y lenguaje |
| NestJS 11 | API HTTP y cableado de la aplicación |
| MikroORM 7 / PostgreSQL 17 | Estado durable, transacciones y migraciones |
| PostgreSQL, MongoDB 7 o Redis 8 / BullMQ 6 | Transporte de entrega de webhooks, elegido con WEBHOOK_QUEUE_DRIVER |
| Nuxt 4 / Vue 3 | Consola de operador y checkout de comprador |
| PrimeVue 4 | Sólo la consola |
| Vitest 4 / Supertest / fast-check | Pruebas unitarias, de integración, de conformidad y de propiedades |
| Docker Compose | Pila local de seis servicios |
Las versiones están confirmadas contra los manifiestos y el lockfile actuales.
#Arranque rápido
Requisitos: Docker con Compose. Node.js >=22.17.0 hace falta además para la
semilla, las pruebas de humo y el desarrollo sin Docker.
docker compose up --build --wait
npm run seed
La semilla crea las personas de la demo y datos representativos de Mercado Pago y Stripe a través de las superficies HTTP reales. Con los valores por defecto de Compose, entra a la consola con:
correo: operadora@example.test
contraseña: emulator-demo-password
Son credenciales locales de demostración y nada más. Si la pila es accesible por
alguien más, copia .env.example a .env y cambia todos los secretos antes de
arrancarla. Si cambias ADMIN_TOKEN, pásale el mismo valor a npm run seed.
| URL | Superficie |
|---|---|
| http://localhost:3000 | Consola del operador |
| http://localhost:3001 | Checkout del comprador |
| http://localhost:8080/health | Salud de la API |
Para comprobar la pila entera en marcha:
npm run smoke:stack
Para instalar sin Docker, las migraciones y los cuatro procesos de desarrollo, sigue Primeros pasos.
#Mapa del repositorio
apps/
├── api/ # API NestJS y worker de webhooks y relojes
├── console/ # Aplicación Nuxt del operador (PrimeVue)
└── checkout/ # Aplicación Nuxt del comprador y los kits visuales
contracts/ # Especificaciones importadas de proveedores y sus sumas
scenarios/ # Fuentes canónicas de los escenarios, en JSON
scripts/ # Prueba de humo de aceptación sobre la pila entera
docs/ # Documentación de mantenimiento y de entrada
.agents/ # Reglas de arquitectura vinculantes; leer antes de tocar código
graphify-out/ # Grafo de descubrimiento generado (instantánea histórica)
La instantánea de Graphify se generó antes de la disposición actual en apps/.
Sigue sirviendo para encontrar los centros originales (pasarela, libro mayor,
escenarios y webhooks), pero sus rutas y relaciones hay que confirmarlas contra
el código. Dónde va el código nuevo está en
Estructura del proyecto.
#Órdenes habituales
Todas desde la raíz del repositorio.
npm ci
npm run typecheck
npm run lint
npm test
npm run build
npm run smoke
Las pruebas de integración corren contra PostgreSQL sólo si DATABASE_URL está
puesta; sin ella, Vitest las da por saltadas. La humareda de pila entera exige
una pila de Compose en marcha.
Órdenes útiles de desarrollo:
npm run dev:api
npm run dev:console
npm run dev:checkout
npm run start:worker --workspace @payment-emulator/api
npm run migrate
npm run admin:create
npm run scenario:compile
#Tu primer cambio
Antes de tocar código, lee .agents/README.md — que está en
inglés a propósito: es el contrato, y un contrato traducido son dos contratos. La
versión corta:
- Encuentra la aplicación y el módulo dueños.
- Deja finos los controladores y las páginas; el comportamiento va en los servicios o en el gateway de la aplicación.
- Lo específico de un proveedor va sólo en
apps/api/src/providers/<id>/, y su identidad visual sólo enapps/checkout/providers/<id>/. - Nunca metas una rama por proveedor en el núcleo, ni escribas filas del libro mayor a mano, ni guardes un saldo mutable, ni dejes que un navegador llame directamente a la API de NestJS.
- Añade o ajusta la prueba más estrecha que valga, y luego pasa typecheck, lint, pruebas y build.
Las rutas y los ejemplos están en la Guía de desarrollo.
#Documentación
Empieza por el índice de documentación:
- Primeros pasos
- Arquitectura
- Dominio de negocio
- Flujos de petición críticos
- Pruebas
- Configuración
- Diagnóstico
- Añadir un proveedor
#Seguridad
- El código en ejecución nunca llama a un proveedor de pagos real.
- Los webhooks se mandan sólo si la salida está habilitada y el host de destino está en la lista de permitidos.
- Los tokens de aplicación y de sesión se guardan como digest; el token de acceso de una aplicación se enseña únicamente al emitirlo o al rotarlo.
- Las trazas tachan los campos de credencial conocidos antes de persistirlos.
- No se guardan números de cuenta completos, ni PAN, ni CVV.
- Los escenarios son datos; no se admite JavaScript arbitrario.
Este repositorio es una herramienta para desarrollar integraciones, no un procesador de pagos ni un libro mayor de tesorería para producción.