Boleto FEBRABAN em Node.js: gerar linha digitável para testes
Boleto FEBRABAN em Node.js: gerar linha digitável para testes
Boleto bancário é parte obrigatória de qualquer suite de testes que toque pagamentos brasileiros. O problema: a maioria das equipes copia números reais de produção para fixtures — o que viola a LGPD e cria dependências frágeis de dados externos. Este tutorial mostra como gerar uma linha digitável FEBRABAN válida do zero, em TypeScript, sem tocar em dados reais.
O que é a linha digitável e por que ela importa em testes
Diferença entre código de barras e linha digitável (FEBRABAN Manual de Normas 003.008)
O código de barras tem 44 dígitos e é lido opticamente. A linha digitável tem 47 dígitos (incluindo três dígitos verificadores de campo) e é o que o usuário digita quando o código de barras não escaneia. O Manual de Normas FEBRABAN 003.008 define os dois formatos e exige que ambos representem os mesmos dados — qualquer divergência entre eles deve ser rejeitada pelo banco cobrador.
Em testes, você precisa dos dois. Parsers de gateway geralmente aceitam qualquer um. Sistemas bancários legados aceitam só a linha digitável.
Campos obrigatórios: banco, agência, nosso número, vencimento, valor, dígito verificador
| Campo | Posição no código de barras | Tamanho | Exemplo |
|---|---|---|---|
| Código do banco | 1-3 | 3 | 341 (Itaú) |
| Código da moeda | 4 | 1 | 9 (Real) |
| Dígito verificador geral | 5 | 1 | calculado |
| Fator de vencimento | 6-9 | 4 | 9999 |
| Valor nominal | 10-19 | 10 | 0000015000 (R$ 150,00) |
| Campo livre | 20-44 | 25 | varia por banco |
Por que testar com boletos reais é LGPD Art. 7º, IX — e quando deixa de ser
LGPD Art. 7º, IX permite tratamento de dados pessoais quando necessário para atender interesses legítimos do controlador. Isso não cobre fixtures de CI/CD: o CPF real do João está no seu banco de testes sem necessidade, sem base legal, e exposto a todos que têm acesso ao repositório. Use dados fictícios gerados algoritmicamente e a questão deixa de existir.
---
Estrutura do boleto FEBRABAN campo a campo
Campo livre e campos 1-2-3 (blocos 9.10 e 10.10 do Manual)
O campo livre (posições 20-44 do código de barras) é onde cada banco coloca seus dados internos: agência, conta, nosso número, carteira. Para montar a linha digitável, esse campo de 25 dígitos é dividido em três blocos:
- Campo 1: banco(3) + moeda(1) + campo livre posições 1-5 = 9 dígitos + dígito verificador mod10
- Campo 2: campo livre posições 6-15 = 10 dígitos + dígito verificador mod10
- Campo 3: campo livre posições 16-25 = 10 dígitos + dígito verificador mod10
Fator de vencimento: cálculo a partir de 07/10/1997 (data base FEBRABAN)
O fator de vencimento é o número de dias corridos entre 07/10/1997 e a data de vencimento do boleto, com 4 dígitos. O valor máximo é 9999 (que corresponde a 18/02/2025 — data em que o ciclo recomeçou, passando a contar do fator 1000 de novo segundo atualização FEBRABAN de 2025). Boleto sem vencimento usa 0000.
Valor nominal: 10 dígitos sem separador decimal
R$ 150,00 vira 0000015000. R$ 1.234,56 vira 0000123456. Zero vírgulas, zero pontos, sempre 10 dígitos com padding esquerdo.
Dígito verificador geral: módulo 11 com pesos 2-9
O dígito geral (posição 5 do código de barras) é calculado sobre os outros 43 dígitos do código de barras (excluindo a própria posição 5). Os pesos ciclam de 2 a 9, da direita para a esquerda. Resto 0 e resto 1 retornam dígito 1 — essa é a exceção da spec FEBRABAN, diferente de outras implementações de módulo 11.
---
Algoritmo do dígito verificador passo a passo
Módulo 10 nos campos 1, 2 e 3
Cada campo da linha digitável termina com um dígito calculado via módulo 10:
1. Multiplique os dígitos alternando entre peso 2 (da direita) e peso 1. 2. Se o produto for maior que 9, some os algarismos do produto. 3. Some todos os resultados. 4. Dígito = 10 - (soma % 10). Se o resultado for 10, dígito = 0.
Módulo 11 no campo 4 (dígito geral do código de barras)
1. Concatene o código de barras sem a posição 5 (43 dígitos). 2. Multiplique da direita com pesos ciclando 2,3,4,5,6,7,8,9,2,3... 3. Some todos os produtos. 4. Resto = soma % 11. 5. Se resto == 0 ou resto == 1, dígito verificador = 1. 6. Caso contrário, dígito verificador = 11 - resto.
Casos especiais: resto 0 e resto 1 retornam dígito 1 (spec FEBRABAN)
Diferente do módulo 11 usado em CPF/CNPJ (onde resto 0 e 1 retornam 0), a spec do código de barras bancário retorna 1 nesses casos. Misturar as duas implementações é a fonte de erro mais comum.
---
Implementação em TypeScript: gerador de linha digitável
Tipos e interface BoletoParams
interface BoletoParams {
bankCode: string; // 3 dígitos: '001', '237', '341'
currencyCode: string; // sempre '9'
freeField: string; // 25 dígitos, específico por banco
maturityFactor: string; // 4 dígitos, '0000' = sem vencimento
value: string; // 10 dígitos sem separador decimal
}Função calcMod10(block: string): string
function calcMod10(block: string): string {
let sum = 0;
let weight = 2;
for (let i = block.length - 1; i >= 0; i--) {
let product = parseInt(block[i]) * weight;
if (product > 9) product = Math.floor(product / 10) + (product % 10);
sum += product;
weight = weight === 2 ? 1 : 2;
}
const remainder = sum % 10;
return remainder === 0 ? '0' : String(10 - remainder);
}Função calcMod11Febraban(barcode: string): number
function calcMod11Febraban(barcode: string): number {
// barcode aqui já exclui a posição 5 (43 dígitos)
const weights = [2, 3, 4, 5, 6, 7, 8, 9];
let sum = 0;
let wi = 0;
for (let i = barcode.length - 1; i >= 0; i--) {
sum += parseInt(barcode[i]) * weights[wi % 8];
wi++;
}
const remainder = sum % 11;
if (remainder === 0 || remainder === 1) return 1;
return 11 - remainder;
}Função buildBarcode(params: BoletoParams): string
function buildBarcode(params: BoletoParams): string {
const { bankCode, currencyCode, freeField, maturityFactor, value } = params;
const withoutCheckDigit =
bankCode + currencyCode + maturityFactor + value + freeField;
const checkDigit = calcMod11Febraban(withoutCheckDigit);
return bankCode + currencyCode + checkDigit + maturityFactor + value + freeField;
}Função barcodeToDigitableLine(barcode: string): string
function barcodeToDigitableLine(barcode: string): string {
const bank = barcode.slice(0, 3);
const currency = barcode.slice(3, 4);
const freeField = barcode.slice(19, 44);
const block1Raw = bank + currency + freeField.slice(0, 5);
const block1 = block1Raw + calcMod10(block1Raw);
const block2Raw = freeField.slice(5, 15);
const block2 = block2Raw + calcMod10(block2Raw);
const block3Raw = freeField.slice(15, 25);
const block3 = block3Raw + calcMod10(block3Raw);
const checkDigit = barcode.slice(4, 5);
const maturityAndValue = barcode.slice(5, 19);
const f1 = `${block1.slice(0, 5)}.${block1.slice(5)}`;
const f2 = `${block2.slice(0, 5)}.${block2.slice(5)}`;
const f3 = `${block3.slice(0, 5)}.${block3.slice(5)}`;
return `${f1} ${f2} ${f3} ${checkDigit} ${maturityAndValue}`;
}Exemplo completo end-to-end:
const params: BoletoParams = {
bankCode: '341',
currencyCode: '9',
freeField: '1234567890123456789012345',
maturityFactor: '9999',
value: '0000015000',
};
const barcode = buildBarcode(params);
const digitableLine = barcodeToDigitableLine(barcode);
console.log('Código de barras:', barcode);
console.log('Linha digitável: ', digitableLine);
// Linha digitável: 34191.23456 67890.123456 78901.234567 X 99990000015000---
Gerando dados de suporte LGPD-safe para o boleto
CNPJ do cedente com /gerador-cnpj
O cedente precisa de CNPJ válido no campo livre de muitos bancos. Use a API REST do FakeForge para pegar um CNPJ fictício que passa no módulo 11:
curl "https://fakeforge.com.br/api/generate?type=cnpj&quantity=1&format=json"A resposta inclui _meta.source e o CNPJ já validado. Passe direto para o campo livre do boleto sem nenhum pós-processamento.
CPF do pagador com /gerador-cpf
O nome e CPF do pagador aparecem no slip impresso mas não entram no cálculo do dígito verificador. Use /gerador-pessoa para gerar nome + CPF correlacionados — o mesmo endpoint gera primeiro nome, sobrenome e CPF coerentes com gênero e região.
Conta bancária e código de banco com /gerador-conta-bancaria
O campo livre varia por banco. Para Itaú (341), o campo livre inclui agência e conta no formato específico Itaú. Para Bradesco (237), é diferente. Use /gerador-conta-bancaria para obter agência, conta e código COMPE aleatórios de um dos 17 bancos reais suportados.
Por que usar dados fictícios correlacionados evita falsos positivos em CI
Um CNPJ aleatório inválido faz o validador de gateway rejeitar o boleto antes de chegar ao teste de pagamento. Um CPF real de produção faz o banco de dados de fraude sinalizar a transação em ambiente de staging. Dados fictícios válidos eliminam as duas falhas.
DICA: Gere os dados de suporte uma vez, commite no repositório como fixture JSON, e referencia o arquivo nos testes. Não faça chamada HTTP em tempo de teste — quebre o CI quando a rede não está disponível.
---
Validando a linha digitável gerada
Verificação manual via banco de testes (Banco do Brasil Sandbox, Sicoob Sandbox)
O Banco do Brasil disponibiliza sandbox em api.sandbox.bb.com.br. O Sicoob tem ambiente de homologação com documentação pública. Ambos aceitam boletos fictícios e retornam 200 se a linha digitável estiver correta.
Biblioteca boleto-utils como referência cruzada (sem dependência obrigatória)
O pacote boleto-utils (npm) implementa validação independente. Use como oráculo nos testes, não como dependência de produção:
describe('boleto gerado', () => {
it('dígito verificador está correto', () => {
const params: BoletoParams = {
bankCode: '341',
currencyCode: '9',
freeField: '1234567890123456789012345',
maturityFactor: '9999',
value: '0000015000',
};
const barcode = buildBarcode(params);
const line = barcodeToDigitableLine(barcode);
// formato esperado: NNNNN.NNNNN NNNNN.NNNNNN NNNNN.NNNNNN N NNNNNNNNNNNNNN
expect(line).toMatch(
/^\d{5}\.\d{6} \d{5}\.\d{7} \d{5}\.\d{7} \d \d{14}$/
);
const checkDigit = parseInt(barcode[4]);
const barcodeWithout = barcode.slice(0, 4) + barcode.slice(5);
expect(calcMod11Febraban(barcodeWithout)).toBe(checkDigit);
});
});---
Integrando o gerador numa fixture de testes Node.js
Fixture com 3 boletos usando CNPJ/CPF fictícios + exportação JSON
const fixtures = [
{ bankCode: '341', value: '0000015000', maturityFactor: '9999' },
{ bankCode: '237', value: '0000250000', maturityFactor: '0000' }, // sem vencimento
{ bankCode: '001', value: '0000000000', maturityFactor: '9998' }, // valor livre
].map((params, i) => {
const freeField = String(i + 1).padStart(25, '0');
const full: BoletoParams = { ...params, currencyCode: '9', freeField };
const barcode = buildBarcode(full);
return {
barcode,
digitableLine: barcodeToDigitableLine(barcode),
cnpjCedente: '11222333000181', // gerado via /gerador-cnpj
cpfPagador: '529.982.247-25', // gerado via /gerador-cpf
};
});
console.log(JSON.stringify(fixtures, null, 2));Seed determinístico: passando seed para reproduzir o mesmo conjunto em CI
O campo livre é derivado do índice no exemplo acima — determinístico por definição. Para dados de suporte (CNPJ, CPF), passe ?seed=42 na chamada à FakeForge API quando o endpoint suportar. Commite o resultado como arquivo .json em __fixtures__/boletos.json.
---
Casos de borda que quebram parsers de boleto
Fator de vencimento 0000 (boleto sem vencimento, FEBRABAN admite)
0000 é válido pela spec. Parsers que esperam fator >= 1000 rejeitam esse boleto silenciosamente. Inclua um caso com maturityFactor: '0000' na suite.
Valor 0000000000 (cobrança de valor livre)
Usado em carnês onde o pagador preenche o valor. Parsers que validam valor > 0 quebram aqui. Teste explicitamente.
Banco 001 vs 237 vs 341: variações no campo livre por instituição
O campo livre de 25 dígitos tem estrutura interna diferente por banco. Itaú usa os primeiros 3 dígitos para carteira. Bradesco usa os primeiros 3 para agência. Para testes de integração contra um gateway específico, construa o campo livre seguindo o manual do banco-alvo.
Boleto de convênio (campo livre diferente do modelo padrão)
Boletos de convênio (tributos, concessionárias) seguem o padrão 9NNNN, não o bancário. O cálculo do dígito verificador é diferente. Não misture os dois parsers.
---
Limites legais ao usar dados fictícios em testes
LGPD Art. 13 — anonimização como base legal para uso em pesquisa e teste
LGPD Art. 13 permite uso de dados anonimizados para pesquisa e desenvolvimento sem necessidade de consentimento. Dados gerados algoritmicamente (nunca associados a uma pessoa real) satisfazem esse critério. CPF gerado via /gerador-cpf não é dado pessoal — é um número que passa na validação de formato sem corresponder a nenhum titular.
Circular BACEN 3.952/2019 e o que ela diz sobre dados de pagamento em sandbox
A Circular BACEN 3.952/2019 regula o uso de dados de pagamento em ambiente de produção. Ambientes de sandbox com dados fictícios ficam fora do escopo — desde que os dados nunca transitem por infra de produção e não haja risco de confusão com transações reais.
O que não fazer: nunca usar CPF/CNPJ real em fixture de CI/CD
Um CPF real em um repositório Git é um dado pessoal exposto. Se o repo é público, é vazamento. Se é privado, ainda é tratamento sem base legal. Use /validar-cpf para confirmar que o CPF fictício passa na validação antes de commitar — e mantenha a origem fictícia documentada no próprio arquivo de fixture.
---
Resumo
- Gere o código de barras com
buildBarcode()e converta para linha digitável combarcodeToDigitableLine()— são funções puras, sem dependência externa. - Módulo 10 vai nos campos 1, 2 e 3 da linha digitável; módulo 11 com pesos 2-9 vai no dígito verificador geral (posição 5 do código de barras).
- Resto 0 e resto 1 no módulo 11 FEBRABAN retornam dígito 1 — diferente de CPF/CNPJ.
- Teste os três casos de borda obrigatórios: fator de vencimento
0000, valor0000000000, e pelo menos dois bancos diferentes. - Use /gerador-cnpj, /gerador-cpf e /gerador-conta-bancaria para preencher CNPJ do cedente, CPF do pagador e campo livre sem tocar em dados reais.
- Commite fixtures como JSON estático gerado com seed fixo — não faça chamadas HTTP durante a execução dos testes.
Perguntas frequentes
Como debugar se minha linha digitável foi rejeitada — erro no módulo 10 ou 11?+
Isolate. Se o gateway retornar 'invalid check digit', valide o módulo 11 do código de barras primeiro (posição 5). Se passar, erro está nos módulos 10 dos campos 1-2-3. Log cada componente antes de concatenar. Use parser online ou boleto-utils para cotejar.
Posso usar a mesma linha digitável em staging e produção?+
Não. Cada ambiente deve gerar boletos novos. Hardcodar linha em produção vincula ao cedente de staging ou expira. Gere dados por ambiente. Se reproduz em testes, passe os parâmetros e rederive localmente com seed fixo para reproducibilidade.
Minha linha digitável precisa bater com a do banco?+
Sim. Se discordam, há erro no campo livre ou dígito verificador. Teste contra sandbox do banco — retorna qual campo errou. Sem sandbox, coteje com boleto-utils. Se ambos divergem do seu cálculo, debug módulo 11 ou estrutura do campo livre.
Como construir o campo livre se o banco não está documentado?+
Consulte FEBRABAN 003.008 banco-específico. Se indisponível, reverta a engenharia observando boletos reais em SPED ou extratos. Campo livre contém agência, conta, carteira — varia por instituição. Último recurso: contacte suporte técnico do banco.
Se o vencimento for daqui 10 anos, qual fator uso?+
Máximo fator é 9999 (vence 18/fev/2025). Vencimentos posteriores usam `0000` com data em observação fiscal. FEBRABAN permite; bancos modernos aceitam. Ciclo reinicia conforme resoluções BACEN. Valide sempre contra documentação do banco-alvo.