Files
Investidor-V2/docs/investmoz/suitecrm/outbox.md
T
nala 250ac35fd1
tests / ci (push) Canceled after 0s
first commit
2026-09-28 10:01:09 +02:00

4.7 KiB

Recepção idempotente e outbox de manifestações

Esta entrega captura eventos de domínio; não envia dados ao SuiteCRM. Não depende de SUITECRM_ENABLED: a captura transaccional preserva eventos enquanto a integração externa está desactivada. Não executar uma futura carga histórica sem reconciliar estes eventos pendentes.

Contrato implementado

POST /api/v1/interest-requests mantém o payload actual e aceita Idempotency-Key com UUID aleatório. A chave identifica uma intenção de submissão, não a pessoa. Não derivar a chave de email ou NUIT.

  • Primeira submissão válida: 201 com referência e estado de recepção; pedido, recibo e evento confirmados na mesma transacção.
  • Mesma chave e payload validado equivalente: mesma confirmação 201; sem novo pedido, notificação ou evento. O estado retornado é o da recepção, não uma consulta do estado comercial actual.
  • Mesma chave com dados diferentes: 409, sem alterações.
  • Chave inválida/vazia ou campos inválidos: 422. Falha técnica: 500 sanitizado. Os limites de pedidos existentes mantêm-se, incluindo nas repetições.
  • Sem chave: compatibilidade com clientes antigos; cada envio é uma nova manifestação, sem garantia de deduplicação de reenvio.

Após retirar uma oportunidade, um pedido previamente aceite pode ser confirmado novamente com a sua chave e payload; novas submissões continuam rejeitadas. As restantes validações, incluindo email/consentimento, são aplicadas antes de devolver a confirmação.

O formulário React gera um UUID e reutiliza-o após falha enquanto os dados e a instância do formulário se mantiverem. Alterar dados cria outra intenção/chave. Não persiste payload pessoal em localStorage/sessionStorage. Recarregar, abandonar a página ou abrir outra aba perde esta chave; persistência segura entre sessões ainda não está implementada.

Persistência e atomicidade

Migration 2026_09_25_120000_add_interest_submission_receipts acrescenta UUID de integração opcional ao pedido e tabela de recibos com hash da chave, HMAC do payload e FK do pedido. Pedidos históricos não são automaticamente migrados. A chave primária única e advisory lock transaccional PostgreSQL serializam a mesma chave entre processos.

O HMAC usa APP_KEY. Preservar a chave durante esta fase; uma rotação precisa de estratégia de versões/revalidação dos recibos antes de ser aplicada. Recibos não expiram automaticamente, para não reabrir a possibilidade de duplicar pedidos; a política de retenção deve ser definida juntamente com pedidos e auditoria.

InterestSubmission envolve a criação, a notificação em database queue, o UUID, a outbox e o recibo numa transacção. Qualquer falha reverte o conjunto. Não há HTTP externo dentro da transacção.

O evento ExpressionOfInterestSubmitted usa source_system=portal-intake, agregado interest_request, UUID de integração, estado pending e classificação confidential. O payload contém apenas schema_version=1 e interest_id (UUID).

Email, telefone, mensagem, IP, token e consentimento integral não são copiados para o evento. O futuro consumidor terá de consultar o pedido e revalidar finalidade, consentimento e permissão de tratamento antes de montar um payload mínimo para o CRM. Referência IR e UUID não conferem autorização de acesso.

Alterações e verificação

  • Criados: migration, InterestSubmission, IdempotencyConflict e InterestSubmissionTest.
  • Alterados: controller e validação de pedidos, formulário React e documentação de progresso.
  • Dependências: PostgreSQL existente, outbox e triggers existentes, APP_KEY preservada e database queue para notificações.
  • Testes cobrem repetição, conflito, compatibilidade, evento sem payload pessoal, falha na outbox com rollback de pedido/notificação, oportunidade retirada e protecção de rollback do esquema.
  • Navegador Edge: resposta perdida após gravação seguida de reenvio com a mesma chave e confirmação original.
  • A serialização entre processos foi implementada com locks PostgreSQL; ainda não foi executado teste de carga concorrente distribuída.

Rollback e pendências

Antes de receber pedidos, é possível reverter esta migration numa base descartável e reaplicá-la. Depois de existirem identidades/recibos, down() recusa a remoção para não perder os vínculos com a outbox. Preferir reverter o código conservando o esquema; reconciliar eventos e dados antes de qualquer remoção aprovada.

Pendentes: dispatcher com leases, workers SuiteCRM, identidade externa única no CRM, recuperação de escritas ambíguas, dead-letter queue, reprocessamento autorizado, monitoria e reconciliação. Nenhum evento está marcado como publicado nesta entrega.