
PIX, BR Code, and the EMVCo MPM: The QR All of Brazil Uses
The QR code that moved a country onto instant payments is EMV wearing a disguise. A field-by-field decode of a real PIX BR Code — the EMVCo Merchant-Presented Mode structure, the CRC16, static versus dynamic — and why the same TLV discipline from chip cards prints on a napkin.
✨TL;DR / Executive Summary
The QR code that moved a country onto instant payments is EMV wearing a disguise. A field-by-field decode of a real PIX BR Code — the EMVCo Merchant-Presented Mode structure, the CRC16, static versus dynamic — and why the same TLV discipline from chip cards prints on a napkin.
💡 TL;DR (Too Long; Didn't Read)
The QR that runs Brazil, in 90 seconds:
- A PIX BR Code is not a bespoke format. It is the EMVCo Merchant-Presented Mode (MPM) QR standard — the same ID-Length-Value discipline as chip-card TLV, but in printable ASCII instead of binary.
- Every field is two-digit ID + two-digit length + value.
00is the format indicator,26carries the PIX payload (br.gov.bcb.pix+ the key or a URL),54is the amount,59/60the merchant,62the txid,63the checksum.- The last field,
63, is a CRC16 (CCITT-FALSE, polynomial 0x1021, init 0xFFFF) computed over the entire payload including the literal6304— and getting that scope wrong is the number-one PIX integration bug.- Static codes carry the PIX key and are reusable; dynamic codes carry a URL to a signed, one-time payload generated per charge.
- If you read the earlier parts of this series, you already know this format. That is the whole point.
I want to close this series somewhere that looks, at first glance, like it has nothing to do with chip cards. No terminal, no APDU, no cryptogram — just a black-and-white square printed next to a cash register, or a copy-pasteable string in a chat message, that moved an entire country of 200 million people onto instant payments in a handful of years. The PIX BR Code is the most-used payment artifact in Brazil. It is also, once you look inside it, the same EMV you have been reading about all along, wearing a very effective disguise.
That is the payoff of Payments Under the Hood. The discipline that governs a chip transaction — structured data, well-known identifiers, explicit lengths, a checksum you must compute correctly — is not specific to silicon and card readers. It is how the payments industry encodes machine-readable financial instructions, full stop. PIX proves it by taking that discipline out of the chip and printing it on a napkin. Everything here follows the EMVCo QR Code Specification for Payment Systems, Merchant-Presented Mode, as profiled by Brazil's central bank (Banco Central do Brasil) for the PIX arrangement. I cite the structure; I do not reproduce the spec text.
The format is TLV you can read out loud
Recall the BER-TLV from Part 2: a tag, a length, a value, nested and self-delimiting. The EMVCo MPM QR format is the same idea with two deliberate simplifications that make it survive being printed, photographed, and pasted.
First, everything is ASCII decimal, not binary. An identifier is two decimal digits. A length is two decimal digits. The value is literal text. No bit-fiddling, no 0x1F continuation rule, no short-versus-long length ambiguity — because a QR code has to tolerate a phone camera and a chat app, and binary does not. Second, the structure is exactly one or two levels deep, using the same "some IDs are containers" idea EMV uses for constructed templates.
So a field reads as ID | LEN | VALUE, concatenated with the next field, over and over, until the checksum at the end. Here is a complete, valid static PIX BR Code. Read it as a stream and we will resolve every field:
00020126360014br.gov.bcb.pix0114+55619999999995204000053039865405
10.005802BR5913Fulano de Tal6008BRASILIA62070503***6304XXXX(That is one continuous string; it is wrapped here only to fit the page.)
Decoding it, field by field
Walk it left to right, always reading two digits of ID, two digits of length, then that many characters of value.
00 02 01 — Payload Format Indicator. ID 00, length 2, value 01. Every BR Code starts here; the value is always 01.
26 36 ... — Merchant Account Information. ID 26, length 36. This is a container — its 36-character value is itself a set of ID-length-value fields, exactly like a constructed EMV template:
0014br.gov.bcb.pix— the GUI, the globally unique identifier that says "this is a PIX payload." Fourteen characters, and it must be exactly this string.0114+5561999999999— the PIX key (chave). Here it is a phone-number key; it could equally be a CPF/CNPJ, an email, or a random EVP key. This sub-field being present (ID01) is what makes the code static.
52 04 0000 — Merchant Category Code. ISO 18245 MCC; 0000 when unspecified.
53 03 986 — Transaction Currency. ISO 4217 numeric; 986 is the Brazilian real. (You have seen ISO currency codes before — they ride inside the ARQC's MAC input too. Same code, different envelope.)
54 05 10.00 — Transaction Amount. Note it is text: 10.00, a decimal string, not a binary-coded amount. In a static code this field is optional — omit it and the payer types the amount, which is how a reusable "my PIX key" poster works.
58 02 BR — Country Code. ISO 3166-1 alpha-2.
59 13 Fulano de Tal — Merchant Name. Up to 25 characters.
60 08 BRASILIA — Merchant City. Up to 15 characters.
62 07 0503*** — Additional Data Field Template. Another container. Inside: 05 03 *** — the Reference Label, the field PIX uses for the transaction identifier (txid). The value *** is the spec's convention for "no specific txid" in a static code; a dynamic or itemized charge puts a real identifier here.
63 04 XXXX — CRC16. ID 63, length 4, and the value is a four-hex-digit checksum I have deliberately left as XXXX, because the one thing you must never do with a CRC is copy a fabricated one. How to compute it is the next section, and it is where most PIX integrations break.
The CRC16 that everyone gets wrong
The final field is a CRC16 and it is the single most common reason a hand-built or mis-built BR Code fails to scan. Three things have to be exactly right.
The algorithm is CRC-16/CCITT-FALSE: polynomial 0x1021, initial value 0xFFFF, no reflection of input or output, no final XOR. Reach for a generic "CRC16" library and you will likely get a different variant (there are many) that produces a plausible-looking four-hex-digit value that no PIX app accepts.
The scope is the trap. The CRC is computed over the entire payload string including the CRC field's own ID and length — the literal characters 6 3 0 4 — but excluding the four checksum characters themselves. In other words: build the whole string up to and including 6304, run the CRC over that, and append the result. Compute it over everything-except-6304, or over the value only, and you get a wrong answer that fails silently at the scanner.
The encoding is the last mile: the result is four uppercase hexadecimal characters, zero-padded (so a value of 0x0A1B is written 0A1B, and 0xB5 would be 00B5). Lowercase or unpadded and, again, some readers balk.
crc = 0xFFFF
for each byte b in payload_up_to_and_including("6304"):
crc = crc XOR (b << 8)
repeat 8 times:
if crc & 0x8000: crc = (crc << 1) XOR 0x1021
else: crc = crc << 1
crc = crc & 0xFFFF
result = uppercase_hex(crc, width=4) // append as the value of field 63That is the whole checksum. It is deterministic, it is testable, and — the recurring theme of this series — it is the kind of thing you validate against known vectors and then never compute by hand again.
Static versus dynamic: the one structural fork
Everything above was a static code, and static is the simple case: the PIX key sits in field 26 sub-01, the amount may or may not be fixed, and the same code can be scanned a thousand times. It is a poster on a wall, a sticker on a counter.
A dynamic code changes one thing and it cascades. Instead of the key in sub-01, field 26 carries a URL in sub-25 — a location (without the https:// prefix, which is implied) pointing at the merchant's payment service provider. The point-of-initiation field 01 is set to 12 (dynamic) rather than 11 (static). When the payer's app reads the code, it does not have enough to pay yet; it calls that URL and retrieves a signed payload — a JWS document from the PSP — that contains the actual amount, the real txid, an expiry, and merchant details, cryptographically signed so it cannot be tampered with in transit.
Sit with that for a moment against the rest of the series. A dynamic PIX code is a pointer to a signed, one-time, transaction-specific instruction — which is conceptually the same move as the ARQC (a one-time, transaction-bound, cryptographically-protected assertion) and the same move as the agent-payment mandates from the bridge piece. The industry keeps reaching for the same shape: bind the authorization to this payment, sign it, make it single-use. PIX just does it with a URL and a JWS instead of a session key and a MAC. Different primitives, identical instinct.
The copia-e-cola is the same bytes
One more Brazilian-specific detail that trips up newcomers. PIX is famous for copia-e-cola — "copy and paste" — where instead of showing a QR you share a long string of text in a chat. Engineers sometimes assume copia-e-cola is a separate format. It is not. The copia-e-cola string is exactly the BR Code payload — the same 000201...6304XXXX content that would have been rendered into the QR — handed over as text. The QR is just a visual transport for that string. Decode a copia-e-cola with the same field walk above and it resolves identically, checksum and all. One format, two transports.
Battle scars
The length is characters, not bytes — until it is bytes. For plain ASCII merchant names the two-digit length equals the character count and the byte count, and everyone is happy. Put an accented merchant name or city in there — SÃO PAULO — and UTF-8 makes some characters multiple bytes, and now "length in characters" and "length in bytes" diverge. Get the counting convention wrong and the field boundaries shift, corrupting everything after it. Decide explicitly how your generator counts and test it with non-ASCII merchant data, because Brazil has a lot of it.
A valid CRC on wrong content still fails, quietly. Because the CRC only proves the string is internally consistent, a BR Code with a correct checksum over a malformed payload will pass your checksum check and still be rejected by the paying app for a schema reason. The CRC is necessary, not sufficient. Validate structure and checksum, and do not let a green checkmark on the CRC lull you.
Dynamic codes fail at the URL, not the QR. When a dynamic PIX code "doesn't work," the QR is usually fine and the PSP endpoint behind sub-25 is the problem — an expired payload, a TLS issue, a signature that does not verify. Debug the fetch, not the square.
The series closes where it started
We began by dipping a chip and watching a terminal and a card negotiate a transaction over structured, tagged, length-prefixed data. We are ending with a QR code on a bakery counter — and it is structured, tagged, length-prefixed data with a checksum, following an EMVCo specification, resolvable with the exact skill we built in Part 2. That is not a coincidence. It is the thesis: payments are an encoding discipline before they are anything else, and once you can read the encoding, the whole domain stops being a black box — chip, contactless, ISO 8583, or the QR that a whole country pays with.
When gsstk ships the PIX BR Code Generator and Validator, it will build and check exactly the structure decoded here — including that unforgiving CRC16 over the right scope — client-side, so the payment string never leaves your browser. Read it by hand once to believe it; use the tool so you never miscount a length again.
Related Reading on gsstk
- What Actually Happens When You Dip a Chip: The EMV Transaction Flow End-to-End — Part 1: where this series began, dipping a chip.
- Reading BER-TLV by Hand — and Why You Shouldn't — Part 2: the same ID-length-value skill, on binary templates.
- ARQC from Scratch: Application Cryptograms Without the Black Box — Part 4: the signed, one-time assertion a dynamic QR mirrors.
- Agentic Commerce and the Return of HTTP 402 — the same "signed, single-use" instinct for agent payments.
Tools: browse the EMV tools suite and the EMV Tag Dictionary; the Brazil-specific ABECS pinpad analyzers cover the terminal side. (A client-side PIX BR Code Generator/Validator — with the CRC16 over the right scope — is on the roadmap.)
Filed under: PIX · Payments · EMV · ABECS · Acquiring
This article was human-architected and synthesized with AI assistance under the Nexus (AI) persona.