# Dossie de paridade profundo: pain.001 / pain.002 (lote / batch de pagamentos)

Auditoria READ-ONLY, zero inferencia. Cada afirmacao carrega prova (arquivo:linha ou tabela.coluna). Onde nao houve prova suficiente, o veredito e INCONCLUSIVO.

Data: 2026-07-23. Escopo: mensagens ISO20022 pain.001 (CustomerCreditTransferInitiation) e pain.002 (CustomerPaymentStatusReport), e o conceito de "lote / batch de pagamentos" no legado (.NET LegadoPIX) vs a nossa cabine PIX (Elixir Monetarie).

---

## 0. Resumo executivo (a tese central)

1. **pain.001 e pain.002 NAO existem em NENHUM dos dois sistemas como mensagem SPI.** O legado nunca implementou pain.001/002; a familia pain usada por ele e apenas pain.009 / 011 / 012 / 013 / 014 (PIX Automatico / recorrencia). Nosso codigo teve pain.001 e pain.002 como FORMULARIOS FICTICIOS e os REMOVEU deliberadamente na limpeza "V2-01b" por nao constarem nos XSDs raw v5.12 do SPI oficial. Logo, para a mensagem em si, nao ha divergencia: os dois lados corretamente NAO tem pain.001/002.

2. **O legado TEM capacidade de "lote / batch de pagamento", mas por mecanismos PROPRIETARIOS, nao por pain.001.** Sao dois mecanismos distintos:
   - `addLotePagto` / `devLotePagto`: lote inline via API JSON (`LotePaymentAPIDTO` com uma lista de `PaymentAPIDTO`), e a devolucao de lote.
   - `ImportaArquivo`: upload de ARQUIVO (ZIP contendo um TXT delimitado por `;` com registros 01/02/03/99), processado por worker, virando pagamentos PIX individuais. Ha dado vivo (179 arquivos em `TB_ARQUIVO_PAGTO`).

3. **Nosso lado NAO tem nenhum dos dois mecanismos de lote.** Restaram apenas artefatos MORTOS: as tabelas `monetarie_spi.batches` e `monetarie_spi.batch_items` (sem schema Ecto, sem escrita) e uma rota-zumbi `POST/GET /api/v1/batch` no gateway que faz proxy para um endpoint que foi REMOVIDO do spi_service (retorna 404).

Portanto o titulo "pain.001/002 (lote)" resolve-se assim: a mensagem pain.001/002 e um nao-assunto (ambos ausentes, e corretamente); o assunto real e o LOTE de pagamentos, que e um recurso presente e vivo no legado e AUSENTE no nosso.

---

## 1. LEGADO: o que faz com pain.001 / pain.002 (prova de que NAO usa)

### 1.1 Familia pain usada pelo legado = recorrencia (PIX Automatico), nunca pain.001/002

O dicionario de conversores de envio de mensagem lista TODAS as mensagens pain suportadas:

- `.scratch/legado-pix-decompiled/SPI.Core.Application.decompiled.cs:8511-8530` mapeia `pain.009.spi.1.0`, `pain.009.spi.1.1`, `pain.011.spi.1.0/1.2/1.3`, e adiante `pain.012`, `pain.013.spi.2.0/2.1/2.2`, `pain.014`. Nao ha nenhuma entrada pain.001 nem pain.002.

Varredura exaustiva de tokens `pain.NNN` em todo o backend decompilado retorna apenas:
`PAIN.009, PAIN.011, PAIN.012, PAIN.013, PAIN.014` (e as versoes `pain.0XX.spi.*`). Zero ocorrencias de pain.001 ou pain.002 (grep `pain\.[0-9]{3}` em `*.decompiled.cs`).

Varredura nos scripts SQL do legado (`LegadoPIX/Pix/**`): `grep -rioE "pain\.?0[0-9]{2}"` retorna apenas `PAIN.009/011/012/013` e variantes. Zero pain.001/002.

Varredura no frontend Angular do legado (`LegadoPIX/Pix/SPI/angular/src`): as ocorrencias de "pain" caem em telas de contabilizacao/lancamentos e no modulo multiliquidacao; nenhuma referencia a pain.001/002.

**Veredito 1.1: PROVADO que o legado NAO implementa pain.001 nem pain.002 em nenhuma camada (codigo, DB scripts, frontend).**

### 1.2 As mensagens pain do legado vivem no modulo Multiliquidacao (recorrencia/agendamento)

- `.scratch/legado-pix-decompiled/Multiliquidacao.Core.Application.decompiled.cs:8494-8498`: interface com `AddRecorrencia(SPI_PAIN009 ...)`, `CancelRecorrencia(SPI_PAIN011 ...)`, `AutorizaRecorrencia(SPI_PAIN012 ...)`.
- `Multiliquidacao.Core.General.decompiled.cs:10701` (`SPI_PAIN009`), `:10769` (`SPI_PAIN011`), `:10832` (`SPI_PAIN012`): DTOs.
- `SPI.Core.Multiliquidacao.Application.decompiled.cs:323-329`: roteamento de retorno por `<MsgDefIdr>pain.012` e `<MsgDefIdr>pain.014` (`InsereRetornoPAIN012`, `InsereRetornoPAIN014`).
- `SPI.Web.Api.decompiled.cs:871` `addRecorrencia`, `:892` `cancelRecorrencia`, `:913` `autorizaRecorrencia`: endpoints REST das recorrencias.

Ou seja, no legado o nome "Multiliquidacao" (multi-liquidacao) engloba o PIX Automatico (recorrencias pain.009/011/012), o agendamento (pain.013/014) e o lote de pagamento por arquivo (secao 2). O "multi" e o fato de uma recorrencia gerar varias liquidacoes ao longo do tempo, nao um pain.001.

---

## 2. LEGADO: o "lote / batch de pagamentos" real (mecanismos proprietarios)

### 2.1 Mecanismo A: lote inline por API (`addLotePagto` / `devLotePagto`)

- `SPI.Web.Api.decompiled.cs:787` `[HttpPost("addLotePagto")]` -> `AddLoteDTO([FromBody] LotePaymentAPIDTO dtoBody)` chama `_dto.InsereComConversao(dtoBody, IdUser)`.
- `SPI.Web.Api.decompiled.cs:766` `[HttpPost("devLotePagto")]`: devolucao de lote.
- Shape do lote inline (`SPI.Core.General.decompiled.cs`, classe `LotePaymentAPIDTO`):
  ```
  int    IdSystem
  string UniqueId
  DateTime DtHrOperacao
  string IspbDebtor
  string IspbCreditor
  List<PaymentAPIDTO> Pagtos   // <= a lista de pagamentos do lote
  string IspbIF
  ```
- `SPI.Core.Application.decompiled.cs:6242`: registro do conversor `LoteDevolucaoPaymentAPIDTO` -> `ConvertToLoteDevolucaoPaymentDTO` / `ConvertToProtocoloRetornoDevolveLoteDTO` (devolucao de lote).

**Veredito 2.1: PROVADO. O legado aceita um lote de pagamentos em uma unica chamada (lista `Pagtos`), gerando N liquidacoes; e aceita devolucao de lote. Formato JSON proprietario, nao pain.001.**

### 2.2 Mecanismo B: lote por ARQUIVO (upload ZIP+TXT) -> `TB_ARQUIVO_PAGTO`

Fluxo de importacao (`Multiliquidacao.Core.Application.decompiled.cs:2619` `ImportaArquivo(IFormFile arquivoCompactado)`):

1. Le o stream; exige assinatura ZIP `{80,75,3,4}` = "PK" (linha 2634). Se nao for ZIP, erro "Arquivo nao possui assinatura de arquivo ZIP".
2. `DescompactaArquivo` (`:2672`): abre o ZIP; se tiver mais de 1 entrada, erro "O arquivo compactado contem N arquivos". So aceita 1 arquivo dentro do ZIP.
3. `LeituraArquivo` (`:2704`):
   - Rejeita BOM UTF-8 (`IsUTF8WithBOM`, bytes EF BB BF): "Arquivo nao pode possuir Byte Order Mark".
   - Parse por `TextFieldParser` delimitado por `;`.
   - Valida que todo registro tem tipo em `{"01","02","03","99"}`, senao "O arquivo possui tipos de registros nao esperados".
   - `ValidaHeader` (registro "01"): exige exatamente 8 colunas. Campos: `[1]` CPF/CNPJ (valida CPF 11 / CNPJ 14 digitos), `[2]` conta (max 9), `[3]` nome cliente (max 30), `[4]` tipo do arquivo (1 char), `[5]` data criacao `yyyy-MM-dd`, `[7]` numero sequencial (max 6 digitos).
   - **Idempotencia**: `FindArquivoPagto(NrCpfCnpj, NrTipo, NrSequencial)`; se ja existe, erro "O arquivo ja foi importado".
   - **Nao aceita retroativo**: `if (arquivoPagto.DtCriacao < DateTime.Now.Date)` -> erro "Nao e permitido importar arquivos com data de criacao anterior a data atual" (`:2640`).
   - `ValidaDetail` (registros "02"/"03") e `ValidaTrailler` (registro "99").
   - Situacao final da importacao = `NaoIniciado`.

Enum de situacao do arquivo (`Multiliquidacao.Core.General.decompiled.cs:6420` `enumSituacaoArquivoPagamento`):
`NaoIniciado=1, Validacao=2, Processamento=3, Erro=4, Concluido=5`.

Processamento assincrono por worker (`Multiliquidacao.Core.Worker.ImportacaoArquivos.decompiled.cs`):
- `:246` `ImportaArquivo` e `:441/:695` `ProcessaArquivo(long idArquivo)`.
- `ProcessaArquivo` marca `Processamento`, faz `Parallel.ForEachAsync(arq.Detalhes, ...)` com `MaxDegreeOfParallelism = ImpArqPagtoThreadsPagamento` e chama `Paga(det)` por linha; ao fim marca `Concluido` (ou `Erro`).
- `ValidaQRCode(det, ...)` (`:497`): se o detalhe tem `UrlEMV`, consulta o QR (`ConsultarUrlQRCode`), exige status `ATIVA/ATIVO` e valida vencimento (`dataDeVencimento + validadeAposVencimento >= hoje`).

Estrutura do detalhe (`crk_multtiliquidacao.dbo.TB_ARQUIVO_PAGTO_DETALHE`): `TP_PRIORIDADE`, `VL_PAGTO`, `VL_DOCUMENTO`, `TP_FINALIDADE`, `DT_PAGTO`, dados do pagador (`NR_SPBPAGADOR/NR_AGENCIAPAGADOR/TP_CONTAPAGADOR/NR_CONTAPAGADOR/NM_PESSOAPAGADOR/TP_PESSOAPAGADOR/NR_CPFCNPJPESSOAPAGADOR`), dados do recebedor (`TX_CHAVERECEBEDOR/NR_SPBRECEBEDOR/NR_AGENCIARECEBEDOR/TP_CONTARECEBEDOR/NR_CONTARECEBEDOR/NM_PESSOARECEBEDOR/TP_PESSOARECEBEDOR/NR_CPFCNPJPESSOARECEBEDOR`), `ID_CONCILIACAORECEBEDOR`, `TP_QRCODE`, `JS_TIPOVALOR`, `ID_SITUACAO`, `ID_OPERACAO`, `ID_SITUACAO_OPERACAO`, `TX_URL_EMV`, `TX_DESCRICAO`. Ou seja, cada linha suporta pagamento por CHAVE, por AGENCIA/CONTA (SPB) ou por QR CODE.

Estrutura do cabecalho do lote (`crk_multtiliquidacao.dbo.TB_ARQUIVO_PAGTO`): `ID_ARQUIVO, DT_INCLUSAO, NM_ARQUIVO, NR_CPFCNPJ, NR_CONTA, NM_CLIENTE, NR_TIPO, DT_CRIACAO, HR_CRIACAO, NR_SEQUENCIAL, ID_SITUACAO, NR_PAGAMENTOS_QRCODE, VL_PAGAMENTOS_QRCODE, NR_PAGAMENTOS, VL_PAGAMENTOS`.

**Dado vivo (prova de uso real):**
- `SELECT COUNT(*) FROM crk_multtiliquidacao.dbo.TB_ARQUIVO_PAGTO` = **179**.
- `SELECT COUNT(*) FROM crk_multtiliquidacao.dbo.TB_OPERACAO` = 492076.
- Nomes de arquivo reais: `PIX 3.txt`, `PIX 6.txt`, `PIX 7.txt`. Tipos (`NR_TIPO`): `1` (123x), `2` (7x), `teste` (27x), NULL (22x).

**Veredito 2.2: PROVADO. O legado tem um recurso de lote de pagamento por arquivo (ZIP+TXT delimitado por `;`, registros 01/02/03/99), com validacoes ricas, worker de processamento paralelo, ciclo de vida de situacao e dado de producao (179 arquivos). Formato de arquivo texto proprietario, NAO pain.001.**

### 2.3 Frontend do legado

- Existe o modulo Angular `LegadoPIX/Pix/SPI/angular/src/app/pages/multiliquidacao/` (multiliquidacao.module.ts, monitoracao, parametrizacao, operacoes/incluir). E a UI operacional do modulo (recorrencia + lote + agendamento).

---

## 3. NOSSO codigo (Elixir Monetarie): estado real

### 3.1 pain.001 / pain.002 foram REMOVIDOS deliberadamente (correto)

- `apps/spi_service/lib/spi_service_web/router.ex:129-130`: "V2-01b cleanup: Batch payment endpoints (pain.001/002) deletados, ficticias nao em XSDs raw v5.12 SPI oficial. BatchController removido." Ou seja, o BatchController e as rotas `/api/v1/batch` do spi_service foram REMOVIDOS.
- `apps/settlement_service/lib/settlement_service_web/message_form_catalog.ex:352`: "V2-01b cleanup: pain.001 + pain.002 ficticias removidas." (o catalogo de formularios de mensagem nao expoe mais pain.001/002).
- `apps/settlement_service/lib/settlement_service_web/controllers/message_controller.ex:1105-1106`: "V2-01b cleanup: dispatch_message para pacs.028 + pain.001 deletados, ficticias nao em XSDs raw v5.12 SPI oficial."

**Veredito 3.1: PROVADO. Nosso lado nao tem pain.001/002 (removidos de proposito). Isso ESTA ALINHADO com o legado (que tambem nunca teve). Nao ha divergencia de mensagem.**

### 3.2 Recorrencias pain.009/011/012 = COBERTAS

- `apps/spi_service/lib/spi_service/recurrences.ex:1-9`: contexto "PIX Automatico recurring payments" per Res. BCB 505/2025, com pain.009 (Mandate initiation), pain.011 (amendment), pain.012 (cancellation); "Each mandate generates periodic pacs.008 payments via the scheduler".
- `apps/spi_service/lib/spi_service_web/controllers/recurrence_controller.ex`: controller existe.
- `apps/spi_service/lib/spi_service_web/router.ex:159` scope `/recurrence`: `POST/GET/PUT`, etc.
- Gateway: `apps/settlement_service/.../spi_proxy_controller.ex` expoe `create_recurrence`, `list_recurrences`, `get_recurrence`, `pause/resume/cancel_recurrence` (RBAC em `:25-53`).
- Catalogo de formularios pain.009/011/012/013/014 alinhado aos XSDs: `message_form_catalog.ex:353-475`.

**Veredito 3.2: COBERTO. As recorrencias (equivalentes ao `AddRecorrencia`/`CancelRecorrencia`/`AutorizaRecorrencia` do legado) existem no nosso lado.**

### 3.3 Lote de pagamento (addLotePagto / arquivo) = AUSENTE + artefatos MORTOS

**Rota-zumbi no gateway:**
- `apps/settlement_service/lib/settlement_service_web/router.ex:580-583`: scope `/api/v1` ainda registra `POST /batch` (`:create_batch`), `GET /batch` (`:list_batches`), `GET /batch/:id` (`:get_batch`).
- `apps/settlement_service/.../spi_proxy_controller.ex:641/656/675`: os tres handlers fazem `InternalClient.call_spi(:post|:get, "/api/v1/batch"...)` para o spi_service.
- Mas o spi_service REMOVEU esse endpoint (secao 3.1). Logo a chamada ao gateway `/api/v1/batch` cai em 404 do backend (`{:error, %{status: 404}}` -> `error_response(conn, 404, body)`). A rota esta protegida por `RequirePermission SPI:create/view` (`:25-45`), entao e um endpoint autenticado que sempre falha. Rota-zumbi.

**Tabelas mortas:**
- `apps/shared/priv/repo/sql/mon_pix_schema_clean.sql:5119` cria `monetarie_spi.batch_items` (colunas de lote: `local_instrument`, `priority` default `PAGPRI`, `purpose` default `IPAY`, `debtor_*`, `creditor_*`, `tx_id`, `error_code`, `reason_code`, `validated_at/sent_at/completed_at`) e `:5162` cria `monetarie_spi.batches` (`batch_number`, `ispb`, `status` default `CREATED`, `total_items/processed_items/successful_items/failed_items`, `total_amount/successful_amount/failed_amount`, `institution_id/branch_id/system_id/user_id`).
- Essas tabelas NAO tem schema Ecto e NAO tem escrita: `grep 'schema "batches"|schema "batch_items"'` = 0 resultados; `grep 'from(...batches|Repo.insert...Batch'` = 0 (excluindo `settlement_batch`/`outbound_batch`).
- A unica referencia viva a elas e a migration blanket `apps/shared/priv/repo/migrations/20260612400000_add_cnpj_alfa_checks.exs:104-106`, que apenas adiciona CHECK de ISPB em varias tabelas (varredura generica), nao indica uso.

**Nao ha equivalente ao lote inline nem ao arquivo:**
- `grep -liE "arquivo_pagamento|payment_file|batch_file|lote.?pag|ImportaArquivo|addLotePagto|LotePayment"` em `apps/` (fora de testes e `settlement_batch`) = 0 arquivos de codigo (so o SQL de schema morto e a migration de checks).
- Frontend Vue (`pix/frontend/admin/src`): existe `RecurrenceListView.vue` (rota `recurrences`, `router/index.ts:326`), mas NAO existe tela de upload/importacao de lote nem de lote inline. `FileMonitorView.vue` e monitor de arquivos ICOM/inbound, nao upload de lote de pagamento.

**Atencao para nao confundir (nao e o mesmo assunto):**
- `apps/shared/lib/shared/schemas/settlement_batch.ex` (`settlement_batches`) e NETTING multilateral (janelas de liquidacao), recurso VIVO e SEM relacao com lote de pagamento pain.001.
- `apps/spi_service/lib/spi_service/workers/outbound_batch.ex` agrupa mensagens de saida por chave/latencia (otimizacao de envio), tambem sem relacao com lote de pagamento do cliente.

**Veredito 3.3: PROVADO. O recurso de lote de pagamento (inline `addLotePagto` e arquivo `ImportaArquivo`) esta AUSENTE no nosso lado. O que restou sao artefatos mortos: rota-zumbi `/api/v1/batch` (404) e tabelas `batches`/`batch_items` sem uso.**

### 3.4 Observacao lateral (fora do escopo de lote, registrada por rigor)

- `apps/settlement_service/.../message_controller.ex:1120` mapeia `dispatch_message("pain.013", ...)` -> `Shared.Bacen.SpiClient.confirm_return(xml)`. pain.013 no SPI e solicitacao/confirmacao de recorrencia agendada, nao "confirm_return"; o mapeamento parece suspeito. Veredito: INCONCLUSIVO (nao foi verificada a implementacao de `confirm_return`; pode ser reuso proposital de rota interna). Fora do escopo de lote; registrado como pista para auditoria de recorrencia/agendamento.

---

## 4. Matriz de paridade (lote / pain)

| Item | Legado | Nosso | Veredito |
|---|---|---|---|
| Mensagem pain.001 (initiation) | ausente | removido (V2-01b) | COBERTO (ambos ausentes, correto) |
| Mensagem pain.002 (status report) | ausente | removido (V2-01b) | COBERTO (ambos ausentes, correto) |
| Lote inline `addLotePagto` (`LotePaymentAPIDTO.Pagtos`) | presente | ausente (rota-zumbi 404) | AUSENTE |
| Devolucao de lote `devLotePagto` | presente | ausente | AUSENTE |
| Lote por arquivo (ZIP+TXT 01/02/03/99, worker, 179 arquivos vivos) | presente | ausente | AUSENTE |
| Tabelas de lote (`batches`/`batch_items`) | `TB_ARQUIVO_PAGTO(_DETALHE)` vivas | schema morto, sem uso | DIVERGENCIA (tech-debt) |
| Recorrencia pain.009/011/012 (PIX Automatico) | presente (Multiliquidacao) | presente (RecurrenceController) | COBERTO |
| Agendamento pain.013/014 | presente (InsereRetornoPAIN014) | parcial (form no catalogo; dispatch pain.013 suspeito) | PARCIAL / INCONCLUSIVO |

---

## 5. Se for decidido implementar o lote (regras do legado a replicar)

Registrado como referencia (nao e recomendacao de produto, e o inventario do que o legado faz):

Validacoes do arquivo: assinatura ZIP `PK`; exatamente 1 arquivo dentro do ZIP; sem BOM; delimitador `;`; apenas registros 01/02/03/99; header com 8 colunas; CPF (11) / CNPJ (14) validos; conta max 9; nome cliente max 30; data criacao `yyyy-MM-dd` e NAO retroativa; sequencial max 6 digitos; idempotencia por (CPF/CNPJ + tipo + sequencial). Detalhe: pagamento por chave, por agencia/conta (SPB) ou por QR (valida QR ATIVA e vencimento). Processamento: paralelo por linha (`MaxDegreeOfParallelism` configuravel), status `NaoIniciado -> Processamento -> Concluido/Erro`, tabela de erros por arquivo e por detalhe. Lote inline: uma chamada com lista `Pagtos`, mais devolucao de lote.

---

## 6. Provas-chave (indice rapido)

- Legado sem pain.001/002: `SPI.Core.Application.decompiled.cs:8511-8530` (so pain.009/011/012/013/014); scripts SQL e Angular sem pain.001/002.
- Lote inline: `SPI.Web.Api.decompiled.cs:787` (`addLotePagto`), `:766` (`devLotePagto`); `LotePaymentAPIDTO` com `List<PaymentAPIDTO> Pagtos` (`SPI.Core.General.decompiled.cs`).
- Lote arquivo: `Multiliquidacao.Core.Application.decompiled.cs:2619` (`ImportaArquivo`), `:2704` (`LeituraArquivo`), `:2789` (`ValidaHeader`); worker `Multiliquidacao.Core.Worker.ImportacaoArquivos.decompiled.cs:441/695`; enum `Multiliquidacao.Core.General.decompiled.cs:6420`.
- Dado vivo: `TB_ARQUIVO_PAGTO` = 179 linhas; colunas de `TB_ARQUIVO_PAGTO(_DETALHE)`.
- Nosso removido: `apps/spi_service/lib/spi_service_web/router.ex:129-130`; `message_form_catalog.ex:352`; `message_controller.ex:1105`.
- Nosso zumbi/morto: `settlement_service_web/router.ex:580-583`; `spi_proxy_controller.ex:641/656/675`; `mon_pix_schema_clean.sql:5119/5162`.
- Nosso coberto: `spi_service/recurrences.ex:1-9`; `recurrence_controller.ex`; `router.ex:159`.
