--- name: Integração Boleto Bradesco (Cobrança QR Code) description: Status, arquitetura e bloqueio atual da integração com API Bradesco "Cobrança com QR Code" (boleto híbrido com PIX) type: project originSessionId: 30ca44ac-e884-4293-ab32-57e3f44147be --- ## Integração Boleto Bradesco (2026-04-20) ### Status: ✅ Código 100% pronto | ❌ API Bradesco rejeita (bloqueio do lado deles) ### Credenciais e dados - **Client ID**: `567821ba-4198-42e5-925c-84bb5f1de225` - **Client Secret**: `22af063b-7136-4a3e-aa0b-4272ad8dc83a` (expira 23/04/2026) - **CNPJ**: 61412207/0001-68 (base/filial/digito) - **Agência/Conta**: 1028/0062060-2 - **Carteira**: 09 (com registro) - **Razão Social**: AG ESCOLA DE MUSICA LTDA - **Certificado A1 ICP-Brasil** (SHA1: `413D0D8BC6B78F926371024DCEEE76DC832A8E48`, Serial `F0A37A3685AE897CE06E`, valido até 24/06/2026) - **Senha do .pfx**: `PIN12345` (armazenada em `boleto_config.cert_password`, mas `key.pem` extraído com `-nodes` então não tem senha efetivamente) - **nseqContrNegoc** (Número Sequencial do Contrato de Negócio): `0005346382` — confirmado pelo usuário como o **número do contrato de cobrança** (2026-04-28). Salvo em `boleto_config.nseq_contr`. - **cpssoaJuridContr** (Código PJ do Contrato): `0006011803` — informado pelo usuário em 2026-04-28, salvo em `boleto_config.cpssoa_jurid`. **Não validado por API** (mTLS continua bloqueado). ### Arquivos do certificado (em `writable/bradesco/`) - `cert.pem` (2816 bytes) — cert público, limpo, bate byte-a-byte com o que está no portal Bradesco - `key.pem` (1918 bytes, perm 600) — chave privada extraída com `openssl pkcs12 -nodes` - `ca-bundle.pem` — cadeia SSL do sistema (`/etc/ssl/certs/ca-bundle.crt`) ### Endpoints e fluxo - **Auth**: `POST https://openapi.bradesco.com.br/auth/server/v1.1/token` - Body: `assertion=&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer` - JWT claims: `aud` (= URL auth), `sub` (= clientId), `iat`, `exp` (30 dias), `jti` (microtime), `ver=1.1` - Assinatura RS256 com chave privada, **base64 regular** no header/payload, **base64url na signature** - Sem `iss`, sem `kid` (importantíssimo — padrão Bradesco é diferente) - **API**: `POST https://openapi.bradesco.com.br/boleto-hibrido/cobranca-registro/v1/gerarBoleto` - Headers: `Authorization: Bearer `, `X-Brad-Nonce`, `X-Brad-Timestamp`, `X-Brad-Algorithm: SHA256`, `X-Brad-Signature` - String assinada: `POST\n\n\n\n\n\n\nSHA256` - Signature: RSA-SHA256 com chave privada, base64url ### Arquivos do sistema - `app/Services/BradescoService.php` — OAuth + mTLS + JWT + signature - `app/Controllers/BoletoController.php` — gerar, enviarWhatsApp, anti-duplicação via UNIQUE - `app/Controllers/BoletoConfigController.php` — UI config (GET, salvar, testarAutenticacao, uploadCertificado) - `app/Models/BoletoModel.php` — getByLancamento só considera `registrado/pago` com linha_digitavel (permite retry em `erro`/pendente vazio) - `app/Models/BoletoConfigModel.php` — single-row config - `app/Views/direcao/configuracoes.php` — aba "Boleto Bradesco" com todos os campos + validação cert + testar auth - `app/Views/direcao/matricula_editar.php` — botão laranja 🧾 "Boleto" em débitos pendentes ### DB - **Migrations**: `2026-04-20-120000_CreateBoletos`, `2026-04-20-130000_CreateBoletoConfig` - **Tabela `boletos`**: UNIQUE em `lancamento_id` (anti-duplicação) - **Tabela `boleto_config`**: single row, pré-populada com valores de produção ### Bloqueio anterior (até 2026-05-05): mTLS 401 — CAUSA IDENTIFICADA - Autenticação (`/auth/server/v1.1/token`) funciona, retorna access_token válido - API (`/boleto-hibrido/cobranca-registro/v1/gerarBoleto`) retorna `HTTP 401 {"code":401,"message":"Falha na autenticação mTLS"}` - **Resposta do Bradesco (2026-05-05)**: o erro acontece porque a chamada está usando o **modelo SERVER-TO-SERVER (legado)**, mas a URL da API só funciona com o **novo modelo mTLS via Portal Developers**. Os dois modelos não são intercambiáveis. ### Correção aplicada (2026-05-05) — Suporte a 2 modos de autenticação - Migration: `2026-05-05-100000_AddAuthModeToBoletoConfig.php` adiciona: - `auth_mode` ENUM('server_to_server','mtls_portal') DEFAULT 'mtls_portal' - `token_url_mtls_prod` DEFAULT 'https://openapi.bradesco.com.br/oauth/access-token' - `token_url_mtls_sandbox` DEFAULT 'https://proxy.api.prebanco.com.br/oauth/access-token' - `BradescoService::autenticar()` agora ramifica em `autenticarMtlsPortal()` (novo: client_credentials + Basic Auth + mTLS) ou `autenticarServerToServer()` (legado: JWT Bearer Assertion) - UI: aba "Boleto Bradesco" ganhou radio "Modo de Autenticação" + URLs específicas por modo (visíveis condicionalmente) - Produção migrada para `auth_mode='mtls_portal'` em 2026-05-05 ### Teste 2026-05-05 — DESBLOQUEIO ✅ URL OFICIAL ENCONTRADA **URL oficial mTLS Portal Developers** (descoberta em Postman Collection oficial Bradesco): - Produção: `https://openapi.bradesco.com.br/auth/server-mtls/v2/token` - Sandbox: `https://openapisandbox.prebanco.com.br/auth/server-mtls/v2/token` **Padrão da requisição (diferente do que implementei inicialmente):** - Method POST - Body urlencoded (NÃO Basic Auth no header): - `grant_type=client_credentials` - `client_id=` - `client_secret=` - mTLS no TLS handshake (cert+key, com cert binding `cnf.x5t#S256` no token) **Resultado do teste em produção:** - Auth `/auth/server-mtls/v2/token` → **200 OK**, token len 2363 com `clientType: server-mtls`, `iss` apontando para a nova URL - API `/boleto-hibrido/cobranca-registro/v1/gerarBoleto` com novo token → **HTTP 500** com `NullPointerException` no `RegistrarBoletoService.criarRegistrarBoleto` (porque mandei `{}` como body) — isso CONFIRMA que a auth foi aceita e agora é só erro de validação de payload no mainframe. - Tanto `Authorization: ` quanto `Authorization: Bearer ` funcionam (gateway aceita ambos) **Aplicado em produção 2026-05-05:** - `boleto_config.token_url_mtls_prod` = `https://openapi.bradesco.com.br/auth/server-mtls/v2/token` - `boleto_config.token_url_mtls_sandbox` = `https://openapisandbox.prebanco.com.br/auth/server-mtls/v2/token` - DDL defaults da tabela atualizados via ALTER TABLE - `BradescoService::autenticarMtlsPortal()` corrigido: client_id+client_secret no body urlencoded (em vez de Basic Auth no header) - `validacao_status='ok'` **Why:** a documentação pública oficial do produto não traz o endpoint de auth — está num documento separado (Postman Collection), e a URL `/auth/server-mtls/v2/token` é exclusiva do produto "Cobrança/Pagamento de Boletos" no Portal Developers (não é OIDC discovery padrão). **How to apply:** se em sessão futura aparecer 401 mTLS de novo, verifique primeiro: (1) se o cert venceu, (2) se o `auth_mode='mtls_portal'`, (3) se a URL ainda é `/auth/server-mtls/v2/token`. ### Tentativas de geração 2026-05-05 — Auth OK, mainframe rejeita Após auth funcionar, evolução dos erros do `gerarBoleto`: 1. **Carteira 09** → `IDENTIFICADOR DO PRODUTO NAO CADASTRADO` (carteira 09 NÃO está cadastrada nesse contrato) 2. **Carteira 26 ou 28** → produto reconhecido, próxima validação dispara 3. `cnegocCobr` 19 chars (com DV) → `Tamanho do Campo foi Excedido` 4. `cnegocCobr` 18 chars (sem DV) → aceito ✅ 5. Sem endereço do aluno → `DADOS INCONSISTENTES - 0840` → adicionado validação no controller que bloqueia antes (mensagem clara dos campos faltantes) 6. Acentos no nome → adicionado `BradescoService::sanitize()` (iconv ASCII//TRANSLIT) 7. **Mesmo com tudo OK** → continua `DADOS INCONSISTENTES - 0840` (genérico, não identifica campo) **CAUSA RAIZ identificada via teste cirúrgico + pesquisa (2026-05-05):** O erro `DADOS INCONSISTENTES - 0840` significa que o **serviço de registro de boletos via webservice NÃO está ativo no contrato em produção**. Confirmado por pesquisa em fóruns de devs (Casa do Desenvolvedor, ACBr) — esse erro genérico indica contrato inativo no produto, não problema de payload. **Mapa de validações descoberto via testes cirúrgicos (curl direto via mTLS):** - `tipoAcesso=2` → gera erro genérico `0840` (mascara o real) - `tipoAcesso=1` → gera erros específicos campo a campo: - `cpssoaJuridContr=0006011803` ✅ VÁLIDO (zerar dá "CÓDIGO DA PESSOA JURÍDICA DO CONTRATO INVÁLIDO") - `nseqContrNegoc=0005346382` ✅ VÁLIDO (zerar dá "TIPO DO CONTRATO DE NEGÓCIO INVÁLIDO") - `ctpoContrNegoc` testado com 30+ valores numéricos (0..900) — todos retornam "TIPO DO CONTRATO DE NEGÓCIO INVÁLIDO". Esse campo provavelmente exige valor específico fornecido pelo Bradesco no momento da ativação do serviço. - `cidtfdProdCobr=09` → "PRODUTO NÃO CADASTRADO" (carteira 09 não está habilitada) - `cidtfdProdCobr=26` ou `28` → produto reconhecido - `cnegocCobr` aceita 18 chars (`agencia(4)+zeros(7)+conta(7 sem DV)`); 19 chars retorna "Tamanho excedido" **Ação pendente (única que falta):** Enviar e-mail para **`suportewebservice@bradesco.com.br`** (NÃO `transacoes_api`) solicitando **ativação do serviço de registro de boletos via webservice** no contrato: - CNPJ: 61.412.207/0001-68 - Agência/Conta: 1028 / 0062060-2 - Contrato: nseqContrNegoc=0005346382 - Erro: `DADOS INCONSISTENTES - 0840` - Modelo de e-mail pronto em: `suporte_bradesco/chamado_0840.txt` **Importante:** o lado técnico está 100% pronto — auth mTLS Portal Developers funcionando, payload válido para todos os campos identificáveis, validações em ordem. Só falta o **Bradesco habilitar o produto no contrato em produção**. ### Carteira correta do contrato (descoberto empiricamente 2026-05-05) - **NÃO usar 09** (não está cadastrada no contrato `0005346382`) - Carteiras 26 e 28 são reconhecidas (passam o gate inicial do produto) - Default na config foi mudado de "09" para "26" ### Descoberta crucial (2026-04-20) A autenticação do Bradesco NÃO usa padrão OAuth clássico. Achei o formato correto no repo [GYOVANNE/bradesco-access-token](https://github.com/GYOVANNE/bradesco-access-token): - `assertion=` (não `client_assertion`) - `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer` (não `client-assertion-type`) - Header/payload com base64 regular, signature com base64url - Sem `kid`, sem `iss` ### Aba Boleto Bradesco em Configurações - Dados pré-preenchidos do banco (`boleto_config`) - Ambiente selecionável (sandbox/producao) - Certificado com preview de validade + SHA1 + badge "✓ Cadastrado" - Botões: "💾 Salvar" + "🧪 Testar Autenticação" - Upload de cert público, chave privada, validação via OpenSSL antes de salvar - Status badge (validado/erro/não testado) **Why:** Sistema pronto pra operar assim que Bradesco destravar mTLS. Todo código aderente ao padrão, cert correto. **How to apply:** Se em nova sessão o usuário pedir pra testar/debuggar boleto, primeiro rode `POST /direcao/api/configuracoes-boleto/testar` — se retornar access_token OK mas a geração ainda falhar, é o mesmo bloqueio mTLS do Bradesco (esperando resposta deles).