Skip to content

Spec coverage

Following the Bank of Thailand / Thai Bankers’ Association / KASIKORN BANK Thai QR Payment supplement layered on top of EMVCo Merchant-Presented-Mode QR Code Specification v1.1:

TagFieldStatusNotes
00Payload Format Indicatoralways 01
01Point of Initiation11 static / 12 dynamic, auto-flipped on amount presence
29Merchant Account Info — PromptPayGUID …0111 (standard), …0114 (OTA); shared by TrueMoney Wallet QR
30Merchant Account Info — BillPaymentGUID …0112 (domestic), …012006 (cross-border)
31Merchant Account Info — KShopreservedconstant exported; no builder helper yet
52Merchant Category Code4-digit ISO 18245
53Transaction Currencyalways 764 (THB)
54Transaction Amountup to 9,999,999,999.99 — 2 decimals — satang input also supported
55Tip / Convenience Indicator01 prompt / 02 fixed / 03 percentage
56Tip FixedTHB
57Tip Percentage2 decimals
58Country Codealways TH
59Merchant Nameup to 25 chars (auto-truncated)
60Merchant Cityup to 15 chars
61Postal Codeup to 10 chars
62Additional Data Field Templateall 9 sub-fields
63CRC-16/CCITT-FALSEpoly 0x1021, init 0xFFFF, no reflect, no XOR out
64Merchant Information Language Templatenot implementedopen issue if you need it
80VAT TQRC (BoT tax-qualified extension)3 sub-fields — promotes a payment QR to an e-tax-receipt source
81Personal messageUTF-16BE-as-hex; carried by TrueMoney Wallet QR

PromptPay merchant template (under tag 29)

Section titled “PromptPay merchant template (under tag 29)”

Sub-tag 00 carries one of two AIDs:

  • A000000677010111 — standard PromptPay credit transfer
  • A000000677010114 — PromptPay credit transfer with One-Time Authorization (OTA)

The OTA AID is auto-selected when .ota(code) is set on the builder. Scanners route the payload through the single-use flow.

Sub-tagFieldStatus
00GUID — A000000677010111 or A000000677010114 (OTA)
01Mobile Number — 13 chars 0066xxxxxxxxxx
02National ID — 13 chars
03e-Wallet ID — 15 chars
04Bank Account — 3-digit bank code + account no, 1–43 chars
05OTA code — fixed 10 chars

Shares the PromptPay AID (A000000677010111) but the e-wallet sub-tag 03 carries a 14-prefixed 15-char identifier (14 + 13-char zero-padded mobile). The literal 14 prefix is the sole disambiguator at parse time. The optional personal message rides on tag 81 as UTF-16BE-encoded hex.

BillPayment merchant template (under tag 30)

Section titled “BillPayment merchant template (under tag 30)”

Sub-tag 00 carries one of two AIDs:

  • A000000677010112 — domestic cross-bank bill payment
  • A000000677012006 — cross-border bill payment (ASEAN PayNow / DuitNow / QRIS interop)

When the cross-border AID is set, the additional-data purposeOfTransaction (tag 62 sub-tag 08) carries an opaque 18-char triple — currencyCode (3) + localAmount (13) + countryCode (2). The builder treats it as a raw string; compose / parse at the call site.

Sub-tagFieldStatus
00GUID — A000000677010112 or A000000677012006 (cross-border)
01Biller ID
02Reference 1
03Reference 2

Bank of Thailand tax-qualified extension. Promotes the surrounding payment QR to an e-tax-receipt source. Sub-tag order on the wire is 00 → 01 → 02.

Sub-tagFieldNotes
00sellerTaxBranchIdexactly 4 chars
01vatRate1–5 chars (optional)
02vatAmount1–13 chars

Additional Data Field Template (under tag 62)

Section titled “Additional Data Field Template (under tag 62)”
Sub-tagFieldBuilder method
01Bill NumberadditionalData({ billNumber })
02Mobile NumberadditionalData({ mobileNumber })
03Store LabeladditionalData({ storeLabel })
04Loyalty NumberadditionalData({ loyaltyNumber })
05Reference LabeladditionalData({ referenceLabel })
06Customer LabeladditionalData({ customerLabel })
07Terminal LabeladditionalData({ terminalLabel })
08Purpose of TransactionadditionalData({ purposeOfTransaction })
09Additional Consumer Data RequestadditionalData({ consumerDataRequest })
AspectStatus
Versions 1-40✓ all
Error correction L / M / Q / H✓ all
Encoding modes (numeric / alphanumeric / byte)✓ all + auto-detect
Reed-Solomon over GF(2^8) with primitive 0x11D
Mask patterns 0-7 + penalty scoring
Format info (BCH 15,5)
Version info (BCH 18,6) for v7+
Alignment patterns per Annex E✓ pinned via regression tests after v0.1.0 bug
Quiet zoneconfigurable (default 4 modules)

These wire formats share no tag space with the payment QR — different envelopes, different CRC tags (or none). They live in @thai-qr-payment/payload alongside the main TLV machinery.

Printed on bank transfer slips. Resolves a transaction via bank Open APIs after slip OCR. Same EMVCo TLV grammar, different root tags.

TagFieldNotes
00Root template (nested)carries sub-fields 00 API type, 01 sending bank, 02 transRef
51Country codealways TH
91CRC-16/CCITT-FALSEsame algorithm as tag 63; positioned at tag 91 here

A TrueMoney variant uses a different sub-field layout under tag 00 and emits a lowercase hex CRC. See Slip Verify for the builder / parser API.

Counter-payment barcode scanned at bank tellers and 7-Eleven cashier stations. Not EMVCo TLV — a |-prefixed, \r-delimited ASCII string with no CRC.

|<billerId>\r<ref1>\r<ref2>\r<amount>
  • billerId — 15 chars (Tax ID + suffix), zero-padded on emit
  • ref1 — mandatory customer / invoice reference
  • ref2 — empty string when unused
  • amount — integer satang, or literal 0 when the cashier keys it in

See BOT barcode for the builder / parser API.