Saltar al contenido
Payment Emulator LabDocumentaciónEntrar a la consola

Arquitectura

Dominio de negocio

El emulador se coloca entre la aplicación que se está probando y el proveedor depagos que esa aplicación espera. Reproduce contratos concretos de proveedor y ala vez mantiene el co

6 min de lectura

Índice / Index · English

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
mermaid
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
mermaid
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, failed y disputed son 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
Payment Emulator Lab · RonuSoftwareMIT