# Handoff 2026-07-25 (2a sessao): recebedor, contratos de webhook e pagador institucional

Tudo aqui foi medido contra producao ou provado em teste. Onde a sessao anterior
errou, esta escrito o que era e o que passou a ser.

---

## 1. Estado final

`main` intocada em `3a2358f5`. **5 commits locais, SEM push:**

| commit | assunto |
|---|---|
| `3cb54426` | PIX-in: nome do recebedor quando o cliente e nosso |
| `45913f4b` | backfill do acervo do recebedor |
| `a33be337` | normalizacao do CPF colado no nome (legado) |
| `d0d44521` | webhooks: forma estavel, apelidos, `eventType` nos dois trilhos |
| `ba5330a3` + `afb75cd6` | pagador institucional na TED recebida |

| servico | HML | PRD | digest |
|---|---|---|---|
| pix-api | td **218** | td **88** | `sha256:39b05e4f` |
| core-api | td **223** | td **112** | `sha256:12721e2e` |

Guard de digest MATCH em todos os retags. Suites: pix **4589/0**, core **8943/0**.

---

## 2. RECEBEDOR aparecia como CPF (chamado do dono, com print)

**O BACEN nao manda `<Cdtr><Nm>` na pacs.008 recebida.** Lido no fio, em PRD:

```
<Dbtr><Nm>Luiz Marcelo Goncalves da Costa Penha</Nm>...  pagador TEM nome
<Cdtr><Id><PrvtId><Othr><Id>32189410835</Id>...</Cdtr>   recebedor SEM <Nm>
```

39/39 PIX-in de 20 dias com `debtor_name`; so 5/39 com `creditor_name`. Nao e
defeito de parser, e a tela nao errou (`partyMain` cai no documento).

O defeito nosso: o recebedor e cliente NOSSO e a **propria mensagem do BACEN**
diz qual conta e qual chave (`<CdtrAcct><Prxy><Id>`), e nunca liamos o nosso
cadastro. `SpiService.Inbound.OwnCreditorName` + backfill do acervo.

| | HML | PRD |
|---|---|---|
| PIX-in sem nome do recebedor | 151 -> **6** | 38 -> **14** |

Os restantes nao tem correspondencia no nosso cadastro: o modulo devolve nil e a
tela segue mostrando o documento. **Nunca inventa nome.**

---

## 3. CPF colado no nome (172 legados)

Eram **172**, nao 206 — e a premissa da sessao anterior estava errada. O numero
embutido nao e lixo nem duplicata do documento: a coluna de documento traz
**CNPJ** (14 digitos em 155/155) e o numero colado tem 11. Sao entidades
diferentes: o CNPJ e o titular da conta (PJ), o CPF e o **pagador final (PF)**.

Sao do import legado (zero `resource_id` do BACEN, zero XML no periodo), entao
limpar apagaria o CPF do pagador final sem copia — dado de PLD/FT. Decisao do
dono: backup primeiro.

172/172 normalizados nos 2 ambientes, 0 restantes, **172 linhas em
`monetarie_spi.legacy_party_name_backup`** com o original preservado (INSERT e
UPDATE na MESMA transacao).

**Fora de escopo por decisao do dono:** as 2 chaves em `monetarie_dict.keys` com
o mesmo padrao. Sao NOSSAS e ACTIVE, e `owner_name` e o que o DICT publica: um
UPDATE local nos desalinharia do BACEN em silencio. Registro para a proxima
sessao: **`GetEntry` NAO serve para investigar isso** — no DICT v2.11 ela e a
consulta *antes de pagar*, exige `PI-PayerId`/`PI-EndToEndId` e grava correlacao
antifraude no BACEN. O caminho e o CID sync.

---

## 4. Contratos de webhook: auditoria campo a campo

Relatorio: `docs/reports/2026-07-25-auditoria-campo-a-campo-webhooks.md`.

**RETRATACAO:** a primeira auditoria disse "`pix.charge.paid` monta 9 de 18".
Mediu UM dos QUATRO produtores. O do money-path TB-first ja montava 17. O
defeito real nao era payload incompleto, era **o mesmo evento com quatro formas**
conforme o caminho interno. `pix.received` tem 3 produtores, `pix.payout.returned`
tambem.

Quatro defeitos provados e corrigidos:

1. **`eventType` so ia pelo `dispatch_event/3`.** O `dispatch_event_to/4` nao
   punha — e `webhook.test`, o evento com que o parceiro valida a integracao, so
   existe nesse trilho. O fix de 24/07 pegou metade porque nao havia ponto unico.
2. **Doc promete `sender`, produtor emite `payer`** (3 eventos `pix.payout.*`).
3. **Doc promete `e2eId`, produtor emite `endToEndId`** (5 eventos).
4. **`pix_in_orphan_reconciliation.ex` lia `meta["debtor_name"]`** — chave que
   NAO existe. As chaves reais sao `payer_*` (medido em PRD, 31 transacoes).

`Monetarie.UseCases.Webhooks.Contract` no ponto unico: `eventType` sempre,
apelidos aditivos, e **toda chave documentada presente, nula quando nao temos**.
Isso estabiliza a FORMA; a §3 do relatorio lista o que ainda nenhum produtor
monta, para nao confundir contrato cumprido com dado entregue.

---

## 5. Pagador da TED recebida (ordem do dono)

Meu primeiro diagnostico — "STR0004/0006/0007 nao tem nome de cliente, nao e
defeito" — estava **incompleto**. O dono corrigiu: mesmo sendo outra
instituicao, ela e o pagador. O ISPB sempre veio no fio.

`Spb.PayerIdentity`: nome do cliente (STR0008) -> instituicao do ISPB -> nada. A
identidade institucional viaja MARCADA (`payer_is_institution`), para nao
confundir "CAIXA ECONOMICA FEDERAL" com titular pessoa fisica.

**PRD: 12 TED-in sem pagador -> 0.** 11 com nome real, 1 com o identificador.

### Achado colateral, mais grave

`banks` em PRD tem **UMA** linha (a propria Monetarie) e o `Institutions.Cache`
nem esta no ar (a tabela ETS nao existe no no). Como `resolve_bank_name/1` faz
`rescue -> nil`, o **`payerBankName` dos webhooks `pix.charge.paid` e
`pix.received` SEMPRE foi nulo em producao** — sem erro, log ou alarme. Um
`rescue` que engole a causa transforma cadastro vazio em campo vazio.

Carregadas **150 instituicoes** do diretorio do BACEN (que a cabine tem, 1003)
no `bank_registry` dos 2 ambientes: 32 -> 182.

---

## 6. Fila da proxima sessao

1. **PUSH dos 6 commits** — nada foi pushado.
2. ~~Diretorio de participantes~~ **RESOLVIDO** — ver §9.
3. **`Institutions.Cache` fora da arvore de supervisao** em PRD (sem efeito
   pratico agora: o `PayerIdentity` consulta o diretorio novo primeiro).
4. **6 eventos documentados que nunca sao emitidos** (`pix.charge.cancelled`,
   `.expired`, `pix.payout.held`, os tres `tef.transfer.*`): implementar ou tirar
   da doc.
5. **`pix.received` sem payload documentado** — e o aviso de dinheiro entrando.
6. **Campos que nenhum produtor monta** (§3 do relatorio de webhooks).
7. **2 chaves DICT** com CPF no `owner_name` — investigar via CID sync.
8. Herdados: validar em PRD com transacao real (PIX-out com IPAY, AB03 com
   volume, QR pelo IB); 16 DLQ de HML; lag ICOM p95/p99; portar credito
   coreproviders + `reverse_entry`; conciliacao SPB x Core contra BACEN real.

---

## 7. Retratacoes desta sessao

1. "`pix.charge.paid` monta 9 de 18" -> media 1 de 4 produtores; o principal ja
   estava completo.
2. "As 12 TED-in nao sao defeito, essas mensagens nao tem nome de cliente" ->
   incompleto. O ISPB sempre veio; deixar vazio era escolha nossa.
3. Os 206 legados eram 172, e o numero embutido nao era lixo: era o CPF do
   pagador final.
4. Rodei `docker buildx` em paralelo com a suite e um teste de latencia (ANS
   1600ms) falhou por contencao de CPU — flake meu, nao do codigo.

## 8. Ferramentas

Helpers em `scratchpad/` de sessao (fora do repo). **GOTCHA novo:** chamada HTTP
dentro de `Task.async` + `Task.yield` derruba a sessao do ECS Exec (o `exit` do
task propaga). Use `try/catch` ou dispare em processo desacoplado guardando o
resultado em `:persistent_term`, e leia numa segunda chamada. **PRD so loga
`warning`/`error`** — `Logger.info` nao aparece no CloudWatch.


---

## 9. Diretorio de participantes do SPB/PIX (ordem do dono)

"Preencher o banco de instituicoes do SPB com quem e participante direto do PIX,
pois estes operam STR, e manter sempre atualizado."

**Tabela nova `spb_participants`, chaveada por ISPB.** Nenhuma das existentes
servia, e a razao e estrutural: `banks` tem CHECK de 3 caracteres e UNIQUE em
`code`; `bank_registry` tem UNIQUE em `compe_code`; `institutions` e outro
dominio (tenant da Monetarie). As duas primeiras modelam "um registro por codigo
COMPE" — e **783 dos 885 participantes nao tem codigo COMPE**. Cabia UM; o resto
colidia. Foi por isso que `banks` em PRD tinha uma linha so.

**Fonte oficial, ja existente:** a cabine importa a lista do BACEN todo dia as
09:30 (`Shared.Bacen.PixParticipants.ImportWorker`, CSV/PDF de
`bcb.gov.br/.../participantes_pix`) e expoe em
`GET /api/v1/bacen/pix-participants`. O Core sincroniza as 10:00
(`Monetarie.Workers.SpbParticipantSync`). **O Core nao fala com o BACEN por fora
da cabine.**

**Carga inicial nos 2 ambientes:** 883 participantes, **216 diretos no SPI** (os
que operam STR), carga do BACEN de 2026-07-24. 18 descartados: instituicoes em
adesao ainda sem ISPB.

O cliente PAGINA ate o fim (limite de 500/pagina, ~885 participantes) e para pela
pagina curta, nao pelo `total` do outro lado. `source_date` e `synced_at` ficam na
linha: "esta atualizado?" virou pergunta respondivel.

Com o diretorio, as 12 TED-in institucionais passaram a ter **nome real**
(`MT IP S.A.`, `Caixa Economica Federal`), sem nenhum texto generico.
