# Handoff — Partner API: deploy geral, validação item a item viva, fixes e itens de melhoria — 2026-07-17 (tarde/noite)

`origin/main = ab49616d`, working tree limpa, tudo pushado. Retomar por este handoff + `/retomar`.

## 1. Revisões vivas (ECS, conferidas)

| Serviço | HML | PROD |
|---|---|---|
| core-api | :157 | :61 |
| pix-api | :181 | :58 |
| spb-api | :66 | :37 (inalterado) |

Rollbacks: HML core:156 pix:180; PROD core:60 pix:57. Deploy por MESMO digest homolog→prod (guard MATCH). ZERO migrations aplicadas neste deploy. ICOM CPM+CSM reassumidos 4+4 nos dois ambientes (zero janela surda). Durable novo `core-pix-qrcode-consumer@MONETARIE_SETTLEMENT` ativo em HML e PROD.

## 2. O que foi deployado (commit `5aff22c8`, tag `*-5aff22c8-partnerqr-20260717`)

Arco acumulado da sessão anterior + P0-3, tudo junto:
- **Item B** (outbox cross-repo): `Shared.Outbox.ObanRouting` roteia o job do `publish_async` para a instância Oban do repo em transação.
- **Pré-existentes do item B**: flake 25P02 morto (cache `system_configs`), guard I-1 reativado em teste por app (`MoneyPath.relativize` reancorado em `apps/`), `:money_path` honrado, PromEx cobre as 3 instâncias.
- **QR partner fixes**: janela do CobV (fim do vencimento em BRT + validade), `QrExpiryWorker` (cron */10), salt PS256 explícito = 32.
- **Partner `/pix/charges`**: honra `expiration` (60..2_592_000, 400 fora da faixa).
- **P0-3 ChargePaid**: consumer do `monetarie.settlement.qrcode.paid` → espelho `qrcodes` vira paid + webhook `pix.charge.paid` (dedup `pix.charge.paid:<e2e>` cruzado com o trilho de órfãos; corrida cabine×crédito vira NAK/reentrega).

**Pré-condição executada (gate do QrExpiryWorker):** backfill do `expires_at` dos CobV pré-fix ANTES do worker. HML: 11 CobV ajustados, 0 vencidos restantes (via rpc na task velha, SQL compatível). PROD: 0 CobV (no-op).

## 3. Validação viva da Partner API (como parceiro, HML)

Auth OAuth client_credentials (secret `monetarie/homolog/partner/herbeth-santana/api_credentials`, form-encoded snake_case), túnel SSM 18080 → ALB (Host header). **35 chamadas, todas conformes:** 28 funcionalidades (2xx com corpo real) + 7 recusas de proteção (4xx esperado). Os 4 fixes PROVADOS VIVOS: `expiration=5/2592001` → 400; JWS PS256 **salt=32** medido no payload público (era 222); `calendario.expiracao=900` fluindo; CobV com janela certa. Webhooks de negócio (`pix.charge.created`, `pix.key.registered`) entregues a receptor externo (webhook.site) — egress do HML OK.

Harness da campanha em `<scratchpad>/campanha/` (harness.py, validate_all.py, gen_pdf.py, ledger.jsonl). **Relatório do cliente:** `~/Desktop/Monetarie-Validacao-Partner-API-2026-07-17.pdf` (17 páginas, evidências de chamada+retorno por item, recusas com erro real, seção 6/7 de responsabilidade do cliente e modelo de envio de evidências). REGRA aprendida do dono: relatório do cliente NÃO expõe defeito interno; 4xx só aparece como "recusa de proteção" (teste negativo), NUNCA como sucesso; acentuação pt-br 100% na prosa (literais da API — `expiracao`/`criacao` — ficam como estão); evidência = chamada + retorno + erro.

## 4. Defeito achado na campanha e reparado + blindado

`list_keys` da Partner API dava 422 "Internal error": 1 chave DUPLICADA gravada com `key_type` minúsculo (`email` vs `EMAIL`, mesma chave `prova-interno@monetarie.com.br` conta 119339-2, criada 15/07 03:17 com 31s de diferença) derrubava a listagem inteira (o Ecto.Enum `[..., email: "EMAIL"]` não carrega "email"). **Reparo de dado:** removida a duplicata lowercase em HML (`DELETE ... WHERE key_type = 'email'`, DELETED=1); PROD estava limpo (0 lowercase). list_keys voltou 200. **Blindagem estrutural (commit `ab49616d`, migration `20260717150000`):** CHECK `key_type = UPPER(key_type)` NOT VALID em `monetarie_dict.keys` — recusa qualquer escrita minúscula, seja changeset ou SQL cru. TDD (dict_service 405/0, shared 1754/0). **Migration NOVA: aplicar via rpc HML+PROD no próximo deploy do pix-api.** Memória [[monetarie-partner-key-type-casing-0717]].

## 5. Itens de melhoria PENDENTES (para as próximas sessões)

### 5a. Deploy do fix do key_type (migration 20260717150000)
Ainda NÃO deployado. É só cabine (pix-api) + rpc migrate. Não urgente (o código já normaliza; é preventivo). Quando deployar: `bin/monetarie_pix rpc "Shared.Release.migrate()"` HML+PROD.

### 5b. P0/P1 das TELAS ib-front + merchant-front (mapeamento `docs/reports/2026-07-17-mapeamento-ibfront-merchantfront-vs-aviv.md`)
O money-path das telas está vivo, MAS:
- **P0**: QR das telas (IB e merchant) é STUB com EMV inválido sem CRC (`core/backend/.../v2/pix_controller.ex:702`, `# TODO`); o motor real da cabine só está ligado na Partner API. Ligar o gateway real (`monetarie.pix.qrcode.static|dynamic`) nas telas.
- **P0**: copia-e-cola do IB chama `pix.payQrCode` que NÃO existe em `usePix.ts` (backend `pix_controller.ex:753` pronto).
- **P1**: TED agendada só no v1 (o v2 das telas debita imediato e ignora `scheduledDate`); limites invisíveis ao usuário (telas estáticas, enforcement real); TEF entre contas 501; PIX Automático com trilho duplo (`in_house/adapter.ex:88-137` sem `event` → DLQ); rotas v1 `/pix/in|out` → DLQ; defesa de infração do lojista → 403; chamadas mortas do merchant.
- **P2**: saldo pending/blocked hardcoded 0; export CSV de extrato; comprovante PDF; telas mock.

### 5c. Rota de eventos de conta na Partner API
`GET /accounts/:id/events` retorna 404 (não publicada nesta versão). Publicar se for do escopo.

### 5d. Pendências gerais antigas (inalteradas)
payables/open_finance stubs (visíveis na DLQ); DLQ triagem; CI inexistente; contador COSIF (SME/LPI); frente de performance/custo.

## 6. Testes que ficam com o CLIENTE (não podem ser provados sem pagador real)
PIX enviado (`pix.payout.confirmed`), cobrança paga (`pix.charge.paid`, pede parceiro pagador), PIX/TED recebido (pede parceiro), devolução (`pix.refund.completed`), TED enviada (`ted.confirmed`), portabilidade de chave (`pix.claim.*`). Detalhado nas seções 6 e 7 do PDF. O cliente deve enviar, por teste: chamada, retorno e erro (quando houver).

## 7. Receitas de deploy e gotchas (replicáveis)
- Build arm64: `docker buildx build --platform linux/arm64 -t $REG/monetarie/<svc>:<tag> --push <ctx>` (ctx: `pix/backend`, `core/backend`).
- PROD por mesmo digest: `docker buildx imagetools create -t <prodtag> <homologtag>` + guard MATCH dos digests.
- Deploy 1 serviço: `describe-task-definition` → jq troca `.image` → `register-task-definition` → `update-service` → esperar `length(deployments)==1`.
- Backfill/rpc via ECS exec: `aws ecs execute-command --task <arn> --container <svc> --interactive --command "bin/monetarie_pix rpc \"...\""`. É LENTO (~1-2 min) e o canal SSM às vezes estoura o timeout do shell local ANTES do rpc terminar (rode em background e leia o output; o UPDATE commita mesmo que o canal caia depois).
- Túnel HML: `aws ssm start-session ... AWS-StartPortForwardingSessionToRemoteHost host=<ALB> port=80 local=18080`; cai sozinho às vezes (curl 000) — matar `session-manager-plugin` e reabrir; aguardar `until curl .../health`.
- Log groups: HML `/ecs/monetarie/homolog/{pix,core}-api`; PROD `/ecs/monetarie/prod/{pix,core}-api`.
- AWS: `AWS_PROFILE=vulcimonetarie AWS_REGION=sa-east-1`, conta `990933657879`. Backfill CobV SQL e reparo key_type: ver §2 e §4.

## 8. Memórias atualizadas (4 sistemas)
CLAUDE.md (estado canônico 2026-07-17 TARDE); MEMORY.md; Serena `monetarie/qr-partner-validacao-viva-0717` e `monetarie/money-path-hardening-0717`; claude-mem #111375, #111411. Arquivos: `monetarie-qr-partner-validacao-viva-0717`, `monetarie-partner-key-type-casing-0717`, `monetarie-item-b-outbox-cross-repo-0717`.
