QR Code de Pix em SVG com a sua marca no meio, sem quebrar a leitura no app do banco.
Eu queria pôr a logo do meu produto no meio do QR do Pix. Parece a coisa mais simples do mundo: gera o QR, joga a imagem em cima, acabou.
Aí o código para de ler.
Passei mais tempo do que eu gostaria de admitir mexendo nisso no olho. Diminuía o furo e testava, o app não abria. Aumentava a imagem, ficava bonito e parava de funcionar de novo. Troquei de gerador achando que era ele. Em lugar nenhum estava escrito o que de fato importa.
Quando finalmente funcionou, entendi que não era uma coisa só: eram quatro decisões, e dava para errar cada uma sozinha. Tirei essa parte do meu código, escrevi o porquê de cada uma junto e deixei aqui, pra próxima pessoa não gastar o tempo que eu gastei.
npm install qrpix-logoimport { qrPix } from "qrpix-logo";
const svg = qrPix(copiaECola, {
tamanho: 320,
logo: '<svg viewBox="0 0 100 100">...</svg>',
});Sai uma string de SVG. Você joga no seu HTML e pronto. Não depende de navegador, então roda no servidor também.
O QR é gerado no nível H, que reconstrói 30% do código. É esse orçamento que paga o buraco da logo.
Com M, que reconstrói 15%, o mesmo furo derruba a leitura em parte dos leitores. E "parte dos leitores" é o pior tipo de bug que existe: funciona no seu celular e falha no do cliente, e você não fica sabendo.
O preço é densidade. Um BR Code de 170 caracteres vira uma matriz 65×65 em H contra 49×49 em M. Mais módulos no mesmo espaço quer dizer cada módulo menor.
Um furo quadrado no meio de módulos pequenos vira uma reta crua, e o olho lê como imagem cortada em vez de parte do desenho. Redondo, a borda acompanha a logo e some.
Essa foi a que mais me irritou, porque eu não conseguia enxergar o que estava errado.
Se você tira só os módulos cujo centro cai dentro do círculo, os que ficam na borda continuam desenhados, e o disco branco corta um pedaço de cada um. Sobra um anel de lascas em volta da logo. No tamanho normal quase não dá pra notar. Ampliando, é a única coisa que você vê.
O módulo tem que sair inteiro se encostar no círculo. Tem teste pra isso: mede a distância de cada módulo desenhado até a borda do disco e exige folga positiva.
Essa me custou mais caro que as outras três juntas.
Se você desenhar os módulos com uma variável CSS de tema (var(--text-primary)
e parentes), o QR some no modo escuro: o token vira quase branco e o fundo do
código continua branco. Eu medi 16,60:1 de contraste no claro contra 1,11:1 no
escuro. A página abre normal, o SVG está inteiro lá, e o que aparece é um
retângulo branco.
Inverter no escuro parece a saída. Não é. QR é escuro sobre claro por especificação, e parte dos leitores recusa o invertido. Numa tela de pagamento isso não se aposta, então o código fica igual nos dois temas, que é o que todo app de banco faz.
Dá pra trocar as cores por opção, mas a escura tem que ser a dos módulos.
pixelsPorModulo() responde antes de renderizar:
pixelsPorModulo(copiaECola, 300); // 4.35 num BR Code de 170 charsAbaixo de uns 4 px por módulo a leitura começa a depender da câmera e da luz. Como a matriz cresce junto com o tamanho do BR Code, o mesmo tamanho em pixels não serve para toda cobrança.
Teste passar não prova que o banco lê. O único jeito é escanear.
O truque que torna isso de graça: use o copia-e-cola de uma cobrança já paga. O app decodifica primeiro e valida depois, então ele abre e avisa que a cobrança não vale mais. Se ele disse isso, ele leu, e nenhum centavo saiu.
npm run exemplo -- "00020126580014BR.GOV.BCB.PIX..."Escreve um escaneia.html com o seu QR e um sem logo do lado. Se o sem logo lê
e o com logo não, o problema é o desenho. Se nenhum dos dois lê, o problema está
no BR Code e não na estética.
| opção | padrão | o que faz |
|---|---|---|
tamanho |
320 |
lado do SVG em px |
logo |
(nenhum) | SVG com viewBox="0 0 100 100". Sem ele não abre furo nenhum |
tinta |
#111111 |
cor dos módulos, tem que ser a escura |
papel |
#ffffff |
cor do fundo |
furo |
0.115 |
raio da área limpa, como fração do lado |
Lança se o copia-e-cola vier vazio. Não valida o BR Code: validar é problema de quem gera a cobrança.
Devolve { tamanho, linhas } com as linhas em "0" e "1", se você quiser
desenhar de outro jeito.
Quantos pixels sobram por módulo naquele tamanho.
Não gera o BR Code, recebe pronto de quem emite a cobrança. Não valida CRC nem os campos do EMV. Não gera PNG, se precisar renderize o SVG.
MIT