referência técnica

A estrutura do BR Code

O que existe dentro de um "Pix copia e cola", campo a campo, e como montar um do zero.

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

IDNomeObservaçãoObrig.
00Payload Format IndicatorSempre 01.sim
01Point of Initiation Method12 marca uso único. Omitido em QR reutilizável.não
26Merchant Account InformationTemplate com os dados do Pix. Teto de 99 caracteres.sim
26.00GUIbr.gov.bcb.pixsim
26.01Chave PixCPF, CNPJ, e-mail, telefone com +55 ou UUID.sim
26.02DescriçãoTexto livre. Divide o teto de 99 com a chave.não
52Merchant Category Code0000 quando não informado.sim
53Transaction Currency986 (BRL, ISO 4217).sim
54Transaction AmountPonto decimal, sem separador de milhar.não
58Country CodeBRsim
59Merchant NameMáximo 25 caracteres.sim
60Merchant CityMáximo 15 caracteres.sim
61Postal CodeSó dígitos.não
62Additional Data FieldTemplate. Sub-campo 05 carrega o txid.sim
62.05Reference LabelA-Za-z0-9, máximo 25. Use *** se não houver.sim
63CRC16Quatro 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.