Traducción de
docs/en/architecture/business-domain.md, que es el original. Si discrepan, manda el inglés.
#Modelo de producto
El emulador se coloca entre la aplicación que se está probando y el proveedor de pagos que esa aplicación espera. Reproduce contratos concretos de proveedor y a la vez mantiene el comportamiento económico en un modelo común, para que un mismo entorno de pruebas pueda comparar proveedores sin aplanar sus APIs públicas.
#Actores
| Actor | Qué hace | Credencial |
|---|---|---|
| Aplicación en pruebas | Llama a rutas con forma de Mercado Pago o de Stripe | Token de acceso de la aplicación |
| Administradora | Gestiona personas, pasarelas y todos los inquilinos | Sesión de persona |
| Comercio | Es dueño de sus aplicaciones y ve sus pagos y su dinero | Sesión de persona |
| Cliente | Tiene cartera y puede pagar con su saldo | Sesión de persona, opcional en el checkout |
| Comprador anónimo | Completa un checkout alojado con un método externo | La capacidad del enlace de pago |
| Operador raíz | Crea la primera administradora y mueve la CI y la semilla | ADMIN_TOKEN; no es una persona |
| Worker | Entrega webhooks y avanza el estado programado | Acceso a la base y al transporte, sin identidad de persona |
#Conceptos centrales
#Aplicación
Una ApplicationEntity es el inquilino y representa un sandbox de integración.
Elige proveedor y escenario por defecto, es dueña de credenciales con la forma
del proveedor, y puede tener comercio dueño, destino de webhook, perfil bancario
y caducidad. La credencial se guarda sólo como digest y se devuelve en claro
únicamente al crearla o al rotarla.
#Plugin de proveedor
Un plugin traduce el vocabulario del proveedor al modelo canónico. Mercado Pago, Stripe y PayPal tienen flujos de pago enrutados. Sus informes de fidelidad delimitan las operaciones y diferencias pendientes; implementado no significa que se reproduzca todo el catálogo de productos del proveedor.
#Intento de pago y recurso de proveedor
PaymentIntentEntity es el hecho económico neutral. ProviderResourceEntity es
ese mismo pago tal y como lo expone el proveedor. La separación permite que
Stripe devuelva un PaymentIntent y Mercado Pago una Order sin meter ninguno de
los dos vocabularios en la tabla canónica.
Código del diagrama
erDiagram
USER ||--o{ APPLICATION : posee
USER ||--o{ USER_SESSION : entra_con
USER ||--o{ BANK_ACCOUNT : posee
BANK ||--o{ BANK_ACCOUNT : emite_numeros_para
APPLICATION ||--o{ PAYMENT_INTENT : contiene
PAYMENT_INTENT ||--|{ PROVIDER_RESOURCE : se_proyecta_como
APPLICATION ||--o{ LEDGER_ACCOUNT : provisiona
APPLICATION ||--o{ LEDGER_ENTRY : registra
LEDGER_ENTRY ||--|{ LEDGER_POSTING : contiene
LEDGER_ACCOUNT ||--o{ LEDGER_POSTING : recibe
APPLICATION ||--o{ WEBHOOK_EVENT : debe
WEBHOOK_EVENT ||--o{ WEBHOOK_ATTEMPT : registra
APPLICATION ||--o{ TRACE : captura
TRACE ||--o{ TRACE_EVENT : contiene
#Escenario
Un escenario es dato de prueba determinista. Su cláusula when empareja
proveedor, operación canónica, rango de importe y/o metadatos exactos. Su
cláusula then puede elegir un desenlace canónico, fijar el estado de cable del
proveedor, inyectar latencia o fallo, programar webhooks o disponer una
transición por reloj. Los propios viven como JSON bajo scenarios/; los
personalizados, en PostgreSQL.
#Libro mayor
Una cuenta pertenece a una aplicación y a una moneda. Un asiento inmutable contiene dos o más apuntes cuyos debes igualan a sus haberes. Un saldo se calcula a partir de los apuntes según el lado normal de la cuenta; nunca se guarda como total mutable. Ver Banco falso y contabilidad.
#Banco y cuenta bancaria externa
Una BankEntity es una institución: un código que alguien escribe, un prefijo
único con el que empiezan sus números de cuenta, y cuántos dígitos tienen esos
números. El prefijo es identidad y no se edita nunca — ver
Banco falso y contabilidad.
Una BankAccountEntity pertenece a una persona, está en uno de esos bancos, y
guarda sólo los cuatro últimos caracteres del número que se le dio. Es una
contraparte a la que apuntan los asientos de fondeo o de retiro, no otra cuenta
dentro del plan contable de la pasarela. Cerrarla deja intactas las referencias
históricas del libro mayor.
#Outbox de webhooks
Una WebhookEventEntity significa que se debe una notificación. Se confirma en
la misma transacción que el estado de pago que la causó. WebhookAttemptEntity
anota los intentos de entrega del worker. Los trabajos de la cola son transporte
sustituible, sea cual sea el almacén que los guarde; la fila del evento es la
verdad durable.
#Ciclo de vida de un pago
Código del diagrama
stateDiagram-v2
[*] --> created
created --> approved
created --> requires_action
created --> authorized
created --> captured
created --> canceled
created --> failed
requires_action --> approved
approved --> authorized
approved --> captured
approved --> canceled
approved --> failed
requires_action --> authorized
requires_action --> captured
requires_action --> canceled
requires_action --> failed
authorized --> captured
authorized --> canceled
authorized --> failed
captured --> settled
captured --> refunded
captured --> disputed
settled --> refunded
settled --> disputed
refunded --> disputed
created: el recurso existe pero no ha habido intento de pago.requires_action: el pago espera al comprador o a un suceso externo.approved: consentimiento del comprador, sin débito ni retención.authorized: los fondos están comprometidos pero no capturados; puede haber una retención en el libro mayor.captured: el dinero entró en la pasarela y el neto del comercio está en reserva.settled: la reserva se liberó hacia el pagadero del comercio.canceled,failedydisputedson terminales. Un pago devuelto todavía puede pasar a disputado en la máquina de estados actual.
Los mapeos de proveedor son totales pero no uno a uno. Stripe mapea captured y
settled los dos a succeeded; Mercado Pago los mapea a
processed/accredited. El estado canónico responde preguntas económicas que el
estado de cable puede no responder.
#Origen del fondeo
Cada pago anota de dónde vino su dinero:
balance: la pasarela debita la cartera del cliente que entró.external: tarjeta, transferencia o ticket meten dinero en el efectivo del sistema.
Se persiste porque una devolución posterior tiene que devolver el dinero por el mismo lado, aunque el catálogo de métodos del proveedor haya cambiado.
#Invariantes del dominio
- Una credencial de aplicación resuelve exactamente un inquilino y un proveedor.
- Las palabras del proveedor no entran en el intento canónico.
- Un plugin no puede escribir estado, contabilidad ni trabajos de cola.
- Todo asiento cuadra en una moneda y todo apunte es positivo.
- La misma clave de idempotencia con el mismo cuerpo se repite; con otro cuerpo, conflicto.
- Un intento de captura manual no puede pasar a capturado hasta que la integración capture.
- El evento de webhook se escribe dentro de la transacción del pago, antes de intentar entregarlo.
- Una sesión de cliente sólo puede gastar la cartera de ese cliente.
- Un comercio sólo ve las aplicaciones que posee; los identificadores invisibles
devuelven
404.
#Glosario
| Término | Qué significa en este repositorio |
|---|---|
| Canónico | Estado u operación neutral que usa el núcleo |
| Cable (wire) | El vocabulario exacto de petición y respuesta de cara al proveedor |
| Plano de control | Las APIs de administración: personas, aplicaciones, pasarelas, vistas del libro mayor |
| Superficie de proveedor | Rutas con la forma de Mercado Pago, Stripe o PayPal, autenticadas con el token de la aplicación |
| Plugin | Objeto plano de proveedor que declara y traduce un contrato, pero nunca persiste |
| Kit | Componente y tema de checkout propios de un proveedor |
| Unidades mínimas | Enteros en la unidad más pequeña; 1000 USD son 10,00 USD |
| Apunte | Una línea de debe o de haber dentro de un asiento |
| Retención (hold) | Fondos comprometidos por una autorización y todavía sin capturar |
| Reserva | Dinero del comercio ya capturado y todavía sin liberar |
| Pagadero | Dinero del comercio liberado y disponible para retirar |
| Outbox | Filas durables que representan notificaciones que se deben |
| BFF | Backend for frontend; cada servidor Nitro media para su propio navegador |
| Fidelidad | Cuánto se parece una superficie emulada a lo documentado y a lo que hace el SDK |