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

In this blog,  I will show how to encode the QR data in base64 format using ABAP and then using it in SAPScript/SmartForms 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, “Firoz Ashraf
      • Tag      = 1 (1 as a type represents the seller name)
      • Length = 12 (The number of the bytes in “Firoz Ashraf” word)
      • Value   = Firoz Ashraf
    2. VAT Number; for example, 1234567891
      • Tag      = 2 (2 as a type represents the VAT number)
      • Length = 10
      • Value   = 1234567891
    3. Time Stamp; for example, 2021-11-17 08:30:00
      • Tag      = 3 (3 as a type represents invoice time stamp)
      • Length = 19
      • Value   = 2021-11-17 08:30:00
    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 concatenate all the TLVs into one string

‘112Firoz Ashraf’  ‘2101234567891’   ‘3192021-11-17 08:30:00’   ‘46100.00’   ‘5515.00’

The single string then will be

‘112Firoz Ashraf21012345678913192021-11-17 08:30:0046100.005515.00’

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

From the above example we get the following Base64 encoded value


Now let’s see how we can do this in ABAP

To get the ‘Length’ in the TLV structure, we will use the Function Module SCMS_STRING_TO_XSTRING to convert the text to xString and then we will use xstrlen to get the length.

FORM tag_length USING p_string CHANGING p_length. DATA: v_xstr TYPE xstring.
*First Convert string to xString CALL FUNCTION 'SCMS_STRING_TO_XSTRING' EXPORTING text = p_string
* MIMETYPE = ' '
* ENCODING = IMPORTING buffer = v_xstr EXCEPTIONS failed = 1 OTHERS = 2. IF sy-subrc <> 0.
* Implement suitable error handling here ELSE. p_length = xstrlen( v_xstr ). ENDIF. ENDFORM.

To convert the string to Base64 we have two ways in ABAP:

The first one is using Class CL_HTTP_UTILITY method ENCODE_BASE64

The second one is using Function Module SCMS_STRING_TO_XSTRING to Convert String to Xstring and the using another Function Module SCMS_BASE64_ENCODE_STR to Convert the Xstring to Base64.

You can choose either of the ways (either Class or FM)

I have used the Class method to convert string to Base64.

To start with, I created a custom FM which takes invoice number as input and gives QR code values in text as well as in Base64.

I use this FM in SAPScript/SmartForms to print the QR Code.

FUNCTION z_einvoice_base64_qrcode_value.
*"*"Local Interface:
*"---------------------------------------------------------------------- DATA: wa_vbrk TYPE vbrk, v_t1_cname TYPE string, v_t2_vatno TYPE string, v_date(10), v_time(8), v_t3_tstmp TYPE string, v_t4_invamt TYPE vbrk-netwr, v_t4_invamx TYPE string, v_t5_vatamt TYPE vbrk-netwr, v_t5_vatamx TYPE string, v_t1_len TYPE i,v_t2_len TYPE i,v_t3_len TYPE i, v_t4_len TYPE i,v_t5_len TYPE i, v_t1_lenx TYPE string,v_t2_lenx TYPE string,v_t3_lenx TYPE string, v_t4_lenx TYPE string,v_t5_lenx TYPE string. SELECT SINGLE * FROM vbrk INTO wa_vbrk WHERE vbeln = invoice_no. IF sy-subrc = 0.
* Company Name & VAT No. SELECT SINGLE butxt stceg FROM t001 INTO ( v_t1_cname, v_t2_vatno ) WHERE bukrs = wa_vbrk-bukrs.
* Invoice Time Stamp CONCATENATE wa_vbrk-fkdat(4) '-' wa_vbrk-fkdat+4(2) '-' wa_vbrk-fkdat+6(2) INTO v_date. CONCATENATE wa_vbrk-erzet(2) ':' wa_vbrk-erzet+2(2) ':' wa_vbrk-erzet+4(2) INTO v_time. CONCATENATE v_date v_time INTO v_t3_tstmp SEPARATED BY space.
* Invoice Total (with VAT) v_t4_invamt = wa_vbrk-netwr + wa_vbrk-mwsbk. v_t4_invamx = v_t4_invamt. CONDENSE v_t4_invamx.
* VAT Total v_t5_vatamt = wa_vbrk-mwsbk. v_t5_vatamx = v_t5_vatamt. CONDENSE v_t5_vatamx. *****************Calculate Tag length********************** PERFORM tag_length USING v_t1_cname CHANGING v_t1_len.
* Convert lenth to text value v_t1_lenx = v_t1_len. CONDENSE v_t1_lenx. PERFORM tag_length USING v_t2_vatno CHANGING v_t2_len. v_t2_lenx = v_t2_len. CONDENSE v_t2_lenx. PERFORM tag_length USING v_t3_tstmp CHANGING v_t3_len. v_t3_lenx = v_t3_len. CONDENSE v_t3_lenx. PERFORM tag_length USING v_t4_invamx CHANGING v_t4_len. v_t4_lenx = v_t4_len. CONDENSE v_t4_lenx. PERFORM tag_length USING v_t5_vatamx CHANGING v_t5_len. v_t5_lenx = v_t5_len. CONDENSE v_t5_lenx. ***************Concatenate all TLV data******************** CONCATENATE '1' v_t1_lenx v_t1_cname '2' v_t2_lenx v_t2_vatno '3' v_t3_lenx v_t3_tstmp '4' v_t4_lenx v_t4_invamx '5' v_t5_lenx v_t5_vatamx INTO qrcode_string. ***************Encode String to Base64********************* CALL METHOD cl_http_utility=>if_http_utility~encode_base64 EXPORTING unencoded = qrcode_string RECEIVING encoded = qrcode_base64. ELSE. RAISE no_invoice. ENDIF. ENDFUNCTION.

Setting up the QR Code font 

Using SE73, create a new ‘System Bar Code’


Once this is done, create a Character format say QR in your SAPScript using the Bar Code (QR Code) created above.


You can then use this in your Window

Here I am calling the subroutine ZEDOC_KSA_QRBASE64 in ABAP program ZSDLINCLUDE which actually has our custom FM Z_EINVOICE_BASE64_QRCODE_VALUE

Note that a single text variable in SAPScript has a capacity to hold 80 characters and our QR code value is more than 80 hence I had to spilt the values in two variables V_QRCODE1 & V_QRCODE2.

FORM zedoc_ksa_qrbase64 TABLES in_tab STRUCTURE itcsy out_tab STRUCTURE itcsy. DATA: v_vbeln TYPE vbeln, v_qrb64 TYPE string, v_len TYPE i, v_rem TYPE i. READ TABLE in_tab INDEX 1. IF sy-subrc = 0. v_vbeln = in_tab-value. CALL FUNCTION 'Z_EINVOICE_BASE64_QRCODE_VALUE' EXPORTING invoice_no = v_vbeln IMPORTING
* QRCODE_STRING = qrcode_base64 = v_qrb64 EXCEPTIONS no_invoice = 1 xstr_error = 2 OTHERS = 3. IF sy-subrc <> 0.
* Implement suitable error handling here ELSE. v_len = strlen( v_qrb64 ). READ TABLE out_tab INDEX 1. IF sy-subrc = 0. IF v_len GT 80. "Split into two variables out_tab-value = v_qrb64(80). v_rem = v_len - 80. MODIFY out_tab INDEX 1.CLEAR out_tab. READ TABLE out_tab INDEX 2. IF sy-subrc = 0. out_tab-value = v_qrb64+80(v_rem). MODIFY out_tab INDEX 2.CLEAR out_tab. ENDIF. ELSE. out_tab-value = v_qrb64. MODIFY out_tab INDEX 1.CLEAR out_tab. ENDIF. ENDIF. ENDIF. ENDIF.

After doing this when you call the layout you will get the QR Code

If you scan this QR code then you will get the the following Base64 coded text


When decoded this will give the following text value

112Firoz Ashraf21012345678913192021-11-17 08:30:0046100.005515.00

You may go through the following blogs and links which were quite helpful in getting the pieces together.


Enjoy coding !!

Firoz Ashraf.