CRC Validation in EMV QR
Tag 63 is a four-hex-digit CRC-16 over the payload up to and including the 6304 prefix. It catches accidental edits. It does not prove the merchant is who they claim.
What is covered
Compute CRC on everything before the checksum value, including the characters 6304. Append the four hex digits. If you later change city, amount, or GUID, recompute. Copying a CRC from another QR always fails unless the earlier bytes are identical.
Failure modes
UTF-8 vs ASCII confusion, stripping a trailing field, using CRC-16/ARC instead of the EMVCo-prescribed variant, or placing Tag 63 in the middle of the string.
Lab drill
Decode a known sample, confirm valid CRC, mutate one letter of the merchant name, recompute or leave the old CRC, decode again. Valid then invalid is the expected pair.
Security reminder
Attackers compute CRC too. Combine CRC checks with merchant-name checks and scheme GUIDs. See QR security best practices.
Why four hex digits
Tag 63 length 04 is the conventional CRC hex encoding in MPM. If you output six digits or a decimal integer, wallets will not treat it as CRC. If you lowercase versus uppercase, most parsers accept both; pick one in your generator and stick to it.
Implementation checklist
Use the polynomial and initialization EMVCo specifies for QR. Feed ASCII bytes of the payload through 6304. Do not UTF-8 encode a string that is already ASCII. Unit-test against a payload whose CRC you computed independently twice.
False confidence
A matching CRC on a payload that pays the attacker is still a matching CRC. Combine with GUID allow-lists in wallets and with staff checks on stickers. See the security article.
Using Decode as an oracle
When you change CRC code, generate ten random legal payloads in the studio and confirm this site still says valid. Then break one field and confirm invalid. That is your regression pair.
What Tag 63 actually covers
CRC in MPM is computed over the payload through the 6304 prefix and does not include the four hex checksum characters themselves. Tag 63 is last. Length is 04. If you checksum a truncated prefix, if you include the hex digits in the input, or if you compute over UTF-16 code units, you will get a tidy four hex characters that no wallet will accept.
Use the polynomial and initialization EMVCo specifies for QR (see QRCPS on emvco.com). Do not copy a CRC-16 flavour from an unrelated protocol because the name matches. Unit-test against a payload whose CRC you computed twice independently — once in your library, once in Decode, optionally once in a third language.
Worked discipline: edit, then recompute, never copy
If you change Tag 59 from EMVQRHUB to a real fascia name, every later byte stays the same only if lengths did not shift. Tag 59’s length digit changes when the name length changes, so CRC must be recomputed. Copying A13F from a sample after editing the name is the most common “it worked in the blog” failure we see in Decode.
Hex case: most parsers accept uppercase or lowercase. Pick one in the generator (this studio emits a consistent case) and stick to it so logs and golden files diff cleanly. Do not emit six digits, a decimal integer, or a Base64 blob in Tag 63.
False confidence: integrity is not identity
A matching CRC on a payload that pays an attacker is still a matching CRC. Overlay stickers are valid TLV with valid checksums. Combine CRC with GUID allow-lists in wallets, staff checks on standees, and customer checks of Tag 59. See the security article; do not tell finance that CRC-valid means “safe.”
Multiple Tag 63 objects, or any object after CRC, should fail validation. Some broken concatenators append 6304 twice. Parsers disagree. Always one CRC at the end.
Using Decode as a regression oracle
After you touch CRC code, generate ten legal payloads in the EMV studio and confirm this site still says valid. Then flip one nibble and confirm invalid. That pair is cheaper than rediscovering a polynomial bug in production. Keep the payloads in git; do not rely on a screenshot of a green badge.
If Decode and your library disagree, dump the exact ASCII bytes you hashed. Invisible spaces, a missing 6304, or hashing only through Tag 60 are the usual diffs. We are not the spec; QRCPS on emvco.com is.
Where CRC sits in the payment flow
Wallets check CRC after scan and before authorization. A CRC fail never reaches the issuer. A GUID or account fail may reach a host that then declines. Knowing which step failed tells you whether to call the generator engineer, the scheme, or the acquirer. Mixing those queues is how QR incidents last a week.
This site’s decoder reports parse errors and CRC separately so you do not treat a length overrun as a checksum bug. Use that split in your own logs.
Cross-language golden vectors
Keep one payload and its expected CRC in your repo with implementations in the languages you ship (Kotlin, Swift, TypeScript, etc.). When a mobile release changes string encoding, the vectors fail before production. Decode is a convenient third opinion, not a replacement for checked-in vectors sourced from EMVCo’s algorithm description.
Performance note
CRC on a 200-character payload is trivial on phones. Still run it after full parse, not on every partial prefix during typing. UX that shows green CRC before mandatory tags exist trains merchants to ship broken strings.
