# Dossiê de paridade profundo: Antifraude pré-transação (PIX-out e devolução)

Auditoria READ-ONLY, zero inferência. Cada afirmação tem prova arquivo:linha ou tabela.coluna.
Legado decompilado (.NET) + DB SQL Server vivo (`crk_spi`) + Angular vs cabine PIX Monetarie
(Elixir `pix/backend/apps`) + Vue `pix/frontend/admin/src`.

VEREDITO GERAL: LACUNA DE FEATURE (controle de segurança ausente). O legado tem um motor
antifraude EXTERNO chamado ANTES de assinar/enviar todo PIX-out e toda devolução iniciados pela
tela (canal `front-spi`), com fail-closed/fail-open configurável, resiliência (timeout, circuit
breaker, fallback), histórico auditável por EndToEndId e relatórios/métricas. Na nossa cabine NÃO
existe nenhum hook antifraude pré-envio em nenhum caminho de dinheiro (PIX-out automático do Core,
PIX-out manual, ou devolução). Grep por `antifraud|validaAntiFraude|motor.*fraud|fraud.*engine|
risk_engine|scoring` no money-path da cabine = 0 hooks reais. O único "FRAUD_ENGINE" é um rótulo
de referência não usado.

---

## PARTE 1 - LEGADO: comportamento completo com prova

### 1.1 Onde entra (fronteira HTTP) e quem chama

Endpoints dedicados no `EntradaController` (separados do `addPagto`/`devPagto`):

- `POST /api/Entrada/validaAntiFraudePagamento` (PIX-out) - `SPI.Web.Api.decompiled.cs:934-946`.
  Captura `ipReq` (X-Forwarded-For, primeiro IP - `:939-942`), `User-Agent` (`:943`), `IdUser`
  do claim JWT (`:944`), e chama `ValidaPagamentoMotorAntifraude` (`:945`).
- `POST /api/Entrada/validaAntiFraudeDevolucao` (devolução) - `SPI.Web.Api.decompiled.cs:953-964`.
  Mesmas capturas, chama `ValidaDevolucaoMotorAntifraude` (`:964`).

O gate é enforçado pelo FRONTEND Angular (canal `front-spi`), que chama o motor ANTES de chamar
o `addPagto`/`devPagto`:

- Serviço Angular `validaMotorAntiFraude(payload, ehPacs008)` posta para
  `.../api/Entrada/validaAntiFraudePagamento` (ehPacs008=true) ou `.../validaAntiFraudeDevolucao`
  (ehPacs008=false) - `LegadoPIX/Pix/SPI/angular/main.js` @4116213 (bloco
  `const url = ehPacs008 ? ... validaAntiFraudePagamento : ... validaAntiFraudeDevolucao`), com
  `addLogAtividade(3106, ...)`.
- Gate de PAGAMENTO - `main.js` @3832560:
  ```
  var ret = yield _this2.lancamentosService.validaMotorAntiFraude(payload, true);
  if (ret.motorHabilitado) {
    if (ret.status?.trim().localeCompare("Aprovado",...) !== 0) {
      if (ret.codigo !== 408 && ret.codigo !== 503) { showConfirmation(...); loading=false; return; }   // BLOQUEIA (hard reject)
      if ((ret.codigo === 408 || ret.codigo === 503) && !ret.permiteFallback) { showConfirmation(...); return; }  // BLOQUEIA no timeout/circuito se fallback nao permitido
    }
    payload.endToEndId = ret.endToEndId;                 // adota o E2E gerado pelo motor
    payload.dtHrOperacao = ...formatDate(now);
  }
  yield _this2.lancamentosService.addPagto(payload)...   // SO ENTAO envia de fato
  ```
  (continuação em `main.js` @3833450).
- Gate de DEVOLUÇÃO - `main.js` @3841210 (só quando `cboMensagem == "PACS.004"`): mesma lógica com
  `validaMotorAntiFraude(payloadDev, false)`.

Consequência de fidelidade: o enforcement é CLIENT-SIDE. Um chamador que bata direto em
`POST /api/Entrada/addPagto` (`SPI.Web.Api.decompiled.cs:724-738`) NÃO passa pelo motor. O motor
legado NÃO cobre o caminho automático/API (o `addPagto`/`InserePgtoComConversao` não chama
antifraude). Portanto o escopo real do controle legado é o ENVIO MANUAL pela tela (canal
`front-spi`), tanto para pagamento quanto para devolução.

### 1.2 O caso de uso (motor de decisão)

`MotorAntiFraudeUseCase` - `SPI.Core.Application.decompiled.cs:6648-7095`.

- `ValidaPagamentoMotorAntifraude` (`:6709-6715`): gera um EndToEndId via
  `GeraEndToEndId(IspbDebtor, DtHrOperacao, IdSystem, bUtilizado:false)`, seta `payload.EndToEndId`,
  `MapFromPayment`, chama `AntiFraudeValidacao(..., ehPacs008:true)`.
- `ValidaDevolucaoMotorAntifraude` (`:6717-6741`): busca a mensagem original por
  `FindFullByEndToEndId(EndToEndIdMsgDev)`. Se NÃO achar, retorna Reprovado, Codigo 0,
  "Pagamento não encontrado.", `MotorHabilitado=false` (`:6722-6730`). Se achar, gera `RtrId`
  (`GeraRtrId`), `MapFromDevolucao`, chama `AntiFraudeValidacao(..., ehPacs008:false)`.
- `MapFromPayment` (`:6743-6767`) monta `MotorAntiFraudeDTO`: `EndToEndId`, `Valor`, `Timestamp`,
  `Canal="front-spi"`, `USER_ID`, `USER_AGENT`, `TX_TYPE="PACS.008"`,
  `Origem{Nome=NmDebtor, Cpf=CpfCnpjDebtor, Banco=IspbDebtor, Ip=ipReq}`,
  `Destino{Nome=IspbCreditor, Cnpj=CpfCnpjCreditor}`.
- `MapFromDevolucao` (`:6769-6793`): `TX_TYPE="PACS.004"`, Origem/Destino da `message.PaymentData`,
  `Ip=ipReq`.

`AntiFraudeValidacao` (`:6795-6903`) - o núcleo:
1. Se `_parametros == null` retorna "Em Análise", `MotorHabilitado=false` (pass-through) `:6806-6809`.
2. `flag = AtivaMotorAntiFraude`; se `!flag` retorna `MotorHabilitado=false` (motor desligado =
   pass-through, deixa seguir) `:6810-6817`.
3. `UrlMotorAntiFraude` precisa começar com `https://`, senão Reprovado 404 "A URL deve ser HTTPS."
   `:6818-6821`.
4. Grava histórico `EnviadaMotorAntiFraude` (`:6824`).
5. `POST` externo `PostJsonAsync<MotorAntiFraudeDTO, AntiFraudeResponseValues>(url, fraudeDTO)`
   (`:6825`).
6. `RetornoValidacao(codigo, e2e, FallbackPermitirRejeitar, mensagem, status=="Aprovado"?...)`
   (`:6826`). Se NÃO "Aprovado" e for pacs.008, materializa mensagem
   `ReprovadaPeloMotorAntiFraude` via `AdicionaMsgPagamento`; se devolução, via `AdicionaMsgDev`
   (`:6827-6837`).
7. Grava histórico `Aprovada`/`Reprovada` (`:6838`).
8. Tratamento de erro fail-closed por tipo:
   - `HttpRequestException` -> 500 Reprovado + materializa msg reprovada (`:6841-6854`).
   - `TimeoutRejectedException` -> 408; se `!FallbackPermitirRejeitar`, materializa msg reprovada
     (`:6855-6871`).
   - `BrokenCircuitException` -> 503; se `!FallbackPermitirRejeitar`, materializa msg reprovada
     (`:6872-6888`).
   - `Exception` genérica -> 500 Reprovado + materializa (`:6889-6902`).

`RetornoValidacao` (`:6957-6992`): mapeia `HttpStatusCode` -> `Codigo` (200/408/503/404/500),
`Fallback = FallbackPermitirRejeitar`, `MotorHabilitado = true`.

Interpretação do fail-closed/fail-open: `Fallback`/`permiteFallback` = valor de
`FallbackPermitirRejeitar`. No gate do frontend, `408/503 && !permiteFallback` BLOQUEIA (fail-closed
no timeout/circuito). Com `FallbackPermitirRejeitar=true` a operação PROSSEGUE no timeout/circuito
(fail-open configurável). Reprovação dura (status != Aprovado com codigo != 408/503) SEMPRE bloqueia.

### 1.3 Resiliência e autenticação do motor

- `MotorAfPolicyProvider` (`:6599-6647`) - Polly wrap: `TimeoutAsync(TimeOutMotorAntifraude ms,
  default 500)` (`:6618,6620`), `CircuitBreakerAsync(QtdTentativaFallback default 2,
  DuracaoFallback seg default 5)` (`:6616-6617,6622`), `FallbackAsync -> 503 "Serviço indisponível."`
  (`:6634-6637`).
- `HttpClientHandlerBase` (`:6500-6588`) - login JWT em `UrlLoginMotorAntiFraude` com
  `{username=NomeUsuarioMotorAntiFraude, password=SenhaUsuarioMotorAntiFraude}` (`:6510-6514`),
  token cacheado até `ValidTo` com renovação 1 min antes (`:6500-6525`), Bearer nos headers
  (`:6546-6555`).

### 1.4 Persistência (trilha antifraude)

- Domínio `HistMotorAntiFraude` - `SPI.Core.Domain.decompiled.cs:241-254`:
  `{InstructId, CdMsg, IdStatus (enumStatusOperacao), Mensagem, JsonMsg, DtOperacao}`.
- `AdicionarHistoricoMotorAntiFraude` (`SPI.Core.Application.decompiled.cs:6905-6924`): MASCARA PII
  antes de gravar (`MaskString` em Origem.Cpf/Nome/Banco, Destino.Cnpj/Nome), trunca `Mensagem` a
  300 chars, serializa o DTO mascarado em `JsonMsg`, `DtOperacao=DateTime.Now`, `SaveAsync`.
- `MaskString` (`:7080-7094`) - mostra 2 primeiros + 2 últimos, resto 'x'.
- Tabela `crk_spi.dbo.SpiHistMotorAntiFraude` - EF config
  `SPI.Core.Angular.Infrastructure.decompiled.cs:161-177`:
  PK `{InstructId, DtOperacao, CdMsg}`; `InstructId char(32)`, `CdMsg varchar(10)`, `IdStatus int`,
  `Mensagem varchar(300)`, `JsonMsg varchar(max)`, `DtOperacao datetime`. Write repo
  `MotorAntiFraudeRepository` (`:534,674-690`), com deduplicação por `InstructId` no `Local`
  (`SPI.Core.Infrastructure.decompiled.cs:3096-3101`).

Status antifraude (enum `enumStatusOperacao` - `SPI.Core.General.decompiled.cs:7204-7215`):
`44 EnviadaMotorAntiFraude`, `45 ReprovadaPeloMotorAntiFraude`, `46 AprovadaPeloMotorAntiFraude`,
`47 ErrorRetornoMotorAntiFraude`, `48 FallbackMotorAntiFraude`, `49 TimeoutMotorAntiFraude`.

### 1.5 Materialização de mensagem reprovada

- `AdicionaMsgPagamento` (`:6926-6946`): monta `MessageFull` com `IdStatus=ReprovadaPeloMotorAntiFraude`,
  `CdMsg=PACS.008`, `MsgDefIdr=pacs.008.spi.1.13`, valida via `ValidaPayment.Valida` e persiste com
  `SaveMessage` (mensagem terminal reprovada, visível no monitor).
- `AdicionaMsgDev` (`:6948-6955`): idem para `PACS.004` (`pacs.004.spi.10`).

### 1.6 Configuração (parametrização)

- Domínio `ParametroMotorAntiFaude` - `SPI.Core.Domain.decompiled.cs:787-806`:
  `AtivaMotorAntiFraude` (bool), `UrlMotorAntiFraude`, `UrlLoginMotorAntiFraude`,
  `NomeUsuarioMotorAntiFraude`, `SenhaUsuarioMotorAntiFraude`, `QtdTentativaFallback` (int),
  `DuracaoFallback` (int), `TimeOutMotorAntifraude` (double), `FallbackPermitirRejeitar` (bool).
- Armazenado como JSON em `crk_spi.dbo.SpiCadParam` onde `IdParam='MotorAntiFraude'` (lido por
  `ValorParametro<string>(null, "MotorAntiFraude", null)` - `:6701,6614`).
- Tela de configuração no Angular: chaves i18n `txtAtivaMotorAntiFraude`, `txtUrlMotorAntiFraude`,
  `txtUrlLoginMotorAntiFraude`, `txtNomeUsuarioMotorAntiFraude`, `txtSenhaUsuarioMotorAntiFraude`,
  `txtTimeOutMotorAntifraude`, "Quantidade Tentativas Fallback Motor Antifraude", "Duração Fallback
  Motor Antifraude", validações `txtUrlMotorAntifraudeObrigatorio`,
  `txtValidacaoUrlMotorAntifraudeInvalida` (assets/i18n/pt-BR.json).

### 1.7 Relatórios e métricas

- `ConsultaMotorAntiFraudeUseCase` - `SPI.Core.Application.decompiled.cs:6432-6457` +
  `SPI.Core.Angular.Application.decompiled.cs:142-186`: `FindByEndToEndId`,
  `FindByListDadosEndToEndId`, `FindByDataMovimento`, `GroupByInstructId`, `GroupByCdMsg`,
  `GroupByIdStatus`, `GetMetricasPorData` (`MotorAntiFraudeMetricas`).
- API Angular: `ListHistoricoMotorAntiFraude(idEndToEnd)` e `ListHistoricoMotorAntiFraude(dtDe,
  dtAte, agruparPor, tipoRelatorio)` - `SPI.Web.Angular.Api.decompiled.cs:1975,1995`.
- Telas i18n: "Extrato Motor Antifraude", "Relatório de Métricas Motor Antifraude"
  (assets/i18n/pt-BR.json). Métricas agrupadas por `Enviadas/Aprovadas/Reprovadas` etc
  (`SPI.Core.Angular.Infrastructure.decompiled.cs:619-628`).

### 1.8 DTOs

- `MotorAntiFraudeDTO` - `SPI.Core.General.decompiled.cs:11512-11531`
  (`EndToEndId, Valor, Origem, Destino, Timestamp, Canal, USER_ID, USER_AGENT, TX_TYPE`).
- `Origem{Nome,Cpf,Banco,Ip}` :11532; `Destino{Nome,Cnpj}` :11542.
- `AntiFraudeResponseValues{status,codigo,mensagem}` :4564 (resposta do motor externo).
- `AntiFraudeReturnValues{Status,Codigo,Mensagem,EndToEndId,Fallback,MotorHabilitado}` :5515.

### 1.9 Prova empírica no DB vivo (`crk_spi`)

- `SELECT COUNT(*) FROM crk_spi.dbo.SpiHistMotorAntiFraude` = 54.
- `GROUP BY IdStatus, CdMsg`: `44 PACS.008 = 27`, `47 PACS.008 = 27`. Ou seja: 27 pagamentos
  passaram pelo motor (EnviadaMotorAntiFraude), e todos os 27 voltaram `ErrorRetornoMotorAntiFraude`
  (o motor externo em `localhost:7166` recusava conexão no ambiente de teste). Nenhuma devolução
  (CdMsg=PACS.004) foi exercitada.
- Amostra (PII já mascarada na origem): `JsonMsg` com `"Canal":"front-spi"`,
  `"Origem":{"Cpf":"58xxxxxxx68","Banco":"12xxxx78","Ip":"198.18.45.5"}`, `"Destino":{...}`,
  `Mensagem="Falha na requisição: No connection could be made ... (localhost:7166)."`.
- Janela de dados: `MIN(DtOperacao)=2026-01-19 11:49`, `MAX=2026-04-29 11:45`.
- Config viva `crk_spi.dbo.SpiCadParam` `IdParam='MotorAntiFraude'`:
  `{"AtivaMotorAntiFraude": false, "UrlMotorAntiFraude": "https://localhost:7166/...",
  "UrlLoginMotorAntiFraude": "https://localhost:7166/auth/login", "NomeUsuarioMotorAntiFraude":
  "admin", "SenhaUsuarioMotorAntiFraude": <omitido>, "QtdTentativaFallback": 0, "DuracaoFallback":
  0, "TimeOutMotorAntifraude": 500, "FallbackPermitirRejeitar": false}` (fail-closed configurado;
  motor atualmente desligado neste snapshot, mas foi exercitado Jan-Abr/2026).

CONCLUSÃO PARTE 1: feature completa e WIRED no legado (fronteira HTTP + gate frontend + motor
externo + resiliência + histórico mascarado + materialização de reprovado + config UI + relatórios),
escopo canal `front-spi` (manual), fail-closed por padrão, exercitada em produção de teste.

---

## PARTE 2 - NOSSO: comportamento com prova

### 2.1 Ausência do motor antifraude em todo o money-path

Grep no `pix/backend/apps` por `antifraud|validaAntiFraude|motor.*fraud|fraud.*engine|risk_engine|
risk_score|scoring` = ZERO hook real. Os únicos hits de "fraud":
- `fraud_markers` (registro de marcador de fraude do BACEN, DICT - reativo, não é scoring
  pré-transação), `dict_proxy_controller.ex:442-472`, `fraud_controller.ex`, etc.
- `dict_client.ex:132,143` - comentários explicando que o BACEN usa a consulta DICT para
  "antifraude e limite por usuario" (é o antifraude do BACEN, não nosso motor).
- `dict_service/lib/dict_service/statistics.ex:3,25-27,49,174` - estatísticas antifraude COMPUTADAS,
  mas o próprio código admite: "hoje nenhum decisor automático consome estes números ... o wiring
  da decisão antifraude fica registrado como follow-up do P9a".
- `shared/priv/repo/seeds/gap_resolution_seed.sql:602` - `('FRAUD_ENGINE', 'Motor Antifraude', ...)`
  é apenas um code em `monetarie_spi_ref.ref_sources` (rótulo de proveniência de dado, INSERT em
  `ref_sources` `:584`), sem nenhum motor que produza ou consuma esse rótulo.

### 2.2 Fronteira de entrada do PIX-out (fluxo real Core -> cabine)

`CoreEventProcessor.handle_payment_request` -
`settlement_service/lib/settlement_service/workers/core_event_processor.ex:227`. Validações ANTES de
montar/assinar/enviar, em ordem:
1. `resolve_sender_ispb` fail-closed (`:249-266`).
2. `resolve_e2e_id` (exige E2E da consulta DICT em cache, senão `DICT_CONSULT_REQUIRED`)
   (`:276-290`).
3. `check_and_block_balance` (saldo Conta PI + bloqueio) (`:299`).
4. `SettlementService.Spi.Pacs008SendValidator.validate` (`:653`) - validação SEMÂNTICA/estrutural
   (forma de iniciação x TxId/chave, finalidade x tipo de valor, prioridade), espelho do
   `ValidaPaymentUseCase`, NÃO antifraude.

Nenhuma chama motor antifraude externo. Não há captura de IP/User-Agent/USER_ID/Canal para scoring
neste caminho.

### 2.3 Devolução (pacs.004)

`return_processor.ex:43-44` - `validate_return_eligibility` + `validate_spi_return_rules`
(`SpiValidator.validate_outbound_return`). São validações de elegibilidade/regra estrutural, sem
motor antifraude externo (grep antifraud/fraud/http/scoring no arquivo = 0 hooks).

### 2.4 Envio ao BACEN (OutboundSender)

`spi_service/lib/spi_service/workers/outbound_sender.ex` só tem: guarda de
`alcada_status != "awaiting_approval"` (defesa em profundidade do gate manual, `:162,417-419`) e
validação XSD estrutural (`:164,268,473`). Nenhum antifraude.

### 2.5 Gate manual de valor (alçada) - existe, mas não é antifraude e não cobre o automático

`Shared.Alcada.SendGate` - `shared/lib/shared/alcada/send_gate.ex:10-16`: "O fluxo AUTOMÁTICO do
Core (`payment_request` do IB/Partner via `CoreEventProcessor`) já passa por limites/alçada NO CORE
(Onda 1) e NÃO pode ser retido aqui ... O gate aplica-se ao ENVIO MANUAL/admin: tela Construir
Mensagem". É um controle de VALOR/alçada (4-olhos), não scoring antifraude; e (conforme mapa D5)
com flag `ALCADA_PIPELINE_ENABLED` default DESLIGADO. Chamado só em
`settlement_service_web/controllers/message_controller.ex` e `spi_service_web/.../payment_controller.ex`.

### 2.6 Frontend (Vue admin) - sem UI de motor antifraude

Grep em `pix/frontend/admin/src` por `antifraud|antifraude|MotorAntiFraude` = 0. Não há tela de
configuração do motor, nem "Extrato Motor Antifraude", nem "Relatório de Métricas".

### 2.7 Persistência - sem trilha equivalente

Não existe tabela/schema equivalente ao `SpiHistMotorAntiFraude` (histórico de decisões antifraude
por E2E). Grep por `hist.*motor|motor.*fraud|antifraud.*hist` no `pix/backend` = 0.

### 2.8 Controles compensatórios existentes (adjacentes, não são o motor)

- Rate-limit DICT por tier BACEN + penalidade de 404 (anti-enumeração) -
  `dict_service/.../plugs/dict_rate_limit.ex` (mapa 08/D4). Reativo por cota, não scoring.
- Uso único do E2E por pagamento (`Shared.E2eBurn.burn`, `core_event_processor.ex:599`) - impede
  reusar consulta para vários pagamentos, não limita quem consulta sem pagar.
- Marcadores de fraude DICT (registro/consulta ao BACEN) - `fraud_markers.ex`, reativo.
- Limites/alçada NO CORE (Onda 1) - controle de valor no Core Banking, fora da cabine.

CONCLUSÃO PARTE 2: a cabine NÃO tem motor antifraude pré-transação em nenhum caminho de dinheiro.
A defesa é: validação estrutural (Pacs008SendValidator/SpiValidator), controle de valor no Core,
rate-limit/uso-único de E2E no DICT, e marcadores de fraude reativos. Nenhum é o motor externo do
legado.

---

## PARTE 3 - GAPS

| id | Título | Tipo | Risco | Prova legado | Prova nosso |
|----|--------|------|-------|--------------|-------------|
| g1 | Motor antifraude externo pré-envio (PIX-out) ausente | ausente | alto | `SPI.Web.Api:934-946`, `SPI.Core.Application:6709-6903` | `core_event_processor.ex:227-266,653`; grep antifraud=0 |
| g2 | Motor antifraude pré-devolução (pacs.004) ausente | ausente | alto | `SPI.Web.Api:953-964`, `MotorAntiFraudeUseCase:6717-6741` | `return_processor.ex:43-44`; grep antifraud=0 |
| g3 | Captura de sinais de sessão (IP/User-Agent/UserId/Canal) para scoring | ausente | medio | `MapFromPayment:6743-6767` (Canal=front-spi, Ip, USER_ID, USER_AGENT) | nenhuma captura no money-path da cabine |
| g4 | Fail-closed/fail-open configurável + resiliência (timeout/circuit/fallback) | ausente | medio | `AntiFraudeValidacao:6810-6902`, `MotorAfPolicyProvider:6599-6647`, `FallbackPermitirRejeitar` | inexistente na cabine (só XSD/alçada) |
| g5 | Trilha auditável de decisão antifraude por E2E (mascarada) | ausente | medio | `SpiHistMotorAntiFraude` (`Infra:161-177`), `AdicionarHistorico:6905-6924` | sem tabela/schema equivalente |
| g6 | Relatórios/métricas antifraude (Extrato + Métricas + agrupamentos) | ausente | baixo | `ConsultaMotorAntiFraudeUseCase:6432`, `SPI.Web.Angular.Api:1975,1995` | grep frontend/backend=0 |
| g7 | Config/parametrização do motor (ativação, URL, credencial, timeouts) | ausente | baixo | `ParametroMotorAntiFaude:787-806`, `SpiCadParam IdParam='MotorAntiFraude'` | inexistente |
| g8 | Materialização de mensagem terminal "Reprovada pelo Motor Antifraude" | ausente | info | `AdicionaMsgPagamento:6926`, `AdicionaMsgDev:6948`, status 45 | inexistente |

Caveat de fidelidade honesto: o controle legado é ENFORCADO NO FRONTEND (canal `front-spi`), não é
um chokepoint server-side. Quem chama `addPagto`/`devPagto` direto (ou o caminho automático/API)
NÃO passava pelo motor nem no legado. Logo o gap de PARIDADE mais forte é no ENVIO MANUAL pela tela;
para o caminho automático, nem legado nem cabine têm motor (na cabine o controle de valor mora no
Core). A FEATURE inteira (scoring externo, histórico, config, relatórios) está ausente na cabine.

---

## PARTE 4 - Itens COBERTOS / adjacentes (para dar confiança)

Nenhum deles é o motor antifraude, mas cobrem parte da superfície de controle pré-envio:

- c1 (coberto adjacente): validação estrutural/semântica fail-closed do PIX-out. Legado
  `ValidaPaymentUseCase`; nosso `Pacs008SendValidator.validate` (`core_event_processor.ex:653`) e
  `SpiValidator` na devolução (`return_processor.ex:44,174-184`). Paridade nesse controle específico
  (forma de iniciação, finalidade x tipo de valor, prioridade), que NÃO é antifraude.
- c2 (parcial): controle de valor pré-envio. Legado = alçada/limites; nosso = `Shared.Alcada.SendGate`
  (manual, `send_gate.ex:10-16`) + limites no Core (Onda 1). Cobre valor/4-olhos, não scoring.
- c3 (parcial): anti-abuso de consulta. Legado = correlação consulta x pagamento (Worker.Protecao,
  mapa 08); nosso = rate-limit DICT + penalidade 404 + uso único de E2E (`dict_rate_limit.ex`,
  `E2eBurn.burn` em `core_event_processor.ex:599`). Defesa por cota/enumeração, não o motor.
- c4 (coberto): marcadores de fraude do BACEN (registro/consulta reativos) presentes nos dois lados
  (legado + `fraud_markers` no DICT da cabine).

---

## Resumo executivo

| # | Tema | Veredito | Prova-chave |
|---|------|----------|-------------|
| g1 | Motor antifraude PIX-out | AUSENTE (alto) | legado `SPI.Core.Application:6709-6903`; cabine grep antifraud=0, `core_event_processor.ex:227-266` |
| g2 | Motor antifraude devolução | AUSENTE (alto) | legado `MotorAntiFraudeUseCase:6717-6741`; `return_processor.ex:43-44` |
| g3 | Sinais de sessão p/ scoring | AUSENTE (medio) | `MapFromPayment:6743-6767` vs cabine sem captura |
| g4 | Fail-closed/open + resiliência | AUSENTE (medio) | `AntiFraudeValidacao:6795-6902` + Polly `:6599-6647` |
| g5 | Trilha `SpiHistMotorAntiFraude` | AUSENTE (medio) | `Infra:161-177`, DB 54 linhas |
| g6 | Relatórios/métricas | AUSENTE (baixo) | `ConsultaMotorAntiFraudeUseCase:6432` |
| g7 | Config do motor | AUSENTE (baixo) | `ParametroMotorAntiFaude:787-806`, `SpiCadParam` |
| g8 | Msg "Reprovada pelo Motor" | AUSENTE (info) | `AdicionaMsgPagamento:6926` |
| c1 | Validação estrutural fail-closed | COBERTO adjacente | `Pacs008SendValidator` `:653` |
| c4 | Marcadores de fraude DICT | COBERTO | `fraud_markers` (ambos os lados) |
