ZATCA Simplified Invoice QR TLV Base64 Python Generator
Saudi invoices carry a QR code whose payload is a Base64 string of tag-length-value fields. This generator writes the Python that builds that string from your seller and invoice data, so you can test it or adapt it in a print or middleware step.
Fill in
15 digits, starting and ending with 3 for Saudi VAT numbers.
Python script
import base64
def tlv(tag, value):
data = value.encode("utf-8")
if len(data) > 255:
raise ValueError("field too long for single byte length")
return bytes([tag, len(data)]) + data
payload = b"".join([
tlv(1, "Al Noor Industrial Co"),
tlv(2, "300000000000003"),
tlv(3, "2026-10-20T10:30:00Z"),
tlv(4, "1150.00"),
tlv(5, "150.00"),
])
qr_text = base64.b64encode(payload).decode("ascii")
print(qr_text)Tag map used above
Tag 1: seller name Tag 2: seller VAT registration number Tag 3: invoice date and time Tag 4: invoice total including VAT Tag 5: VAT amount Each field is encoded as one byte tag, one byte length, then the UTF-8 value. The joined bytes are Base64 encoded and that string is what the QR image contains.
This covers the basic five tag structure used for the Saudi QR code. Phase 2 invoices add further tags (such as invoice hash, signature and public key) produced by the cryptographic stamp, which this script does not create. Check the current ZATCA technical specification and your solution provider's requirements before relying on it for live invoices.
Plan a ZATCA integration around your ERP
Optional: a short walk-through of how invoice data can flow from your ERP to the e-invoicing layer.
Optional. The tool and every export work without this. No spam.
How to use the generator
Fill in the seller name, VAT number, the invoice timestamp, the total including VAT and the VAT amount. Copy the script and run it with any Python 3 interpreter. It prints the Base64 text. Feed that text to a QR library or to your report writer's QR barcode element, rather than printing it as visible text. Because the length byte is a single byte, each field must stay under 256 bytes, which matters for long Arabic seller names since Arabic characters take two bytes each in UTF-8.
- Use a full ISO 8601 timestamp with a time component.
- Format amounts with two decimals as they appear on the invoice.
- Test the result by scanning it with a QR reader that decodes Base64.
The TLV method
TLV stands for tag, length, value. Each of the basic fields is written as one byte for the tag number, one byte for the number of value bytes, then the value itself in UTF-8. All the fields are concatenated in tag order and the whole byte sequence is Base64 encoded. A reader reverses the steps. If the encoded length does not equal the value byte count, the reader will mis-parse every later field, so counting bytes rather than characters is the point most implementations get wrong.
Where this fits and what it does not do
The simplified tax invoice used for business to consumer sales carries this QR code. Under the Fatoora integration phase the invoice is also generated as structured XML, cryptographically stamped and reported or cleared through ZATCA, and that stamped invoice carries additional QR tags. This script is useful for understanding the format, for testing a print layout and for prototyping, but a compliant production flow needs the full solution described in ZATCA's technical documents. Plan for the integration with your ERP's invoice posting and for storing the generated XML.
- Confirm which fields your ERP localisation already produces.
- Do not hard code test VAT numbers into production jobs.
- Retain the QR payload with the invoice record for audit.
Frequently Asked Questions
What is TLV in the ZATCA QR code?
TLV means tag, length, value. Each field is stored as a one byte tag, a one byte length and the value bytes. The fields are joined and the whole result is Base64 encoded to form the QR text.
Why is my QR code failing validation?
The usual causes are counting characters instead of UTF-8 bytes for the length, a wrong timestamp format, amounts that do not match the printed invoice, or fields in the wrong order. Decode your Base64 and read each tag in turn to find the mismatch.
Does this replace a ZATCA compliant solution?
No. It only builds the basic QR payload. A compliant solution also produces the structured invoice, applies the cryptographic stamp and handles reporting or clearing with ZATCA as required for your invoice type.
Do Arabic seller names work?
Yes, if encoded as UTF-8 and the length byte uses the byte count. Arabic letters use two bytes each, so a name of 130 characters would exceed the 255 byte limit and must be shortened to the registered form.
If you need ZATCA invoice data pulled out of your ERP into a clean, testable middleware layer, we can scope that with your finance and IT teams.
Related Tools
Saudi Arabia VAT Tax Invoice and Credit Note Template
Fill-in layout for a standard Saudi tax invoice or credit note with seller and buyer VAT numbers, SAR line totals at 15% and a QR code placeholder.
Country & TaxSaudi Withholding Tax Gross-Up Calculator
Calculates the gross payment, withholding tax due to ZATCA and VAT reverse charge when a Saudi company agrees to pay a non-resident a fixed net amount.
Country & TaxGCC Import Landed Cost Calculator with Duty and VAT
Calculates CIF value, customs duty, import VAT and landed cost for parts and equipment brought into a GCC country, with a duty rate sweep.
Country & TaxSaudi SABER Product and Shipment Certificate Checklist
A checklist covering the SABER steps manufacturers and importers follow for regulated products entering Saudi Arabia.
More Country & Tax tools
See all- Saudi ZATCA E-Invoicing Phase 2 Integration Checklist
- Tally Prime Manufacturing Journal Checklist
- Tally Stock Item Import Template (Excel)
- Busy Accounting GST Setup Checklist
- GST Tax Invoice Format Template for Indian Manufacturers
- Delivery Challan Format Template for Goods Movement in India
- Purchase Order Format Template for Indian Manufacturers
- Job Work Challan Register and ITC-04 Worksheet
- TDS Deduction Tracker Worksheet for Vendor Payments