Repository navigation
PHPAY-109: compor o QR Code estático de Pix localmente - #126
Merged
Merged
Conversation
By the BACEN standard a static QR Code is assembled from the Pix key, locally: there is nothing to request. Asaas and Woovi offer an endpoint for it as a convenience, Efí does not offer one, and until now the library could only pass that gap on to whoever needed it. StaticPixCode is support code, like Money — not a Resource, not a capability, no case in the Capability enum, because no HTTP happens. It returns the copy-and-paste payload only: rendering the image needs a QR Code encoder, and a new dependency for a picture is not a trade this library makes. The CRC is the part implementations get wrong: it covers the `6304` literal that announces it, not only what comes before. Stopping earlier produces a code that simply does not read, and the error shows up in front of the payer rather than in a log. So the suite carries the test vector from the Manual de Padrões para Iniciação do Pix and demands the payload byte for byte, 1D3D included — the wrong calculation gives 4AAD for the same vector. Two decisions worth naming. Lengths in the TLV are counted in bytes, not characters, because a reader walks bytes and a name with an accent would misalign every field after it. And the key format is not validated: EVP, e-mail, phone and CPF/CNPJ look nothing alike, so a hasty check would refuse a good key — only an empty key and one over 77 characters are refused, 77 being what fits the field.
A page in docs/ with what to know before using it: the receiver name is required by the standard and ignored by the payer app, which shows the name from the DICT; only the payload is returned; the txid takes letters and digits and PEDIDO-1234 does not pass; a raw amount is reais. The matrix bullet and the Efí note now point there, since that is where the asymmetry was documented.
This was referenced Oct 22, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #109.
O que muda
PHPay\Support\StaticPixCodecompõe o BR Code de uma chave Pix, sem rede:É suporte, como o
Money: não é Resource, não é capacidade e não entra na enumCapability, porque não há chamada HTTP e portanto não há gateway envolvido. Fecha a assimetria doSupportsPixKeys— Asaas e Woovi expõem endpoint para o QR estático, a Efí não — sem stub e sem sugerir que a API da Efí oferece o que não oferece.O CRC, que é onde isso dá errado
O CRC-16 final cobre o literal
6304que o anuncia, não para antes dele. Parar antes é o erro mais comum de quem implementa o padrão, e o resultado é um código que simplesmente não lê — o erro aparece na frente do pagador, não no log.Então a prova não é "os testes passam", é o vetor oficial do Manual de Padrões para Iniciação do Pix, byte a byte. Conferi o vetor por conta própria antes de escrever uma linha de implementação:
E o payload que a classe produz para esse vetor é idêntico ao do manual:
Esse é o primeiro teste do arquivo. Um teste adicional lê o TLV de volta e confere campo por campo, porque um byte errado em qualquer tamanho desalinha todos os campos seguintes — é o tipo de defeito que um
toContain()não pega.Decisões que valem revisão
Só o payload, nunca a imagem. Renderizar exige codificador de QR Code, e o runtime aqui é
ext-curl,ext-json,ext-openssle o Guzzle. Dependência nova numa biblioteca de pagamentos custa mais que a conveniência.Tamanho em bytes, não em caracteres. Quem lê o payload anda em bytes;
Comérciotem 8 caracteres e 9 bytes, e declarar 8 desalinharia tudo depois. Tem teste.O formato da chave não é validado. EVP, e-mail, telefone e CPF/CNPJ não se parecem, e uma checagem apressada reprovaria chave boa. Recusa chave vazia e acima de 77 caracteres, que é o que cabe no campo 26.
O
merchantNameé cosmético — o app do pagador mostra o nome que vem do DICT e ignora o campo 59. Está no PHPDoc e na documentação, para ninguém construir em cima dele.A Efí não ganhou atalho. Dava para expor um
staticQrCode()no recursoPixdela chamando o VO por baixo, pela simetria. Não fiz: todo método de Resource faz HTTP, um que não faz quebra a expectativa, e sugeriria que a API da Efí oferece algo que ela não oferece. A nota da Efí emdocs/aponta para o VO.Como testar
composer test: 506 testes (de 483), nenhum tocando a rede — não há o que mockar aqui. Os dois commits foram verificados um por um.Além do vetor oficial: valor ausente e presente,
Moneyem reais e em centavos, número cru lido como reais, txid ausente e presente, imutabilidade dowith...(), acento contado em bytes, e as recusas (chave vazia, chave longa, nome e cidade vazios ou longos, txid com hífen, espaço ou longo, valor com três casas e valor negativo).Checklist
composer testpassa (Pint, Pest e PHPStan nível 9)Aditivo: classe nova, nada existente mudou de assinatura ou de comportamento.
Um limite que vale registrar: com isso a biblioteca passa a implementar uma especificação do BACEN, não só a API de um gateway. Se o manual mudar, isso aqui envelhece — o vetor na suíte é o que vai avisar.