Implementação de referência production-grade do Iniciador de Pagamento (PISP) do Open Finance Brasil — fluxo de consentimento, criação de pagamento Pix iniciado por terceiro, simulador in-process do banco autorizador (detentor de conta), arquitetura hexagonal validada por ArchUnit, observabilidade end-to-end. mTLS-ready (ICP-Brasil) e roadmap para FAPI Advanced + DPoP em v0.2.0.
Java 21 · Spring Boot 4.0 · Hexagonal · Resilience4j · OpenTelemetry · Grafana
O Open Finance Brasil — Fase 4 (Iniciação de Pagamento) permite que um TPP (Third Party Provider, atuando como PISP) inicie um pagamento Pix em nome do usuário, contra o banco detentor da conta — sem o usuário sair do app do iniciador. É o Pix iniciado por terceiro — fundamento de fluxos de checkout one-tap, transferência por aproximação, e recargas em apps de mobilidade/varejo.
Spec é vasta (consent flow + payment flow + FAPI + DCR + ICP-Brasil + JWS sigs) e o material open source de referência em Java é fragmentado. Este repo cobre o núcleo do fluxo com nível de produção e roadmap explícito pros componentes que ainda faltam (FAPI/DPoP/DCR em v0.2.0).
Faz parte da Suíte de Referência Regulatória BR mantida ao lado de:
pix-automatico-reference— Pix Automático + Open Finance Fase 4 com saga, outbox, painel Reactdict-client-reference— cliente DICT do BCBpix-nfc-reference— Pix por aproximação (NFC)
- Lado iniciador (PISP): criação de consent, consulta de status, criação de payment baseado em consent autorizado, consulta de payment com cadeia completa de status
- Lado detentor (simulator): endpoints HTTP que mimetizam o banco autorizador — registra consents, autoriza inline (mimics customer SCA approval), recebe pagamentos e agenda settlement assíncrono
- State machines do domínio:
- Consent:
RCVD→AUTHORISED→CONSUMED|REJECTED|REVOKED|EXPIRED - Payment Initiation:
RCVD→PDNG→ACSP→ACSC|RJCT
- Consent:
- EndToEndId ISO 20022 de 32 chars com prefixo
E+ ISPB do debtor + timestamp + UUID - Resilience4j em 2 grupos:
holder-consent(rate-limited, retry agressivo) eholder-payment(retry conservador 2 attempts pra evitar duplicar Pix) - Audit log estruturado JSON com documento sempre mascarado (
Document.masked()) - Observabilidade rica: Prometheus + Tempo + Loki, OpenTelemetry traces correlacionados
por
traceId, exemplars Tempo no histograma de latência - Hexagonal estrito + ArchUnit — domain puro (sem Spring, sem Jakarta), application só depende de domain, infrastructure implementa ports out
- mTLS ICP-Brasil-ready via Spring Boot SSL Bundle (config-driven, off por default)
- CI/CD end-to-end: 7 jobs paralelos GitHub Actions (build, unit, IT, ArchUnit, Semgrep SAST, Trivy image scan, Docker) + CodeQL + Dependency Review + Release tag-driven
git clone https://github.com/Paulo-Marcos-Lucio/open-finance-payments-reference
cd open-finance-payments-reference
make up # otel + prometheus + tempo + loki + grafana
make run-sim # app + simulador holder in-process
make load # tráfego sintético end-to-endAcesse:
- Swagger UI: http://localhost:8082/swagger-ui.html
- Grafana (admin/admin): http://localhost:3000
- Dashboard OF Payments · Operations Overview
- Dashboard OF Payments · Resilience
- Prometheus: http://localhost:9090
POST /open-banking/payments/v1/consents # cria consent
GET /open-banking/payments/v1/consents/{consentId} # status do consent
POST /open-banking/payments/v1/pix/payments # cria payment (consent precisa estar AUTHORISED)
GET /open-banking/payments/v1/pix/payments/{paymentId} # status do paymentPOST /sim/holder/consents # PISP registra consent no holder
POST /sim/holder/consents/{consentId}/authorise # autoriza inline (mimic customer SCA)
POST /sim/holder/payments # PISP envia payment; settlement assíncronoVeja requests.http pra exemplos rodáveis na IntelliJ ou via VS Code REST Client.
Camadas (validadas por ArchUnit em HexagonalArchitectureTest):
domain/— modelos, exceções, ports, state machines. Sem Spring, sem Jakartaapplication/— use cases (CreateConsent, GetConsent, AuthoriseConsent, CreatePayment, GetPayment, SettlePayment). Depende apenas dedomain/infrastructure/— implementação dos ports out (HTTP gateway, in-memory repositories, audit, simulator)adapter/web/— controllers HTTP, DTOs, exception handler, security config
| Padrão | Onde aplica | Por que importa |
|---|---|---|
| State machines no domínio | Consent, PaymentInitiation |
Transições válidas explícitas; ArchUnit + tests evitam regressão |
| Documents mascarados por default | Document.masked(), Subject.maskedDocument() |
Logs/audit nunca expõem CPF/CNPJ completo |
| Resilience por grupo de operação | holder-consent vs holder-payment |
Consent tolera retry agressivo; payment não — Pix duplicado é incidente regulatório |
| Simulator-as-controller | HolderSimulatorController |
IT end-to-end no mesmo Spring context, sem Docker, sem WireMock |
| EndToEndId ISO 20022 | EndToEndId.generate() |
32 chars exatos, prefixo E + ISPB + timestamp + UUID truncado |
| Hexagonal estrito + ArchUnit | HexagonalArchitectureTest |
Garantido em CI — não dá pra acidentalmente importar Spring no domain |
| Sender-constrained mTLS (produção) | Spring SSL Bundle | Cert ICP-Brasil obrigatório em produção; off em local/test |
Todos os parâmetros via ofpayments.* em application.yml ou env vars correspondentes.
ofpayments:
initiator:
organisation-id: 11111111-1111-1111-1111-111111111111
software-statement-id: 22222222-2222-2222-2222-222222222222
holder:
base-url: ${HOLDER_BASE_URL}
connect-timeout: 2s
read-timeout: 5s
consent:
default-validity-minutes: 5
max-validity-minutes: 1440
payment:
settle-delay-ms: 250
simulator:
failure-rate: 0.0
jitter-min-ms: 0
jitter-max-ms: 0
mtls:
enabled: true
bundle-name: ofpayments-prodA v0.2.0 adicionou sender-constrained tokens via DPoP (RFC 9449) end-to-end:
# 1) cliente PISP requisita token apresentando DPoP proof (assinado por sua chave)
POST /mock-auth/token
DPoP: <proof_jwt_assinado_pela_ec_key_do_cliente>
→ { "access_token": "...jwt.com.cnf.jkt...", "token_type": "DPoP", "expires_in": 600 }
# 2) cliente usa o token bound + um novo proof a cada request PISP
POST /open-banking/payments/v1/consents
Authorization: DPoP <access_token>
DPoP: <novo_proof_jwt>
→ filter valida: assinatura do proof, htm=POST, htu=URI, iat<60s, jti único, e
thumbprint(jwk_do_proof) == cnf.jkt do access token. Falha qualquer: 401.Ativar via profile fapi:
./mvnw spring-boot:run -Dspring-boot.run.profiles=local,simulator,fapiSem o profile, comportamento da v0.1.0 (mock auth permitAll) — preserva back-compat.
Roubo de token + replay de outra máquina = 401. Coberto por test (replayedTokenFromAnotherKeyIsRejected).
Detalhes em ADR 0006 — FAPI Advanced + DPoP, incluindo trade-offs aceitos e roadmap pra private_key_jwt/PAR/JARM/DCR/JWS.
| Categoria | Tests |
|---|---|
| Unit (incl. ArchUnit + 8 DPoPValidatorTest) | 22 |
| Integration (E2E + FAPI E2E) | 4 |
| Total | 26 |
JaCoCo coverage report gerado em target/site/jacoco/index.html após ./mvnw verify.
API Documentation (live com a app rodando em http://localhost:8082):
- Swagger UI: http://localhost:8082/swagger-ui.html
- OpenAPI 3 spec (JSON): http://localhost:8082/v3/api-docs
- Geração offline do spec:
./mvnw spring-boot:run # em outro terminal curl http://localhost:8082/v3/api-docs > docs/openapi.json
Quando rodando com profile fapi, os endpoints /open-banking/payments/** exigem Authorization: DPoP + header DPoP — Swagger UI vai precisar de um proof JWT válido pra exercitar (use o DPoPHelper de teste como template).
- v0.1.0 — Consent flow + Payment Initiation + state machines + simulator + observabilidade
- v0.2.0 — DPoP (RFC 9449) sender-constrained tokens + mock auth server in-process
- v0.3.0 —
private_key_jwtclient authentication (RFC 7523) + DCR (Dynamic Client Registration) - v0.4.0 — PAR (Pushed Authorization Requests) + JARM (Authorization Response como JWT)
- v0.5.0 — JWS detached signature em payloads (RFC 7515 + cert ICP-Brasil)
- v0.6.0 — Webhook receiver pra status callback assíncrono do holder
- v0.7.0 — Suporte a Pix Automático cross-link com
pix-automatico-reference - Persistência durável (Postgres com partitioning) — hoje é in-memory
- Idempotência forte com
Idempotency-Keyem todos os POSTs - Conformance test contra a suite oficial do Open Finance Brasil
ADRs em docs/adr/:
- 0001 — Hexagonal architecture
- 0002 — Consent state machine
- 0003 — Payment status mapping (ISO 20022)
- 0004 — Resilience strategy por grupo
- 0005 — Simulator-as-controller no Spring context
- 0006 — FAPI Advanced + DPoP
- 0005 — Simulator-as-controller no Spring context
MIT — use, modifique, distribua. Atribuição apreciada.
Paulo Marcos Lucio — Engenheiro Java pleno · Consultor em integrações regulatórias BR
LinkedIn · GitHub · pmlsp23@gmail.com
Se este repo ajudou seu time, ⭐ uma star — ajuda outros engenheiros do nicho a encontrarem.