O modelo de dados foi projetado para garantir integridade transacional, rastreabilidade de auditoria e flexibilidade para suportar os diferentes fluxos de transferência. As entidades são armazenadas em PostgreSQL.
Entidades principais
Transfer
A entidade Transfer é o registro central para qualquer tipo de transferência (TED OUT, TED IN, P2P). Armazena os detalhes do remetente e destinatário, valores e o estado atual da transação.
Campos principais:
| Campo | Tipo | Descrição |
|---|
transferId | UUID (PK) | Identificador único da transferência |
organizationId | UUID (FK) | Isolamento multi-tenant |
ledgerId | UUID | Do CRM (Midaz multi-tenant) |
senderAccountId | UUID | Conta Midaz do remetente |
recipientAccountId | UUID (nullable) | Apenas para P2P |
recipientDetails | JSONB | ISPB, agência, conta, titular |
amount | Decimal | Valor sem taxa |
feeAmount | Decimal | Taxa cobrada |
totalAmount | Decimal | amount + fee (calculado) |
type | Enum | TED_OUT, TED_IN, P2P |
status | Enum | Estado atual da transferência |
confirmationNumber | String | Número legível, único por organização |
controlNumber | String | JD SPB NumCtrlIF |
midazTransactionId | UUID | Referência de transação Midaz |
Máquinas de estado
CREATED → PENDING → PROCESSING → COMPLETED
→ REJECTED (4xx da JD)
→ FAILED (5xx/timeout)
→ CANCELLED (usuário cancela antes do processamento)
RECEIVED → PROCESSING → COMPLETED
→ REJECTED (destinatário não encontrado)
CREATED → PROCESSING → COMPLETED
→ FAILED (erro Midaz)
→ CANCELLED
PaymentInitiation
Utilizada no fluxo de duas etapas do TED OUT, armazena os detalhes da transferência para que o cliente revise as taxas antes de confirmar. Uma iniciação expira em 24 horas se não for processada.
Campos principais:
| Campo | Tipo | Descrição |
|---|
initiationId | UUID (PK) | Identificador único |
organizationId | UUID (FK) | Isolamento multi-tenant |
senderAccountId | UUID | Conta do remetente |
recipientDetails | JSONB | Dados do destinatário |
amount | Decimal | Valor da transferência |
feeAmount | Decimal | Calculado por plugin-fees |
totalAmount | Decimal | amount + fee |
status | Enum | PENDING_CONFIRMATION, PROCESSED, EXPIRED |
transferId | UUID (FK) | Preenchido quando PROCESSED |
expiresAt | Timestamp | 24h da criação |
Ciclo de vida:
Criado → Usuário revisa taxa → ProcessTransfer → PROCESSED
→ EXPIRED (após 24h)
TransferStatusHistory
Log de auditoria imutável que registra cada transição de estado da entidade Transfer. Fundamental para rastreabilidade e conformidade com a exigência do BACEN de reter dados por 5 anos.
Campos principais:
| Campo | Tipo | Descrição |
|---|
historyId | UUID (PK) | Identificador único |
transferId | UUID (FK) | Referência para Transfer |
oldStatus | Enum (nullable) | Estado anterior |
newStatus | Enum | Novo estado |
reason | String (nullable) | Motivo (para casos de erro) |
changedAt | Timestamp | Data/hora da mudança |
changedBy | Enum | system, user, admin |
Retenção: 5 anos conforme requisito regulatório do BACEN (Resolução 4.753/2019).
JDIncomingMessage
Persiste as mensagens brutas recebidas do sistema da JD Consultores antes de qualquer processamento. Garante que nenhuma transferência de entrada (TED IN) seja perdida em caso de falha, implementando garantia de entrega at-most-once.
Campos principais:
| Campo | Tipo | Descrição |
|---|
organizationId | UUID (FK) | Isolamento multi-tenant |
sequenceNumber | String (unique) | JD NumCabSeq |
controlNumber | String (nullable) | NumCtrlIF |
messageCode | Enum | STR0008R2, STR0010R2 |
returnType | Enum | P=Processed, E=Error, R=Receipt, C=Cancelled |
rawXml | Text | Mensagem JD completa (auditoria) |
processed | Boolean | false inicialmente |
transferId | UUID (FK) | Preenchido após processamento |
Garantia crítica: A mensagem é persistida ANTES do processamento, garantindo que nenhuma transferência seja perdida em caso de falha.
OrganizationConfig
Armazena as configurações específicas de cada cliente (tenant), como credenciais da JD (criptografadas), URL de webhook e configurações de taxas.
Campos principais:
| Campo | Tipo | Descrição |
|---|
organizationId | UUID (PK) | Mesmo que JWT tenantId |
jdCredentials | JSONB (encrypted) | legacyCode, userCode, password, privateKey |
jdIspb | String | 8 dígitos (ISPB da instituição) |
webhookUrl | String | URL HTTPS para notificações |
webhookSecret | String | Segredo HMAC para validação |
feeEnabled | Boolean | Habilita cobrança de tarifa |
duplicateWindowSec | Integer | Janela de deduplicação (10-300, padrão 60) |
tenantMode | Enum | DATABASE, SCHEMA, SINGLE |
Relacionamento entre entidades
OrganizationConfig (1) ──< has many >── (*) Transfer
├─< has many >─ (*) TransferStatusHistory
└─< originates from >─ (0..1) PaymentInitiation
JDIncomingMessage (1) ──< creates >── (0..1) Transfer (do tipo TED_IN)
Propriedade de dados: Cada entidade é de propriedade de UM componente, seguindo os princípios da Arquitetura Hexagonal.
Índices principais
| Tabela | Índice | Propósito |
|---|
transfers | (organization_id, created_at) | Listagem paginada por data |
transfers | (organization_id, status) | Filtro por status |
transfers | (control_number) | Lookup por número JD |
jd_incoming_message | (sequence_number) | Deduplicação at-most-once |
transfer_status_history | (transfer_id, changed_at) | Auditoria cronológica |
Particionamento
Tabelas de auditoria são particionadas por mês para melhor performance em consultas históricas e facilitar a gestão de retenção de dados.
Campos para reconciliação
| Campo | Uso |
|---|
controlNumber | Número de controle do JD SPB (único por transferência) |
transferId | Identificador interno da Lerian |
confirmationNumber | Número legível para o usuário final |
createdAt | Timestamp de criação |
completedAt | Timestamp de conclusão |
O histórico de transferências é mantido por 5 anos, conforme requisitos regulatórios do BACEN.