TLV Encoding

As per Zakat, Tax and Customs Authority (ZATCA) of Saudi Arabia, one of the main requirements is the implementation of QR codes on tax invoices in the e-invoicing project (Fatoora), which will be mandatory starting December 4, 2021

As per the ZATCA instructions(Page No. 23), the minimum requirements that must be shown after scanning a QR code are the following fields, which should be represented in form of based64 encoding:

  1. Seller’s name.
  2. VAT registration number of the seller.
  3. Time stamp of the invoice (date and time).
  4. Invoice total (with VAT).
  5. VAT total.

We usually do encoding for transmitting a message and for this here we encode the message as Tag Length Value (TLV).

In this article, you can see the details of the TLV Encoding Algorithm for the QR Code requirement of ZATCA. We are implementing this using the N/Encoding module of the suite script along with some native JS functions.

1st Step – is to prepare each of the five values in TLV (Tag-Length-Value) structure

TAG is fixed (1 for Seller’s name, 2 for VAT No……5 for VAT Total). This represented as 1 byte like 01,02…05 when adding to the byte array(dealt in 2nd step – hexadecimal conversion)

LENGTH is the size of the value field in bytes (IMP: it’s not the count of characters but how many bytes the value represents)

VALUE is the data against each of the five fields.

Tag” and “Length” are used as the identifier

Let’s take an example to clarify TLV

  1. Seller name; for example, “Bobs Records
    • Tag      = 1 (1 as a type represents the seller name)
    • Length = 12 (The number of the bytes in “Firoz Ashraf” word)
    • Value   = Bobs Records
  2. VAT Number; for example, 310122393500003
    • Tag      = 2 (2 as a type represents the VAT number)
    • Length = 15
    • Value   = 310122393500003
  3. Time Stamp; for example, 2022-04-25T15:30:00Z 
    • Tag      = 3 (3 as a type represents invoice time stamp)
    • Length = 20
    • Value   = 2022-04-25T15:30:00Z
  4. Invoice Total; for example, 100.00
    • Tag      = 4 (4 as a type represents the invoice amount)
    • Length = 7
    • Value   = 1000.00
  5. VAT Total; for example, 15.00
    • Tag      = 5 (5 as a type represents the tax amount)
    • Length = 6
    • Value   = 150.00

2nd Step – is to convert TLV to Hexadecimal and concatenate all the 5 encoded packets.

  1. For Tag – Decimal to Hexadecimal Conversion (should be stored in 1 byte eg: 01 for 1, 02 for 2 etc.)
  2. For value – Decimal to Hexadecimal Conversion
  3. For Value – Text to Hexadecimal Conversion
//Text to Hexadecimal Conversion using encoder module
encode.convert( {
                    string: params,
                    inputEncoding: encode.Encoding.UTF_8,
                    outputEncoding: encode.Encoding.HEX
                } );
//For Decimal number to Hexadecimal Conversion
var Length = length.toString( 16 )

IMPORTANT:
If the Seller name is in Arabic the use UTF-8 Encoding and the Length of the value is not the length of the direct text string in Arabic. It should be the UTF-8 encoded byte array length ie. First, convert the Arabic text to Hexadecimal, and then the length = half of the hexadecimal string.

Concatenation
1. Firstly do this => hexadecimal(Tag) + hexadecimal(Length) + hexadecimal(Tag)

2. Concatenate all the five TLVs into one string

3rd Step is to convert the concatenated string to Base64 format

From the above example, we get the following Base64 encoded value

Leave a comment

Your email address will not be published. Required fields are marked *