Payments
The PIX CRC16 checksum, and why hand-edited codes always fail
PIX uses CRC16/CCITT-FALSE (polynomial 0x1021, initial value 0xFFFF, no reflection, no final XOR) computed over the entire payload including the literal 6304 prefix, and written as four uppercase hex digits in field 63. Any edit made without recomputing the checksum makes every banking app reject the code.
The parameters
Field 63 of every BR Code is a CRC16 in the
CCITT-FALSE configuration:
| Parameter | Value |
|---|---|
| Polynomial | 0x1021 |
| Initial value | 0xFFFF |
| Input/output reflection | None |
| Final XOR | None |
| Output | 4 uppercase hex digits |
This is the same CRC family used across EMVCo merchant-presented codes, PIX inherited it from the EMVCo base spec rather than inventing it. See CRC16 for the general concept.
How it runs
The algorithm walks the payload one byte at a time. Each byte is XORed into the high byte of
a 16-bit register, then the register is shifted left eight times; whenever the top bit is
set before a shift, the register is XORed with 0x1021. After the last byte, the register
value (printed as four uppercase hex digits) is the checksum. It is about ten lines in
any language, and the worked example below is the fastest way to check yours against a
known-good answer.
The detail that catches implementers
The CRC is computed over the payload including the literal 6304 field header, as if
the four checksum digits themselves were simply absent:
input = 000201...6207 0503 *** 6304
crc = CRC16(input) → e.g. A13F
output = input + "A13F"
The single most common implementation bug is computing the CRC over everything before
6304. The result is a well-formed string with a checksum that never matches, and a code
that no app in Brazil will accept. If your generated codes fail in every app, check this
first.
Why a hand-edited PIX string always fails
People try to edit a working copia e cola string (swap the amount, change the name), without regenerating it. This cannot work. The checksum was computed over the old bytes; after any edit it no longer matches, and apps reject the whole string. For a random edit, the chance the old checksum happens to remain valid is 1 in 65,536.
That is by design. The CRC exists to catch corruption: a mangled character in a copied-and-pasted string, a misdecoded module in a damaged print. A payments payload that tolerated single-character changes would be a fraud vector, not a convenience.
Also note the interaction with field lengths: every TLV field carries a two-digit byte
length, so a non-ASCII character (whose UTF-8 encoding is more than one byte) silently breaks
the length counting, which changes the bytes, which breaks the CRC. Transliterate names
first, São Paulo must become Sao Paulo.
Verifying a code
Three ways, in increasing rigour:
- Decode the code with /validate and confirm the payload parses.
- Regenerate the payload from the same inputs in the PIX builder and compare strings: same inputs must produce the identical string, checksum included.
- Call
GET https://useqr.app/api/v1/verify?data=…, which renders the code and decodes it back, proving the printed artefact will scan.
And always, before printing: scan with a real banking app and check the name and amount it displays.
FAQ
What CRC does a PIX QR code use?
CRC16/CCITT-FALSE (polynomial 0x1021, initial value 0xFFFF, no input or output reflection, no final XOR) output as four uppercase hexadecimal digits in field 63, computed over the whole payload including the literal 6304 prefix.
Why is my generated PIX code rejected by every app?
Most often the CRC was computed over the payload excluding the 6304 field header, or a non-ASCII character broke the field-length counting. Both produce a checksum mismatch, and apps reject mismatched codes without a specific error.
Can I edit a PIX string to change the amount?
Not by editing text. Any change invalidates the CRC16 and the string will be rejected. Regenerate the payload with the new amount so the checksum is recomputed over the new bytes.
Does the CRC protect against fraud?
It protects against corruption, not substitution. A fraudster can generate a fully valid code pointing at their own key. The fraud defence is the payee name shown on the confirmation screen, not the checksum.
Try it: free, no signup
Related
- The PIX QR code format: EMV BR Code explained, A PIX QR code is an EMVCo BR Code: tag-length-value fields ending in a CRC16/CCITT-FALSE checksum. The same string is also copia e cola, which people…
- PIX copia e cola: the copy-paste twin of the QR code, PIX copia e cola is the same BR Code string as the QR, offered as text. It solves the one case a QR cannot: paying a code shown on your own phone.
- EMVCo merchant-presented QR: the spec behind PIX, QRIS and PromptPay, The EMVCo MPM spec defines the TLV payload national payment QRs share: two-digit ids and lengths, templates 26-51 for scheme data, CRC16 in field 63.
- QR payment standards compared: UPI, PIX, EPC, EMVCo, UPI uses its own upi:// deep link; PIX, PromptPay, QRIS, DuitNow and PayNow are all EMVCo tag-length-value profiles; EPC uses a twelve-line text block.…