Phase 1 vs Phase 2 QR Codes: The Cryptographic Leap
In Phase 1 (enforced in December 2021), anyone with basic graphic design software could forge a QR code containing fake company names and VAT amounts because Phase 1 QR codes contained plain text without digital signature validation.
Under Phase 2, ZATCA introduced cryptographic binding. An authentic QR code can only be generated by software possessing a valid private key linked to an authorized ZATCA Production CSID:
Extract Invoice Data
Gather seller name, 15-digit TRN, ISO 8601 timestamp, and total amounts
Canonicalize & Hash
Generate SHA-256 hash digest of the canonicalized UBL 2.1 invoice XML (Tag 6)
ECDSA Digital Signature
Sign the invoice hash with the EGS private key using secp256k1 curve (Tag 7)
Pack Binary TLV Buffer
Concatenate all 9 fields using Tag-Length-Value byte serialization
Base64 Encoding
Convert complete binary TLV buffer into a single Base64 encoded string
Render 2D Barcode
Render Base64 string as high-contrast QR code on thermal slips or digital PDFs
The 9 Mandatory TLV Tags: Complete Specification
The following technical matrix defines the exact byte layout, tag numbers, and data types required for Phase 2 compliance:
| Tag # | Description | Technical Data Type | Max Length (Bytes) | Source / Description |
|---|---|---|---|---|
| 1 | Seller Legal Name | UTF-8 String | Up to 255 | Sponsoring company legal name matching Commercial Registration. |
| 2 | Seller 15-Digit VAT TRN | ASCII String | Exactly 15 | Tax Registration Number starting with 3 and ending with 3. |
| 3 | Invoice Timestamp | ASCII (ISO 8601) | Exactly 19 or 25 | Format: YYYY-MM-DDTHH:MM:SS (Saudi local time). |
| 4 | Invoice Total (With VAT) | ASCII Numeric | Variable | Total payable amount rounded to 2 decimal places (e.g., 1150.00). |
| 5 | Total VAT Amount | ASCII Numeric | Variable | Total VAT amount rounded to 2 decimal places (e.g., 150.00). |
| 6 | Invoice XML Digest | Binary / Hex | Exactly 32 | SHA-256 cryptographic hash of the canonicalized invoice XML. |
| 7 | ECDSA Digital Signature | ASN.1 / Binary | ~70 to 72 | Digital signature of the invoice digest signed by EGS private key. |
| 8 | ECDSA Public Key | Binary (DER) | Exactly 65 or 88 | Public key bytes extracted from the active EGS Production CSID. |
| 9 | Certificate Signature | Binary | ~70 to 72 | Cryptographic signature of ZATCA's Trust Sub-CA that signed the CSID. |
Understanding Tag-Length-Value (TLV) Binary Encoding
TLV is a binary encoding scheme where data is stored in self-describing segments:
0x01 for Tag 1).Visualizing the TLV Byte Structure
For Tag 1 containing company name "ACME":
Tag = 0x01 (1 byte)Length = 0x04 (4 bytes)Value = 0x41 0x43 0x4D 0x45 (ASCII representation of "ACME")01 04 41 43 4D 45Working Code Implementations: TLV Packing & QR Generation
Python Implementation (Reference Script)
The following Python script illustrates how to serialize the TLV buffer and produce the compliant Base64 string:
import base64
from typing import List, Tuple
def create_tlv_tag(tag_num: int, value: bytes) -> bytes:
"""Encodes a single tag, length, and value into compliant TLV bytes."""
tag_byte = bytes([tag_num])
length = len(value)
if length < 128:
length_bytes = bytes([length])
else:
# Multi-byte length encoding for large values (like public keys and signatures)
length_bytes = bytes([0x81, length]) if length < 256 else bytes([0x82, (length >> 8) & 0xFF, length & 0xFF])
return tag_byte + length_bytes + value
def generate_zatca_phase2_qr(
seller_name: str,
vat_number: str,
timestamp: str,
total_with_vat: str,
vat_total: str,
invoice_hash: bytes, # 32 bytes SHA-256
ecdsa_signature: bytes, # Raw ASN.1 ECDSA signature
public_key: bytes, # Raw DER public key
cert_signature: bytes # ZATCA CA signature
) -> str:
"""Concatenates all 9 tags and returns Base64 encoded string."""
tlv_buffer = bytearray()
# Tags 1 to 5 (String UTF-8 values)
tlv_buffer.extend(create_tlv_tag(1, seller_name.encode('utf-8')))
tlv_buffer.extend(create_tlv_tag(2, vat_number.encode('ascii')))
tlv_buffer.extend(create_tlv_tag(3, timestamp.encode('ascii')))
tlv_buffer.extend(create_tlv_tag(4, total_with_vat.encode('ascii')))
tlv_buffer.extend(create_tlv_tag(5, vat_total.encode('ascii')))
# Tags 6 to 9 (Cryptographic binary elements)
tlv_buffer.extend(create_tlv_tag(6, invoice_hash))
tlv_buffer.extend(create_tlv_tag(7, ecdsa_signature))
tlv_buffer.extend(create_tlv_tag(8, public_key))
tlv_buffer.extend(create_tlv_tag(9, cert_signature))
# Final Base64 encoding
return base64.b64encode(tlv_buffer).decode('ascii')Physical Printing & Scanning Requirements
Generating the Base64 string is only half the equation; the physical barcode must be readable by consumer smartphones in retail settings:
Statutory Safeguards & Rights Protection
Mandatory protections enforced by MHRSD & Saudi Labor Law
Testing & Verification with the Official ZATCA Mobile App
Taxpayers and developers can verify their QR codes using the official government inspection tool:
- Taxpayer Name & Valid VAT Number
- Exact Transaction Timestamp
- Invoice Financial Breakdown
- Verified Cryptographic Signature Match
Frequently Asked Questions (FAQ)
Can I include custom business text or URLs inside the ZATCA QR code?
No. Adding extra non-standard tags, website links, or promotional text inside the ZATCA QR code byte buffer violates Schematron business rules. The QR code must contain exclusively the authorized TLV tags mandated by ZATCA specifications.
Why is the Phase 2 QR code significantly denser than Phase 1?
Phase 1 QR codes contained approximately 80 to 120 bytes of plain text. Phase 2 QR codes incorporate cryptographic signatures, hashes, and public keys, resulting in a payload of 500 to 700 bytes. This increased payload produces a visibly denser 2D matrix.
Is the QR code mandatory on B2B Standard Tax Invoices?
Under Phase 2 rules, the QR code is mandatory inside the UBL 2.1 XML document for both B2B and B2C invoices. On printed or visual PDF representations, the QR code is mandatory on Simplified (B2C) invoices, but optional on human-readable Standard (B2B) invoices.
What happens if the thermal printer cuts off part of the QR code?
If a thermal printer clips the QR code or prints with low contrast, the invoice is treated as a defective receipt under VAT regulations. Sponsoring establishments are subject to statutory fines starting at SAR 1,000 for issuing illegible electronic receipts.
Does the QR code timestamp need to include timezone offsets?
No. The timestamp format must follow YYYY-MM-DDTHH:MM:SS in local Saudi Arabia standard time (GMT+3) without trailing timezone characters like Z or +03:00.
Was this compliance guide helpful?
Your feedback helps our legal editorial team maintain authoritative KSA guidance.
Advocate High Court (Punjab Bar Council) and ATT GCC VAT Certified Specialist. Author of comprehensive regulatory commentaries on Saudi corporate law, ZATCA e-invoicing protocols, and Labor Reform Initiatives.