# Full deep audit do front PIX admin (2026-06-25)

Auditoria multi-agente (7 auditores + verificacao adversarial) ancorada em: codigo do
front `pix/frontend/admin/src`, backend `pix/backend`, legado `.NET`
`/Users/luizpenha/mwbank/LegadoPIX`, front antigo `/Users/luizpenha/mwbank/frontend/pix-admin`
e catalogo BACEN v5.12.1. Cada achado tem arquivo:linha real. Distingue bug de FRONT de
bug de BACKEND. Nada inferido. Prova de dados ao vivo no DB incluida.

Nota de honestidade: as verificacoes adversariais das telas "Nova Transacao" e parte da
"Central de Mensagens" cairam em rate-limit da API durante o workflow; esses dois pontos
eu reli e confirmei manualmente no codigo (marcados como "verificado a mao").

## Prova de dados ao vivo (raiz comum de 3 queixas)

Contagem real das tabelas (DB vivo):

| Camada | Tabela | Linhas |
|--------|--------|--------|
| Trafego BACEN cru / auditoria | `monetarie_spi.bacen_outbound` | 69 |
| | `monetarie_spi.bacen_inbound` | 1948 |
| | `monetarie_audit.xml_audit_logs` | 1929 |
| **Negocio (o que a UI le)** | `monetarie_spi.messages` (+ particoes) | **0** |
| | `monetarie_spi.payments` | **0** |
| | `monetarie_spi.transactions` | **0** |
| | `monetarie_spi.message_history` (timeline) | **0** |
| | `monetarie_spi.transaction_returns` | **0** |

Conclusao: o sistema grava o trafego cru, mas NAO popula a camada de negocio. As pacs.008
que enviei nos testes sairam por rpc cru (`SpiClient` direto), que NAO cria linha em
`messages`. A operacao `204f3bb9` do screenshot do dono existe so em `xml_audit_logs`
(coluna `end_to_end_id = NULL`, embora o XML tenha
`EndToEndId = E46026562202606250335fijaok2aoay`). Das 15 pacs.008 enviadas: 15 tem E2E no
XML, 0 tem a coluna preenchida.

## Tela 1 — Monitor de Operacoes / Detalhe + Timeline (queixa: cade a pacs.002/timeline/E2E)

Causa raiz: BACKEND. O front esta correto.

- [HIGH][BACKEND] `BacenLogger.log_exchange/1` nao extrai o EndToEndId do XML.
  `apps/shared/lib/shared/audit/bacen_logger.ex:55-71` monta `audit_attrs` sem
  `end_to_end_id`/`tx_id`/`pi_end_to_end_id`. A coluna existe e o changeset a aceita
  (`xml_audit_log.ex:55` @optional_fields). Ja existe o extrator pronto e nao usado:
  `Shared.Bacen.Iso20022.MessageParser.extract_end_to_end_id/1`
  (`message_parser.ex:166-181`, com fallback OrgnlEndToEndId). O legado fazia isso com
  colunas de primeira classe `EndToEndId` + `EndToEndIdOrig` (MessageConfiguration.cs:28,40)
  e correlacionava a operacao inteira (incluindo Historico/timeline) por E2E.
  Fix: em `bacen_logger.ex`, antes do `audit_attrs`, calcular
  `xml = request_body || response_body` e `e2e = MessageParser.extract_end_to_end_id(xml)`,
  e adicionar `end_to_end_id: e2e`, `pi_end_to_end_id`, `tx_id` (regex `<TxId>`),
  `pi_resource_id`, `pi_requesting_participant`, `pi_payer_id`.
- [HIGH][BACKEND] pacs.002 de retorno nao correlaciona porque o `OrgnlEndToEndId` tambem
  nao e gravado. Resolvido pelo mesmo fix (o extrator ja trata OrgnlEndToEndId). Recomendado
  guardar tambem `original_end_to_end_id` separado para distinguir ida de volta.
- [MEDIUM][FRONT] `OperationsMonitorView.vue:115-118` (correlationKey) + `:561`/`:204`
  (coluna E2E) + `:743-745` (ramo "sem E2E"): a UI esta certa, so depende 100% do backend
  gravar a coluna. Apos o fix de backend, validar com screenshot do drawer mostrando E2E +
  timeline ida/volta.

## Tela 2 — Painel / Total de Transacoes 0 (queixa: cade o total contabilizando certo)

- [HIGH] O card conta `monetarie_spi.messages`, que esta VAZIA
  (`dashboard_controller.ex:128`). O card e honesto; o defeito esta no FLUXO de envio que
  nao grava a linha canonica. Fix: corrigir o fluxo (ver Tela 3), nao a query.
- [MEDIUM][FRONT] `DashboardView.vue:77` engole erro do endpoint e cai no mesmo empty-state,
  mascarando "0 real" vs "falha 500/RBAC". Fix: flag de erro separada + aviso "falha ao
  carregar" com retry.
- [LOW] "Atividade Recente" e REAL (audit_logs vivos), nao mock. Saldo/Chaves/Participantes
  conferem com a fonte.

## Tela 3 — Transacoes (lista + detalhe + devolucoes) (queixa: nenhuma transacao aparece)

- [HIGH] Lista vazia porque `monetarie_spi.messages` esta vazia. O endpoint
  (`payment_controller.ex:35`) e a query estao certos; pacs.008 enviada por rpc cru
  (`spi_client.ex:93`) nao cria transacao. Fix: persistir a transacao no fluxo (rotear pelo
  `PaymentController.create`/`OutboundSender` que insere messages+payments), ou gravar a
  linha tambem no caminho cru.
- [HIGH] Aba Devolucoes SEMPRE vazia: `return_controller.ex:23` retorna `[]` hardcoded
  (nem consulta a tabela). Fix: implementar o index real consultando
  `SpiService.Transactions.Return`.
- [HIGH] Criar devolucao tem contrato divergente front/back: o front manda `reason`
  (`TransactionDetailView.vue:113`) e o controller espera `reason_code`
  (`return_controller.ex:47`) e descarta. Definir tambem a unidade do `amount`
  (centavos vs reais). Fix: alinhar o contrato.
- [MEDIUM][FRONT] Filtro "Instrumento" oferece `IPMF`, codigo invalido que nunca casa
  (`TransactionListView.vue:81` vs `payment_controller.ex:493`). Fix: remover IPMF, adicionar
  INIC e AUTO; idealmente alimentar do catalogo do back.

## Tela 4 — Nova Transacao / pagamento por chave (queixa: por chave ainda obriga campos) — verificado a mao

Arquivo: `views/transactions/TransactionCreateView.vue`.

- [HIGH][FRONT] NAO existe resolucao DICT. Ao preencher a "Chave PIX" (`:119-121`), o form
  NAO dispara GetEntry para resolver o recebedor; o bloco inteiro "Recebedor (Creditor)"
  (`:150-167`) continua visivel pedindo ISPB/Conta/Nome/Documento manualmente. Num pagamento
  por chave esses dados vem do DICT (igual app de banco: digita a chave, aparece o nome do
  recebedor). Fix: ao informar a chave, chamar o GetEntry (com PI-PayerId = CPF do pagador
  + PI-EndToEndId), exibir o recebedor resolvido em modo leitura e ocultar/desobrigar os
  campos manuais.
- [MEDIUM][FRONT] Rotulo "ISPB *" do recebedor induz a obrigatoriedade que nao se aplica por
  chave (a validacao real em `:50-51` ja aceita "chave OU ISPB", mas o asterisco e o bloco
  visivel contradizem isso). Fix: tornar a obrigatoriedade condicional e visivel (por chave
  some o bloco manual).
- [MEDIUM][FRONT/contrato] Ha DOIS seletores concorrentes: "Instrumento" (DICT/QRES/QRDN/MANU,
  `:40-43`) e "Tipo de Chave PIX" (`:113-116`). Precisa garantir que, havendo chave, o envio
  va com `local_instrument=DICT` e SEM `tx_id` (o backend ja faz esse default desde o commit
  `4b711a28`, mas o form deve mandar `pix_key` como proxy e nao mandar tx_id). Validar o que
  `store.createTransaction(form.value)` serializa de fato.

## Tela 5 — CAMT.060 Consulta de Saldo (queixa: painel de merda que nao funciona)

- [HIGH] As 7 abas mandam a MESMA camt.060: o backend descarta todos os parametros da aba.
  `SpiService.Camt060.query/3` chama `send_camt060(ispb)` so com o ISPB
  (`camt060.ex:30-34,108-118`); o map de params nunca chega ao `SpiClient.get_balance`.
  Saldo atual, Saldo na data, Lista lancamentos, Detalha lancamento, TIR, Total TIR e
  Remuneracao geram XML byte-a-byte identico. Fix: parametrizar por acao (reqd_bal_tp,
  rptg_prd, id, reqd_msg_nm_id), espelhando MontaId/MontaRptgPrd/MontaTipoRel do legado.
- [HIGH] O XML camt.060 do `SpiClient` esta fora do XSD v5.12 (`spi_client.ex:426-454`):
  usa `<Acct>` inexistente, `AcctOwnr` sem `<Agt>`, sem `RptgPrd`, sem `ReqdBalTp`. (Mesma
  classe de bug que ja corrigi no builder do `message_builder.ex` nesta sessao, mas este
  segundo builder do SpiClient ficou para tras.) Fix: reescrever seguindo o XSD.
- [MEDIUM][FRONT] A area "Resposta" nunca mostra o saldo real: zero correlacao da camt.053
  inbound (SADP/SABK) e zero refresh do retorno assincrono
  (`Camt060ToolView.vue:459-512`). Fix: persistir o BizMsgIdr da camt.060, correlacionar a
  camt.053 de retorno e renderizar SADP->disponivel / SABK->bloqueado (parser ja pronto).
- [MEDIUM][FRONT] Labels errados: `reqd_msg_nm_id` chamado de "Tipo de Resposta" com camt.052/053;
  `ReqdBalTp` (CSA/REL/TRD) nem existe na UI (`:82-99,128-130`).
- [MEDIUM] Segundo form orfao `components/messages/forms/Camt060Form.vue` cai num TERCEIRO
  builder malformado (`message_builder.ex:602-618`, default invalido `AVLB`). Fix: consolidar
  num unico builder camt.060 conforme o XSD.
- [MEDIUM] Status sempre "OK" mesmo quando a camt.060 falha/e rejeitada (`camt060.ex:41-53`).

## Tela 6 — Central de Mensagens / Construir Mensagem (queixa: desde quando construo msg de retorno?)

A queixa procede. Verificado a mao:

- [HIGH] O dropdown oferece para CONSTRUIR E ENVIAR tipos que so chegam do BACEN ou so
  existem como resposta automatica: camt.025, camt.052, camt.053, camt.054, admi.004,
  pacs.002, pibr.002, admi.002. O catalogo (`message_controller.ex` `message_type_catalog/0`)
  lista 22 tipos e o front oferece todos.
- [HIGH] O campo `direction` e PURAMENTE COSMETICO: gera so o badge Saida/Entrada
  (`MessageBuilderView.vue:271-276`). Nem o front nem o backend filtram por direcao. O gating
  "Envio Permitido" olha SO permissao RBAC (`auth.ts:203-206`), nunca a direcao. O backend
  `send_message` so checa `has_permission? :can_create` (`message_controller.ex:135`) e tem
  clausula generica de dispatch (`:664-669`) que faz POST de qualquer tipo. Fix: barrar envio
  dos tipos so-inbound (filtrar por "originavel por participante" no front E no backend;
  fail-closed no back). Originaveis: pacs.008, pacs.004 (devolucao), pain.009/011/012/013,
  camt.029, camt.055, camt.060, pibr.001, admi.004 (ack), reda.014/022/031.
- [MEDIUM] Existe um wizard correto e curado (`composables/useMessageForms.ts`, expoe so
  PIBR.001/REDA.022/CAMT.060, todos originaveis) com MessageWizardStep1/2/3, mas esta ORFAO:
  nenhuma rota o importa (`grep MessageWizard router/` = vazio). A UI boa nao esta no ar e a
  ruim esta.
- [LOW] Rotulos errados no catalogo: camt.052/053 marcados como "REQUEST" (sao a RESPOSTA;
  o request e a camt.060); admi.004 nomeado "SystemEventAcknowledgement" quando no SPI v5.12
  e originado como notificacao.

## Tela 7 — Varredura ampla (transversal)

- [HIGH][FRONT] Detalhe do Participante (`participants/:ispb`): 3 abas 100% MOCK com botoes
  mortos: `ResponsiblesSection.vue:17`, `WebhooksSection.vue:16`, `LimitsSection.vue:10`
  (numeros chumbados; Adicionar/Editar/Remover sao funcoes vazias sem chamada de API). A aba
  Limites ainda e renderizada DUAS vezes (Globais x por Transacao mostram o mesmo objeto fixo,
  `ParticipantDetailView` 317 e 337). Fix: buscar dados reais no onMounted + CRUD real, ou
  ocultar as abas ate o backend existir (nao induzir o operador a achar que configurou algo).
- [MEDIUM][FRONT] i18n: so 43 das 105 views usam `useI18n`; 62 telas tem pt-BR cravado no
  template, e o cabecalho oferece troca para EN/ES/FR. Fix: externalizar strings.

## Sintese das causas-raiz (poucas causas, muitos sintomas)

1. **Camada de negocio nao e populada** (`messages`/`payments`/`message_history`/`returns`
   vazias) -> derruba Painel (total 0), Transacoes (lista vazia), Timeline. Causa: envio nao
   grava a linha canonica (rpc cru + 3 bugs latentes do OutboundSender ja documentados).
2. **E2E nunca extraido para a coluna** -> Monitor mostra travessao e timeline nao monta.
   Causa: `BacenLogger` nao chama o extrator que ja existe.
3. **camt.060 nao parametrizado + 3 builders divergentes (2 malformados)** -> a ferramenta
   inteira manda a mesma mensagem fora do XSD e nunca mostra o saldo real.
4. **Direcao nao e enforced** -> a Central deixa enviar mensagens de retorno/inbound.
5. **Telas mock em producao** (Participante: Responsaveis/Limites/Webhooks).

## Plano de ondas proposto

- Onda A (dados aparecem): fix do fluxo de envio para gravar `messages`/`payments` + extrator
  de E2E no `BacenLogger`. Resolve Painel + Transacoes + Monitor/Timeline + pacs.002
  correlacionada de uma vez.
- Onda B (pagamento por chave no front): resolucao DICT + ocultar/desobrigar recebedor.
- Onda C (camt.060): unificar builder conforme XSD + parametrizar as 7 abas + render do saldo
  real (camt.053 SADP/SABK).
- Onda D (central de mensagens): enforcement de direcao (so-inbound bloqueado) front+back;
  opcional: ligar o wizard curado.
- Onda E (devolucoes + participante mock + i18n): index real de devolucoes, contrato
  reason_code, remover/implementar abas mock, externalizar strings.
