DEV Community

mo osman
mo osman

Posted on Originally published at qeemahcloud.com

ZATCA QR Code Requirements & TLV Encoding: A Technical Implementation Guide

In the Saudi Arabian business landscape, the QR code on an e-invoice is far more than a simple URL shortcut. It is a strictly encoded, cryptographic data structure mandated by the Zakat, Tax and Customs Authority (ZATCA). Whether you are in Phase 1 (Generation) or Phase 2 (Integration), ensuring your QR code complies with the TLV (Tag-Length-Value) encoding standard is critical to avoiding fines and invoice rejections.

This guide provides a comprehensive breakdown of the technical requirements, the underlying TLV binary structure, and a complete TypeScript/Node.js implementation.


1. What is TLV Encoding?

ZATCA requires QR code data to be formatted using the TLV (Tag-Length-Value) method before being converted into a Base64 string. This ensures that the data is compact, deterministic, and easily readable by the Fatoora simulation and production environments.

The Anatomy of TLV

  1. Tag: A 1-byte identifier for the data field (e.g., Tag 1 for Seller Name).
  2. Length: The length (in bytes) of the value field.
  3. Value: The raw UTF-8 byte representation of the data.

Required Fields for Phase 1 (Simplified & Standard Invoices)

For Phase 1, the QR code must contain the following five tags:

Tag Field Name Description Example
1 Seller Name Legal entity name as registered with ZATCA Qeemah Software
2 VAT Number 15-digit VAT registration number 300000000000003
3 Timestamp Invoice issuance timestamp (ISO 8601 UTC/Z) 2026-10-23T06:00:00Z
4 Invoice Total Total invoice amount including VAT 1150.00
5 VAT Total Total VAT amount charged 150.00

2. Phase 2: Cryptographic Extensions

In Phase 2 (Integration Phase), the QR code for Simplified Tax Invoices (B2C) becomes a cryptographic proof of document integrity.

In addition to Tags 1 through 5, Phase 2 mandates four additional cryptographic tags:

  • Tag 6 (Invoice Hash): The SHA-256 hash of the sanitized invoice XML.
  • Tag 7 (Digital Signature): An ECDSA digital signature (secp256k1) generated using the private key associated with your Cryptographic Stamp (CSID).
  • Tag 8 (Public Key): The ECDSA public key used by ZATCA to verify the signature.
  • Tag 9 (Certificate Signature): For simplified invoices, the digital signature of the CSID certificate itself.

3. Step-by-Step Implementation in Node.js / TypeScript

Here is a clean, production-ready utility to generate compliant ZATCA TLV Base64 QR strings:

import { Buffer } from "node:buffer";

interface ZATCAQRData {
  sellerName: string;
  vatNumber: string;
  timestamp: string;
  invoiceTotal: string;
  vatTotal: string;
  invoiceHash?: string;
  signature?: string;
  publicKey?: string;
  certificateSignature?: string;
}

function encodeTLVField(tag: number, value: string | Buffer): Buffer {
  const valBuf = Buffer.isBuffer(value) ? value : Buffer.from(value, "utf8");
  const tagBuf = Buffer.from([tag]);
  const lenBuf = Buffer.from([valBuf.length]);
  return Buffer.concat([tagBuf, lenBuf, valBuf]);
}

export function generateZATCAQRCode(data: ZATCAQRData): string {
  const buffers: Buffer[] = [
    encodeTLVField(1, data.sellerName),
    encodeTLVField(2, data.vatNumber),
    encodeTLVField(3, data.timestamp),
    encodeTLVField(4, data.invoiceTotal),
    encodeTLVField(5, data.vatTotal),
  ];

  if (data.invoiceHash) {
    buffers.push(encodeTLVField(6, data.invoiceHash));
  }
  if (data.signature) {
    buffers.push(encodeTLVField(7, data.signature));
  }
  if (data.publicKey) {
    buffers.push(encodeTLVField(8, data.publicKey));
  }
  if (data.certificateSignature) {
    buffers.push(encodeTLVField(9, data.certificateSignature));
  }

  const combined = Buffer.concat(buffers);
  return combined.toString("base64");
}
Enter fullscreen mode Exit fullscreen mode

4. Common Validation Pitfalls

When submitting to the Fatoora portal, common rejection errors include:

  • BR-KSA-26 / BR-KSA-27: The QR code does not conform to the TLV structure or is missing mandatory tags.
  • Timestamp Mismatch: The timestamp in Tag 3 must match the cbc:IssueDate and cbc:IssueTime XML elements exactly to the second.
  • Character Encoding: Seller names with Arabic characters must be encoded strictly in UTF-8 to ensure byte-length calculations match the TLV length header.

Conclusion & Resources

Building compliant e-invoicing pipelines requires strict adherence to cryptographic standards and binary data structures.

If you are developing or managing business infrastructure in Saudi Arabia, Qeemah provides a modern, fully compliant cloud ERP and accounting platform with native ZATCA Phase 2 clearance and automated cryptographic stamping.

Original article published on Qeemah Cloud Accounting.

Top comments (0)