EMV QR Hub
EMV QR Hub
Developer Resources

EMV QR Testing

Testing is not “it scanned on my phone.” It is a matrix: payload rules plus the apps your customers have. Academy samples are drills, not merchants.

Payload checks

CRC valid. Tag 00 = 01. Tag 01 matches amount policy. Tag 53 numeric. Name/city lengths. MAI GUID is the production value.

Test-case matrix (start here)

Pass — static INR sticker shape: Tag 01=11, numeric Tag 53, CRC ok, no Tag 54.

Fail — Tag 53 letters: currency must be ISO 4217 numeric digits.

Fail — CRC last digit flipped: integrity must fail closed.

Fail — Tag 01=11 with Tag 54 present: sticker + amount mismatch.

Pass — dynamic billed QR: Tag 01=12 with Tag 54 amount.

Fail — MAI length overrun: outer Tag 26 length ≠ inner TLV character count.

Fail — Tag 63 not last: CRC object must terminate the payload.

Record each run with payload hash, decoder version, and expected verdict so regressions are obvious.

App checks

Two local wallets, one bank app. Static amount entry vs dynamic locked amount. Photograph the success screen; do not rely on memory.

Negative tests

Bad CRC, alphabetic currency, 11+54 conflict, truncated city. Each should fail closed.

Tools

Generate here, decode here, scan devices. Scheme cert tools from your acquirer still apply.

Environment matrix

Test on at least one Android and one iOS wallet where both exist in your market. Camera quality changes truncation risk. Brightness changes screen QR contrast. A test that only uses this site’s decoder is incomplete.

Data that must differ per build

GUID, merchant name, and city should be your test merchant, not EMVQRHUB. If two builds share the cookbook sample, you cannot tell whether a wallet cached the first scan.

Regression set

Keep a folder of strings: last known good static, last known good dynamic, last known CRC fail, last known alphabetic Tag 53. Run them after every generator change. That is cheaper than rediscovering Tag 53 bugs in production.

When to involve the acquirer

If CRC, POI, and currency are correct and two independent wallets still refuse, it is almost always membership or AID. Escalate with the decoded MAI, not with a screenshot of the pretty QR.

What a payload-only test cannot prove

A CRC-valid string in Decode only proves the envelope is well-formed. It does not prove that a wallet in your market will open a pay screen, that the GUID is on that wallet’s allow-list, or that the acquirer will post to the intended merchant file. Treat decoder green as gate one. Gate two is at least two live apps. Gate three is a settlement report that matches Tag 59, MCC, and any Tag 62 bill number you sent.

Write those three gates into the test plan so a “QR looks fine” screenshot never closes a ticket. EMVCo publishes the QR specification family and testing programmes on emvco.com; this site is not a substitute for those programmes. We only give you a local encode/decode bench.

Camera, print, and screen as first-class variables

Phone cameras disagree on contrast, glare, and minimum module size. A payload that Decode accepts from a pasted string can still fail when the same payload is a 2 cm sticker under a plastic standee. Print at the size you will deploy, scan from a typical queue distance, then repeat with a second device. Screen QR on a dim POS needs a longer dwell; if cashiers rush the customer, you will collect “app cannot read” tickets that are lighting bugs, not TLV bugs.

Keep a photo of the official print in the operations folder. Overlay fraud and accidental reprints are easier to catch when staff can compare the live standee to a known-good image. Testing is not only CI; it is the physical object the camera will see.

Wallet matrix that actually matches customers

List the apps that together cover most of your tickets — not the apps your engineers prefer. One Android super-app plus one bank iOS wallet is a common minimum. If tourists are material, add one inbound wallet that is known to be picky about GUID. Record app name, OS version, and whether the scan used the in-app camera or a system camera hand-off; those paths parse the same string but surface errors differently.

When two wallets disagree, paste the same string into Decode and export the tag table. If CRC, Tag 01, Tag 53, and MAI look legal, the disagreement is membership or UX, not a reason to rewrite CRC code. Escalate with the raw payload, not with a cropped JPEG of the symbol.

Negative cases that belong in every release

Keep fixtures for: alphabetic Tag 53, Tag 01=11 with Tag 54 present, Tag 01=12 with Tag 54 missing, CRC last nibble flipped, Tag 63 not last, MAI outer length one character too long, and Tag 58 written as a country name. Each fixture should have an expected Decode verdict. If a generator change makes a negative fixture pass, you shipped a silent relaxation.

Do not reuse cookbook merchant names like EMVQRHUB in a certification build. Wallets and reviewers treat placeholder names as a smell, and you cannot tell whether a wallet cached an earlier scan of the same sample. Mint a test merchant whose MCC and city match the file the acquirer expects.

When the lab should stop and call the scheme

If CRC, POI, currency, country, and nested MAI parse cleanly on this site, and two independent production wallets still refuse, further TLV edits will not help. The next artefact is the GUID and AID the scheme registered for that merchant. Send the decoded MAI subtree, the raw string, and the wallet error text. Do not send a regenerated payload that “might work” unless you version it; otherwise the partner debugs a moving target.

Official pass/fail for a national QR programme still comes from that programme’s kit and from EMVCo’s published QR documents, not from emvqrhub.com. Use this academy to isolate grammar defects quickly so you only involve the switch when the grammar is already honest.

Definition of done

Done means: Decode pass, two-wallet pass, stored payload matches print, negative fixtures still fail, and a human signed the ops photo. Anything less is a demo, not a launch.

Pass: static INR sticker shape

POI 11, Tag 53 = 356, no Tag 54, CRC valid, name ≤ 25. Verdict: structurally OK. Still not payable unless GUID is live.

Fail: Tag 53 letters

5303INR. Verdict: non-compliant currency even if CRC was computed after the letters.

Fail: CRC last digit flipped

Verdict: integrity fail. Do not pay.

Fail: 11 with Tag 54

Verdict: POI/amount conflict. Fix before print.

Pass: dynamic billed QR

POI 12, Tag 54 present, Tag 53 numeric, CRC valid. Decoder should show a locked amount equal to Tag 54. If the amount on the pay screen differs, the wallet is rounding or ignoring decimals — file that as an app issue, not a CRC issue.

Include Tag 62 bill number when your recon process needs it. Validation still passes if 62 is omitted; operations may fail later.

Fail: length overrun after MAI

If the outer MAI length is larger than the inner TLV, the next root tag is eaten as residual account data. Symptom: merchant name looks like a fragment of a GUID. Fix the outer length; do not rename the merchant.

Fail: Tag 63 not last

Any object after CRC is unspecified for MPM. Treat as malformed even if a naive CRC over a prefix still matches. Regenerators should always append 63 as the final root tag.

How to record a validation run

Save the raw string, decoder screenshot of the tag table, wallet name and version, and pass/fail. Academy cookbook strings must be labelled illustrative so nobody pastes them onto a door.

Official pass criteria for a scheme still come from that scheme’s cert kit, not from this page.

Pass: static INR sticker without amount

Expect Tag 00=01, Tag 01=11, one MAI, Tag 52 four digits, Tag 53=356, Tag 58=IN, Tag 59 and 60 within length caps, Tag 63 last with a CRC that Decode marks valid, and no Tag 54. If your generator emits 54 on this shape, fail the build even if CRC matches. Static means the customer types the amount in the wallet.

Label every Academy sample illustrative. The EMVQRHUB / DELHI strings are for layout drills. A reviewer or a wallet risk engine that sees those names on a live counter should treat them as placeholders, not as a merchant file.

Fail: alphabetic currency and why CRC can still match

Tag 53=INR with length 03 is a well-formed TLV object and will checksum if you compute CRC after the mistake. Validation must include a semantic check: Tag 53 is three digits, not three letters. Decode on this site flags that class of error because many wallets refuse to charge. Do not “fix” it by padding INR to 356 in a support script without regenerating CRC and retesting wallets.

Pair this with the ISO 4217 article. Numeric 356, 764, 702, 840 are the usual examples; the rule is the numeric code, not the alphabetic code you type in accounting software.

Fail: POI 11 with Tag 54, and the opposite

A static method with a locked amount is undefined behaviour across wallets: some hide the keypad, some ignore 54, some refuse. Treat 11+54 as a fail in your own validator even if a particular app is lenient. The inverse (12 without 54) is also a fail for billed QR: the customer should not be typing the ticket total.

Record both as named fixtures. After every generator release, run them. See Point of Initiation for why Tag 01 is a method bit, not a marketing label, and Static vs Dynamic for the operations choice.

Fail: CRC nibble flip versus length overrun

Flipping the last hex character of Tag 63 should yield CRC invalid with a still-readable tag table. That is an integrity fail. A length overrun after MAI often yields a garbage tree and a CRC that may or may not match depending on how far you compared. Do not lump both under “QR broken.” Ticket them as checksum versus framing so the right engineer looks.

Tag 63 not last is its own fail: extra objects after CRC are unspecified for MPM. A naive CRC over a prefix can still match. Regenerators must append 63 as the final root tag every time.

How to archive a run so it is reproducible

Save the raw payload, the Decode tag table (or a screenshot plus the string), wallet name and version, pass/fail, and the date. If you only keep the pretty QR image, JPEG compression and crop make the ticket useless. If you only keep “failed in PhonePe,” you cannot tell CRC from GUID from camera glare.

Official scheme pass criteria still come from that scheme’s certification kit and from EMVCo’s QR documents on emvco.com. This page is a cookbook of shapes we use when teaching the decoder — not a cert lab.

Teaching order for new hires

Start with pass static, then fail alphabetic Tag 53, then fail CRC nibble, then fail 11+54. Only then show dynamic pass. That order mirrors how often each defect appears in Decode tickets. End with “record the string.”

Automating verdicts

Script pass/fail against the cookbook strings: assert Tag 53.isdigit(), assert not (poi==11 and has54), assert crc_valid, assert tag63_is_last. Human-readable pages and machine fixtures can share the same strings. Keep them in git beside the generator.

Fixture hygiene

Refresh CRC when sample bytes change; never commit a pass fixture with EMVQRHUB on a production branch tag.

Explore Related Guides

EMV QR Generator
Create merchant-presented EMV QR payment payloads.
QR Parser & Decoder
Decode QR payloads and inspect EMV tags.
Legal Disclaimer:EMV QR Hub is a technical utility. We do not process financial transactions or store sensitive payment data. Not affiliated with EMVCo, LLC.