Skip to content

About

Implementação de referência production-grade do Pix Automático + consent flow Open Finance Brasil — Spring Boot · Hexagonal · Saga · Outbox · Idempotência forte · OpenTelemetry. Parte da Suíte de Referência Regulatória BR.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Repository files navigation

Suíte Regulatória BR

Pix Automático — Reference Implementation

Implementação de referência production-grade para Pix Automático + Open Finance Brasil

Backend Spring Boot 4.0 + Java 21 · Frontend React 18 + TypeScript + Tailwind · Empacotados como artefato único

Hexagonal · Saga · Outbox · Idempotência forte · OpenTelemetry

CI CodeQL Release License: MIT Java 21 Spring Boot 4.0 Conventional Commits PRs welcome


TL;DR  ·  Esta é a implementação que você procuraria se sua fintech precisasse integrar com o Pix Automático amanhã e não pudesse errar. Hexagonal, com idempotência forte, outbox transacional, saga orquestrada, observabilidade end-to-end, painel operacional React e testes que cobrem do agregado até o webhook. Roda 100% local com make up && make run.

Sumário


Por que existe

O Pix Automático (lançado em out/2025) e a Fase 4 do Open Finance Brasil tornaram obrigatória a adequação de sistemas de cobrança recorrente, assinaturas, utilities e fintechs a um conjunto de contratos técnicos não triviais definidos pelo Banco Central. Integrações mal-feitas resultam em cobranças duplicadas, perda de conciliação, multas regulatórias e churn.

Este repositório demonstra, em código real e executável, os padrões que uma integração correta, auditável e resiliente exige — sem atalhos.

Capacidades

Categoria O que está implementado
Domínio Agregados Consent, Subscription, Charge com domain events e invariantes do BCB
Idempotência Idempotency-Key (UUIDv4) + Redis + unique constraint em DB — defesa em profundidade
Consistência Outbox Pattern: transação local atômica + publisher Kafka assíncrono
Orquestração Saga orquestrada: autorizar → agendar → iniciar → conciliar com compensação
Resiliência Resilience4j: circuit breaker, retry, rate limiter, bulkhead — por endpoint
Segurança Spring Security + OAuth2 Client Credentials simulando mTLS / FAPI para BCB
Observabilidade OpenTelemetry traces + Prometheus metrics + Loki logs estruturados, correlacionados por traceId
Testes Unitário (domínio puro), integração (Testcontainers), contrato (Pact), arquitetura (ArchUnit)
Migrations Flyway versionado
Contratos OpenAPI 3 autogerado (SpringDoc)
Decisões 5 ADRs documentando cada escolha arquitetural não trivial
Painel operacional SPA React + TypeScript + shadcn/ui — Dashboard, CRUD de Consents, agendamento de Charges, timeline de saga

Painel operacional (frontend)

SPA em React 18 + TypeScript empacotada dentro do mesmo JAR do backend — make run sobe API + UI no mesmo :8080. Não tem deploy separado, não tem CORS, não tem dois contextos pra debugar.

Página O que mostra
Dashboard KPIs (consents/subscriptions/charges totais, taxa de sucesso), feed das últimas 5 cobranças, indicadores de saúde da plataforma
Consents Tabela paginada, criar consent, autorizar, revogar (com motivo), drill-down no detalhe
Subscriptions Tabela vinculada a consents autorizados, criar subscription
Charges Tabela com auto-refresh a cada 5s (acompanha saga ao vivo), agendar cobrança
Charge detail Timeline visual SCHEDULED → INITIATED → SETTLED/FAILED com erro do BCB se houver
Webhooks Documentação do contrato POST /webhooks/bcb/charge-status + atalhos pra Kafka UI / Grafana

Stack: Vite (build), React 18 + TypeScript, Tailwind CSS + shadcn/ui (Radix primitives), TanStack Query (cache + refetch), React Router.

Comandos:

make web-dev    # Vite dev server em :5173 com proxy pra :8080 (hot reload)
make web-build  # builda só o frontend pra src/main/resources/static/
make run        # build full + roda app (API + UI bundled em :8080)
make run-fast   # roda só backend, pula build do frontend

Arquitetura

Arquitetura hexagonal (Ports & Adapters). O domínio é puro: nem Spring, nem Jakarta, nem JPA. ArchUnit valida as fronteiras no CI.

flowchart LR
    subgraph In[" "]
      direction TB
      WEB["adapter.web<br/>REST controllers"]
      WHK["adapter.web<br/>BCB webhooks"]
    end

    subgraph App["application"]
      direction TB
      UC["Use cases"]
      SAGA["Charge Saga"]
    end

    subgraph Dom["domain (puro)"]
      direction TB
      AGG["Aggregates<br/>Consent · Subscription · Charge"]
      EVT["Domain Events"]
    end

    subgraph Out["infrastructure"]
      direction TB
      DB[("PostgreSQL<br/>JPA / Flyway")]
      RDS[("Redis<br/>Idempotency")]
      KFK[["Kafka<br/>Outbox publisher"]]
      BCB(["BCB API<br/>OAuth2 / FAPI"])
    end

    WEB --> UC
    WHK --> UC
    UC --> AGG
    UC --> SAGA
    SAGA --> AGG
    AGG -.eventos.-> EVT
    UC --> DB
    UC --> RDS
    EVT --> KFK
    SAGA --> BCB

    classDef domain fill:#fff5d6,stroke:#b08800,color:#000
    classDef app fill:#dff5e1,stroke:#2e7d32,color:#000
    classDef adapter fill:#e3f0ff,stroke:#1565c0,color:#000
    classDef infra fill:#fde2e2,stroke:#b71c1c,color:#000
    class AGG,EVT domain
    class UC,SAGA app
    class WEB,WHK adapter
    class DB,RDS,KFK,BCB infra
Loading

Diagramas C4 detalhados em docs/architecture/.

Stack

Camada Tecnologia
Runtime Java 21 (virtual threads)
App framework Spring Boot 4.0 + Spring Web MVC
Persistência PostgreSQL 16 + Spring Data JPA + Flyway
Cache / Idempotência Redis 7
Mensageria Apache Kafka 3.8 (KRaft, sem ZooKeeper)
Resiliência Resilience4j 2.4
Tracing OpenTelemetry → Tempo
Métricas Micrometer → Prometheus → Grafana
Logs Logback JSON estruturado → Loki
Segurança Spring Security + OAuth2 client/resource server
API docs SpringDoc OpenAPI 3
Testes JUnit 5 · Testcontainers · Pact · ArchUnit · JaCoCo
Build Maven 3.9 (wrapper) + frontend-maven-plugin (npm via Maven)
Container Buildpacks (Spring Boot OCI image)
Frontend Vite + React 18 + TypeScript + Tailwind + shadcn/ui + TanStack Query
CI/CD GitHub Actions · CodeQL · Semgrep · Trivy · Dependabot

Quickstart

Pré-requisitos: JDK 21, Docker Desktop, ~4 GB de RAM livre. Node 22 só é necessário se for desenvolver o frontend separado (make web-dev); o build pelo Maven baixa o Node automaticamente.

# 1. Clonar
git clone https://github.com/Paulo-Marcos-Lucio/pix-automatico-reference.git
cd pix-automatico-reference

# 2. Sobe Postgres + Redis + Kafka + stack de observabilidade
make up

# 3. Roda a app (perfil local) — builda backend + frontend e empacota tudo
make run

# 4. Abre o painel
open http://localhost:8080  # ou xdg-open / start, conforme OS

Acessos:

Serviço URL Notas
Painel operacional http://localhost:8080 SPA bundled — Dashboard, CRUD de consents/charges, timeline da saga
API REST http://localhost:8080/v1/* mesma origem do painel
OpenAPI UI http://localhost:8080/swagger-ui.html contratos vivos
Actuator http://localhost:8080/actuator health, metrics, info
Grafana http://localhost:3000 login anônimo (admin)
Prometheus http://localhost:9090
Tempo (traces) http://localhost:3200 acessar via Grafana
Kafka UI http://localhost:8089

Targets úteis do Makefile:

make up        # docker-compose up -d
make down      # derruba (preserva volumes)
make test      # mvn test (unit)
make it        # mvn verify (unit + integration via Testcontainers)
make image     # build OCI image via Buildpacks
make clean     # mvn clean + docker volumes prune

Fluxo Pix Automático

Resumido — detalhe completo em docs/flows/charge-saga.md.

sequenceDiagram
    autonumber
    actor C as Cliente (devedor)
    participant App as Sua plataforma<br/>(este repo)
    participant DB as Postgres
    participant K as Kafka
    participant BC as BCB / OF API

    C->>App: autoriza recorrência
    App->>BC: POST /consents
    BC-->>App: consentId AUTHORIZED
    App->>DB: persist Consent + outbox event (tx)
    App-->>K: publish ConsentAuthorized (async)

    Note over App: agenda Subscription

    loop Cada ciclo de cobrança
      App->>DB: persist Charge SCHEDULED
      App->>BC: POST /charges (idempotência)
      BC-->>App: status + endToEndId
      App->>DB: update Charge INITIATED
      BC-->>App: webhook charge-status
      App->>DB: reconcile + update SETTLED
      App-->>K: publish ChargeSettled
    end
Loading

Garantias do fluxo:

  • ✅ Cobrança duplicada é matemática-impossível (idempotência em 3 camadas)
  • ✅ Falha entre update DB e publish Kafka é resolvida pelo Outbox (eventual consistency)
  • ✅ Webhook reentrante: mesmo endToEndId chegando 2× não causa double-credit
  • ✅ Saga compensa ao falhar entre etapas (rollback de side-effects parciais)

Endpoints

Método Path Descrição Idempotente
POST /v1/consents Cria Consent de recorrência ✅
GET /v1/consents/{id} Consulta Consent —
POST /v1/consents/{id}/revoke Revoga Consent ✅
POST /v1/subscriptions Cria Subscription vinculada a um Consent ✅
POST /v1/charges Agenda/inicia Charge ✅
GET /v1/charges/{id} Consulta Charge —
POST /webhooks/bcb/charge-status Recebe notificação do BCB ✅

Todos os POST exigem header Idempotency-Key: <UUIDv4>. Reuso da mesma key com payload divergente retorna 422 Unprocessable Entity.

Exemplos prontos em requests.http (compatível com IntelliJ HTTP Client e VS Code REST Client).

Estrutura do projeto

pix-automatico-reference/
├── src/main/java/dev/pmlsp/pixauto/
│   ├── domain/                 # puro: entidades, agregados, eventos, ports
│   │   ├── model/              # Consent, Subscription, Charge, value objects
│   │   ├── port/in/            # use case interfaces
│   │   ├── port/out/           # repositories, gateways, stores
│   │   └── exception/
│   ├── application/            # casos de uso, saga, services
│   ├── infrastructure/         # adapters de saída (JPA, Kafka, Redis, BCB HTTP)
│   │   ├── persistence/
│   │   ├── messaging/
│   │   ├── cache/
│   │   └── config/             # WebMvcConfig (SPA fallback), Security, Jackson...
│   └── adapter/web/            # controllers, DTOs, exception handlers
├── src/main/resources/
│   ├── application.yml
│   ├── db/migration/           # Flyway
│   └── static/                 # gerado pelo build do frontend (gitignored)
├── src/test/java/.../
│   ├── domain/                 # testes unitários puros
│   ├── application/            # casos de uso com ports mockados
│   ├── architecture/           # ArchUnit
│   └── integration/            # Testcontainers (Postgres + Redis + Kafka)
├── web/                        # frontend React + TS + Tailwind
│   ├── src/
│   │   ├── components/         # ui/* (shadcn primitives), layout/*
│   │   ├── pages/              # Dashboard, Consents, Subscriptions, Charges, Webhooks
│   │   ├── hooks/              # TanStack Query bindings
│   │   └── lib/                # api client, types, query client, utils
│   ├── package.json
│   └── vite.config.ts          # outDir aponta pra ../src/main/resources/static
├── config/                     # configs do otel-collector, prometheus, grafana, etc
├── docs/
│   ├── adr/                    # decisões arquiteturais
│   ├── architecture/           # C4 (context, container, component)
│   └── flows/                  # diagramas de fluxos críticos
├── docker-compose.yml          # stack completa de apoio
├── Makefile                    # DX: up/down/test/it/run/image/web-dev/web-build
└── pom.xml                     # frontend-maven-plugin builda o web/ na fase generate-resources

Estratégia de testes

Tipo Onde Ferramentas O que valida
Unitário domain/, application/ JUnit 5 + AssertJ Regras de negócio do BCB, invariantes de agregados
Integração integration/ Testcontainers (Postgres + Redis + Kafka) Idempotência real, outbox, persistence, saga end-to-end
Arquitetura architecture/ ArchUnit Camadas hexagonais não vazam (domínio sem Spring etc.)
Contrato pact/ Pact JVM Compatibilidade do contrato consumido do BCB
Cobertura — JaCoCo Reporta no PR via Codecov

Regra de ouro: integração nunca usa mock para banco/Kafka/Redis — sempre Testcontainers. Mock em testes de I/O é dívida técnica disfarçada de teste rápido.

Observabilidade

Tudo correlacionado por traceId propagado via W3C Trace Context.

flowchart LR
    APP[App Spring Boot]
    OTEL[OTel Collector]
    PROM[(Prometheus)]
    TEMPO[(Tempo)]
    LOKI[(Loki)]
    GRAF[Grafana]

    APP -- OTLP traces --> OTEL
    APP -- /actuator/prometheus --> PROM
    APP -- JSON logs --> LOKI
    OTEL --> TEMPO
    PROM --> GRAF
    TEMPO --> GRAF
    LOKI --> GRAF

    classDef store fill:#f0f4ff,stroke:#3949ab
    class PROM,TEMPO,LOKI store
Loading

Cada request HTTP, evento Kafka, query JPA e chamada externa gera um span com tags consistentes (http.method, messaging.kafka.topic, db.statement). Métricas expostas via Micrometer (latência p50/p95/p99, taxa de erro, circuit breaker state). Logs JSON com traceId, spanId, idempotencyKey e consentId — joinable com traces no Grafana.

ADRs

Toda decisão arquitetural não trivial é documentada como ADR (Architecture Decision Record).

# Decisão
0001 Arquitetura hexagonal (Ports & Adapters)
0002 Estratégia de idempotência em 3 camadas
0003 Outbox Pattern para consistência eventual
0004 Saga orquestrada vs coreografada
0005 Observabilidade e correlação por traceId

FAPI 2.0 + DPoP (RFC 9449) — v0.2.0

A v0.2.0 ativa um perfil fapi que protege os endpoints /v1/consents/**, /v1/subscriptions/** e /v1/charges/** com DPoP sender-constrained access tokens — o padrão que o profile FAPI 2.0 do Open Finance Brasil herdará pros fluxos de Pix Automático recorrente.

SPRING_PROFILES_ACTIVE=local,fapi make run

Wire-protocol esperado pelo receiver (PISP):

POST /mock-auth/token                      # auth server in-process
DPoP: <proof JWT assinado pela chave do receiver>
X-Client-Id: demo-receiver
→ { "access_token": "...", "token_type": "DPoP", "expires_in": 600, "scope": "recurring-payments" }

POST /v1/consents
Authorization: DPoP <access_token>         # carrega cnf.jkt = thumbprint da chave do receiver
DPoP: <proof JWT fresco pra esta requisição>
Idempotency-Key: <uuid v4>
{ ...payload do consent... }
→ 201 Created (ou 4xx por business; 401 só se DPoP falha)

11 testes cobrem o validador isoladamente (DPoPValidatorTest × 8) e o flow E2E ponta-a-ponta (FapiE2EIT × 3 — token issuance, request sem headers retorna 401 + WWW-Authenticate: DPoP, replay attack com chave diferente retorna 401 invalid_token).

A chain FAPI declara @Order(0) e securityMatcher apenas sobre os 3 paths OF — paths fora (actuator, webhooks BCB, frontend SPA) seguem fluindo pela chain permissiva existente. Roubo de token + replay de outra máquina = 401 garantido pelo cnf.jkt mismatch.

Test Coverage & API Docs

Categoria Tests
Unit (incl. ArchUnit + 8 DPoPValidatorTest) 38
Integration (Testcontainers + 3 FapiE2EIT) 10
Total 48

JaCoCo coverage report gerado em target/site/jacoco/index.html após ./mvnw verify.

API Documentation (live com a app rodando em http://localhost:8080):

Roadmap

  • v0.2.0 — FAPI 2.0 + DPoP RFC 9449 sender-constrained tokens · mock auth in-process · 11 testes (8 unit + 3 IT)
  • v0.3.0 — private_key_jwt (RFC 7523) + DCR (Dynamic Client Registration) + PAR (RFC 9126)
  • Suporte a DICT (consulta de chave Pix antes de cobrar)
  • Conciliação batch com extrato BCB diário
  • Multi-tenant via tenantId propagado por toda a stack
  • Chaos testing com Toxiproxy nos Testcontainers
  • Helm chart para deploy em Kubernetes
  • Dashboards Grafana versionados como código (Jsonnet)
  • Performance benchmark (p99 < 200ms para POST /charges)
  • Migração para Java 25 (plano em .github/java-upgrade/)

Veja issues abertas para o detalhe.

Como contribuir

PRs muito bem-vindas. Leia CONTRIBUTING.md — em resumo:

  • Conventional Commits
  • Testes obrigatórios para mudanças funcionais
  • ADR para decisões não triviais
  • ArchUnit não pode regredir

Segurança

Para reportar vulnerabilidade, não abra issue pública. Use Security Advisories. Detalhes em SECURITY.md.

Licença

MIT © 2026 Paulo SP.

Autor

Construído por Paulo SP — @Paulo-Marcos-Lucio no GitHub, LinkedIn — como referência pública para consultoria em integrações regulatórias (Pix Automático, Open Finance, Open Insurance).

Sua empresa precisa de revisão de arquitetura ou implementação de integração com Pix Automático ou Open Finance? Me chama no LinkedIn ou abre uma issue de contato.


Se este repositório te ajudou, deixa uma ⭐ — ajuda outros engenheiros a encontrarem.

About

Implementação de referência production-grade do Pix Automático + consent flow Open Finance Brasil — Spring Boot · Hexagonal · Saga · Outbox · Idempotência forte · OpenTelemetry. Parte da Suíte de Referência Regulatória BR.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages