Java QR Code in Base64 encoding for KSA E-Invoicing

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 was 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.

In this blog,  I will show how to encode the QR data in base64 format using java programming language to print QR code on Invoice layouts.

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)

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

Value is the data against each of the five fields.

Let’s take an example to clarify TLV

  1. Seller name; for example, “Arturo TT lecciones info
    • Tag      = 1 (1 as a type represents the seller name)
    • Length = 9 (The number of the bytes in “Arturo TT” word)
    • Value   = Arturo TT
  2. VAT Number; for example, 301203063800003
    • Tag      = 2 (2 as a type represents the VAT number)
    • Length = 10
    • Value   = 301203063800003
  3. Time Stamp; for example, 2021-12-25T15:10:09Z
    • Tag      = 3 (3 as a type represents invoice time stamp)
    • Length = 20
    • Value   = 2021-12-25T15:10:09Z
  4. Invoice Total; for example, 100.00
    • Tag      = 4 (4 as a type represents the invoice amount)
    • Length = 6
    • Value   = 100.00
  5. VAT Total; for example, 15.00
    • Tag      = 5 (5 as a type represents the tax amount)
    • Length = 5
    • Value   = 15.00

2nd Step is to convert ‘Tag’ and ‘Length’ to Hexadecimal and then to string. Then concatenate these two strings with ‘Value’ (stored as string)

concatenate all the five TLVs into one string

‘##Firoz Ashraf##1234567891##2021-11-17 08:30:00##115.00##15.00’

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

From the above example we get the following Base64 encoded value

ARhBcnR1cm8gVFQgbGVjY2lvbmVzIGluZm8CDzMwMTIwMzA2MzgwMDAwMwMUMjAyMS0xMi0yNVQxNToxMDowOVoEBjEwMC4wMAUFMTUuMDA=

String v1 = "Arturo TT lecciones info";
String v2 = "301203063800003";
String v3 = "2021-12-25T15:10:09Z";
String v4 = "100.00";
String v5 = "15.00";

ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );

IntToByteArray svc = new IntToByteArray();

byte[] t1 = svc.intToByteArray(1);//ByteBuffer.allocate(1).putInt(1).array(); 


byte[] t1 = svc.intToByteArray(1);//ByteBuffer.allocate(1).putInt(1).array(); 
byte[] l1 = svc.intToByteArray(v1.length());
outputStream.write( t1 );
outputStream.write( l1 );
outputStream.write( v1.getBytes("UTF-8") );
byte[] t2 = svc.intToByteArray(2);
byte[] l2 = svc.intToByteArray(v2.length());
outputStream.write( t2 );
outputStream.write( l2 );
outputStream.write( v2.getBytes("UTF-8") );
byte[] t3 = svc.intToByteArray(3);
byte[] l3 = svc.intToByteArray(v3.length());
outputStream.write( t3 );
outputStream.write( l3 );
outputStream.write( v3.getBytes("UTF-8") );
byte[] t4 = svc.intToByteArray(4);
byte[] l4 = svc.intToByteArray(v4.length());
outputStream.write( t4 );
outputStream.write( l4 );
outputStream.write( v4.getBytes("UTF-8") );

byte[] t5 = svc.intToByteArray(5);
byte[] l5 = svc.intToByteArray(v5.length());
outputStream.write( t5 );
outputStream.write( l5 );
outputStream.write( v5.getBytes("UTF-8") );

byte c[] = outputStream.toByteArray( );

Base64 base64 = new Base64();
String encodedString = new String(base64.encode(c));
System.out.println(new String(encodedString));



QRCodeWriter barcodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = 
          barcodeWriter.encode(message, BarcodeFormat.QR_CODE, Integer.parseInt(size), Integer.parseInt(size));

ByteArrayOutputStream qrcImgByteStram = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix,"JPG",qrcImgByteStram);
byte[] imgData = qrcImgByteStram.toByteArray();

File file = new File(filename);
OutputStream os = new FileOutputStream(file);
os.write(imgData);
os.close();

finally we get the qr code representation, which can be tested using

https://play.google.com/store/apps/details?id=com.izam.daftraksaeinvoicereader

let me know your feedback!

Leave a Reply