O formato
O BR Code é uma adaptação brasileira do padrão EMV QRCPS. A codificação é TLV: cada campo é um identificador de dois dígitos, seguido do comprimento em dois dígitos, seguido do valor. Os blocos são concatenados sem separador.
Exemplo: 5913Fulano de Tal é o campo 59, com 13
caracteres de conteúdo, valor Fulano de Tal.
Os campos 26 e 62 são templates: o valor deles é, por sua vez,
uma sequência TLV. O parser precisa ser recursivo nesses dois.
Campos
| ID | Nome | Observação | Obrig. |
|---|---|---|---|
| 00 | Payload Format Indicator | Sempre 01. | sim |
| 01 | Point of Initiation Method | 12 marca uso único. Omitido em QR reutilizável. | não |
| 26 | Merchant Account Information | Template com os dados do Pix. Teto de 99 caracteres. | sim |
| 26.00 | GUI | br.gov.bcb.pix | sim |
| 26.01 | Chave Pix | CPF, CNPJ, e-mail, telefone com +55 ou UUID. | sim |
| 26.02 | Descrição | Texto livre. Divide o teto de 99 com a chave. | não |
| 52 | Merchant Category Code | 0000 quando não informado. | sim |
| 53 | Transaction Currency | 986 (BRL, ISO 4217). | sim |
| 54 | Transaction Amount | Ponto decimal, sem separador de milhar. | não |
| 58 | Country Code | BR | sim |
| 59 | Merchant Name | Máximo 25 caracteres. | sim |
| 60 | Merchant City | Máximo 15 caracteres. | sim |
| 61 | Postal Code | Só dígitos. | não |
| 62 | Additional Data Field | Template. Sub-campo 05 carrega o txid. | sim |
| 62.05 | Reference Label | A-Za-z0-9, máximo 25. Use *** se não houver. | sim |
| 63 | CRC16 | Quatro dígitos hexadecimais maiúsculos. | sim |
O CRC16
Variante CRC-16/CCITT-FALSE: polinômio 0x1021, valor inicial
0xFFFF, sem reflexão de bits e sem XOR final. O detalhe que derruba a
maioria das implementações é a ordem: concatene 6304 ao final da string
antes de calcular, e o resultado ocupa os quatro caracteres seguintes.
Para validar sua implementação, o vetor de teste padrão do algoritmo é
CRC("123456789") = 0x29B1.
function crc16(s) {
let crc = 0xffff;
for (let i = 0; i < s.length; i++) {
crc ^= s.charCodeAt(i) << 8;
for (let b = 0; b < 8; b++) {
crc = crc & 0x8000
? ((crc << 1) ^ 0x1021) & 0xffff
: (crc << 1) & 0xffff;
}
}
return crc.toString(16).toUpperCase().padStart(4, "0");
} Montando o payload
const tlv = (id, v) => id + String(v.length).padStart(2, "0") + v;
function montar({ chave, nome, cidade, valor, txid = "***" }) {
const mai = tlv("00", "br.gov.bcb.pix") + tlv("01", chave);
let p = "";
p += tlv("00", "01");
p += tlv("26", mai);
p += tlv("52", "0000");
p += tlv("53", "986");
if (valor) p += tlv("54", Number(valor).toFixed(2));
p += tlv("58", "BR");
p += tlv("59", nome.slice(0, 25));
p += tlv("60", cidade.slice(0, 15));
p += tlv("62", tlv("05", txid));
p += "6304";
return p + crc16(p);
} Armadilhas
Acentuação. O comprimento é contado em caracteres. Se você deixar passar um
caractere multibyte, o valor de length em JavaScript descasa do que o
leitor conta, e o código fica inválido. Vários aplicativos de banco também corrompem ou
recusam payloads com acento. Normalize para ASCII.
Espaço em branco. Ao limpar um código colado, remova apenas quebras de linha e
tabulações. Um replace(/\s/g, "") ingênuo destrói o espaço dentro de nomes
e desalinha todo o parse a partir dali.
Valor decimal. O campo 54 usa ponto como separador decimal. Se o
formulário aceita entrada brasileira, trate a vírgula como decimal e o ponto como
separador de milhar apenas quando houver exatamente três dígitos depois dele.
Teto do campo 26. Chave mais descrição não podem ultrapassar 99 caracteres no total do template. Trunque a descrição, nunca a chave.
O CRC não é segurança. Ele detecta erro acidental de transmissão. Quem adultera um código recalcula os quatro dígitos e o resultado passa em qualquer validação. A verificação real é o nome do titular que o aplicativo do banco exibe, obtido da consulta da chave ao diretório oficial.
Estático e dinâmico
Tudo acima descreve o QR estático, que carrega os dados dentro de si e pode ser montado
por qualquer um. O QR dinâmico usa o campo 26.25 para carregar uma URL que
aponta para o servidor de uma instituição participante, que devolve o payload assinado
em JWS. Emitir dinâmico exige ser participante do arranjo ou contratar a API de um.
Referência normativa
A especificação oficial é o Manual de Padrões para Iniciação do Pix, publicado pelo Banco Central do Brasil. Consulte-o para casos que este resumo não cobre.