El modelo de datos fue diseñado para garantizar integridad transaccional, rastreabilidad de auditoría y flexibilidad para soportar los diferentes flujos de transferencia. Las entidades se almacenan en PostgreSQL.
Entidades principales
Transfer
La entidad Transfer es el registro central para cualquier tipo de transferencia (TED OUT, TED IN, P2P). Almacena los detalles del remitente y destinatario, valores y el estado actual de la transacción.
Campos principales:
| Campo | Tipo | Descripción |
|---|
transferId | UUID (PK) | Identificador único de la transferencia |
organizationId | UUID (FK) | Aislamiento multi-tenant |
ledgerId | UUID | Del CRM (Midaz multi-tenant) |
senderAccountId | UUID | Cuenta Midaz del remitente |
recipientAccountId | UUID (nullable) | Solo para P2P |
recipientDetails | JSONB | ISPB, sucursal, cuenta, titular |
amount | Decimal | Valor sin tarifa |
feeAmount | Decimal | Tarifa cobrada |
totalAmount | Decimal | amount + fee (calculado) |
type | Enum | TED_OUT, TED_IN, P2P |
status | Enum | Estado actual de la transferencia |
confirmationNumber | String | Número legible, único por organización |
controlNumber | String | JD SPB NumCtrlIF |
midazTransactionId | UUID | Referencia de transacción Midaz |
Máquinas de estado
CREATED → PENDING → PROCESSING → COMPLETED
→ REJECTED (4xx de JD)
→ FAILED (5xx/timeout)
→ CANCELLED (usuario cancela antes del procesamiento)
RECEIVED → PROCESSING → COMPLETED
→ REJECTED (destinatario no encontrado)
CREATED → PROCESSING → COMPLETED
→ FAILED (error Midaz)
→ CANCELLED
PaymentInitiation
Utilizada en el flujo de dos etapas del TED OUT, almacena los detalles de la transferencia para que el cliente revise las tarifas antes de confirmar. Una iniciación expira en 24 horas si no es procesada.
Campos principales:
| Campo | Tipo | Descripción |
|---|
initiationId | UUID (PK) | Identificador único |
organizationId | UUID (FK) | Aislamiento multi-tenant |
senderAccountId | UUID | Cuenta del remitente |
recipientDetails | JSONB | Datos del destinatario |
amount | Decimal | Valor de la transferencia |
feeAmount | Decimal | Calculado por plugin-fees |
totalAmount | Decimal | amount + fee |
status | Enum | PENDING_CONFIRMATION, PROCESSED, EXPIRED |
transferId | UUID (FK) | Completado cuando PROCESSED |
expiresAt | Timestamp | 24h desde la creación |
Ciclo de vida:
Creado → Usuario revisa tarifa → ProcessTransfer → PROCESSED
→ EXPIRED (después de 24h)
TransferStatusHistory
Log de auditoría inmutable que registra cada transición de estado de la entidad Transfer. Fundamental para rastreabilidad y cumplimiento con la exigencia del BACEN de retener datos por 5 años.
Campos principales:
| Campo | Tipo | Descripción |
|---|
historyId | UUID (PK) | Identificador único |
transferId | UUID (FK) | Referencia a Transfer |
oldStatus | Enum (nullable) | Estado anterior |
newStatus | Enum | Nuevo estado |
reason | String (nullable) | Motivo (para casos de error) |
changedAt | Timestamp | Fecha/hora del cambio |
changedBy | Enum | system, user, admin |
Retención: 5 años conforme requisito regulatorio del BACEN (Resolución 4.753/2019).
JDIncomingMessage
Persiste los mensajes brutos recibidos del sistema de JD Consultores antes de cualquier procesamiento. Garantiza que ninguna transferencia de entrada (TED IN) sea perdida en caso de falla, implementando garantía de entrega at-least-once. La deduplicación se maneja mediante la restricción única de sequenceNumber.
Campos principales:
| Campo | Tipo | Descripción |
|---|
organizationId | UUID (FK) | Aislamiento multi-tenant |
sequenceNumber | String (unique) | JD NumCabSeq (usado para deduplicación) |
controlNumber | String (nullable) | NumCtrlIF |
messageCode | Enum | STR0008R2, STR0010R2 |
returnType | Enum | P=Processed, E=Error, R=Receipt, C=Cancelled |
rawXml | Text | Mensaje JD completo (auditoría) |
processed | Boolean | false inicialmente |
transferId | UUID (FK) | Completado después del procesamiento |
Garantía crítica: El mensaje es persistido ANTES del procesamiento, garantizando que ninguna transferencia sea perdida en caso de falla. Los mensajes duplicados se manejan mediante la restricción única de sequenceNumber.
OrganizationConfig
Almacena las configuraciones específicas de cada cliente (tenant), como credenciales de JD (encriptadas), URL de webhook y configuraciones de tarifas.
Campos principales:
| Campo | Tipo | Descripción |
|---|
organizationId | UUID (PK) | Mismo que JWT tenantId |
jdCredentials | JSONB (encrypted) | legacyCode, userCode, password, privateKey |
jdIspb | String | 8 dígitos (ISPB de la institución) |
webhookUrl | String | URL HTTPS para notificaciones |
webhookSecret | String | Secreto HMAC para validación |
feeEnabled | Boolean | Habilita cobro de tarifa |
duplicateWindowSec | Integer | Ventana de deduplicación (10-300, default 60) |
tenantMode | Enum | DATABASE, SCHEMA, SINGLE |
Relación entre entidades
OrganizationConfig (1) ──< has many >── (*) Transfer
├─< has many >─ (*) TransferStatusHistory
└─< originates from >─ (0..1) PaymentInitiation
JDIncomingMessage (1) ──< creates >── (0..1) Transfer (de tipo TED_IN)
Propiedad de datos: Cada entidad es propiedad de UN componente, siguiendo los principios de la Arquitectura Hexagonal.
Índices y rendimiento
Índices principales
| Tabla | Índice | Propósito |
|---|
transfers | (organization_id, created_at) | Listado paginado por fecha |
transfers | (organization_id, status) | Filtro por estado |
transfers | (control_number) | Búsqueda por número JD |
jd_incoming_message | (sequence_number) | Deduplicación at-least-once |
transfer_status_history | (transfer_id, changed_at) | Auditoría cronológica |
Particionamiento
Las tablas de auditoría están particionadas por mes para mejor rendimiento en consultas históricas y facilitar la gestión de retención de datos.
Campos para reconciliación
| Campo | Uso |
|---|
controlNumber | Número de control del JD SPB (único por transferencia) |
transferId | Identificador interno de Lerian |
confirmationNumber | Número legible para el usuario final |
createdAt | Timestamp de creación |
completedAt | Timestamp de conclusión |
El historial de transferencias se mantiene por 5 años, conforme requisitos regulatorios del BACEN.