SAP BTP - ABAP in the cloud

Generate PDF from HTML

CLASS zgenerate_pdf DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
*
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
    METHODS: generate_pdf
      IMPORTING
        VALUE(im_url)            TYPE  string
        VALUE(im_api_key)        TYPE  string
        VALUE(im_html)           TYPE  string
        VALUE(im_encryption_key) TYPE  string OPTIONAL
        VALUE(im_landscape)      TYPE  string OPTIONAL
        VALUE(im_format)         TYPE  string OPTIONAL
        VALUE(im_margin_top)     TYPE  string OPTIONAL
        VALUE(im_margin_right)   TYPE  string OPTIONAL
        VALUE(im_margin_bottom)  TYPE  string OPTIONAL
        VALUE(im_margin_left)    TYPE  string OPTIONAL
      EXPORTING
        VALUE(ex_subrc)          TYPE  sysubrc
        VALUE(ex_mess)           TYPE  string
        VALUE(ex_pdf)            TYPE  xstring,
*
      close_client
        CHANGING
          im_client TYPE REF TO if_web_http_client.
*
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.
*
CLASS zgenerate_pdf IMPLEMENTATION.
*
  METHOD if_oo_adt_classrun~main.
*
    DATA: lv_subrc TYPE sysubrc,
          lv_mess  TYPE string,
          lv_xpdf  TYPE xstring.
*
    CALL METHOD generate_pdf
      EXPORTING
        im_url     = 'https://api-eu.renda.io/v2/generatePDF'
        im_api_key = '<your api key>'
        im_html    = '<html>Hello World!</html>'
*       im_encryption_key =
*       im_landscape      =
*       im_format  =
*       im_margin_top     =
*       im_margin_right   =
*       im_margin_bottom  =
*       im_margin_left    =
      IMPORTING
        ex_subrc   = lv_subrc
        ex_mess    = lv_mess
        ex_pdf     = lv_xpdf.
*
    out->write( lv_subrc ).
    IF lv_mess IS NOT INITIAL.
      out->write( lv_mess ).
    ENDIF.
    out->write( lv_xpdf ).
*
  ENDMETHOD.
*
  METHOD close_client.
*
    TRY.
        CALL METHOD im_client->close.
      CATCH cx_web_http_client_error.
    ENDTRY.
    FREE im_client.
*
  ENDMETHOD.
*
  METHOD generate_pdf.
*
    DATA: lo_client      TYPE REF TO if_web_http_client,
          lo_response    TYPE REF TO if_web_http_response,
          lo_destination TYPE REF TO if_http_destination,
          lo_request     TYPE REF TO if_web_http_request,
          lo_resp_status TYPE if_web_http_response=>http_status,
          lv_http_rc     TYPE i,
          lv_response    TYPE string,
          lv_json        TYPE /ui2/cl_json=>json,
          lr_data        TYPE REF TO data,
          lv_req_body    TYPE string.
*
    FIELD-SYMBOLS: <tab>   TYPE ANY TABLE,
                   <data>  TYPE data,
                   <data2> TYPE data,
                   <field> TYPE any.
*
* Setup IF_WEB_HTTP_CLIENT object
*
    TRY.
        CALL METHOD cl_http_destination_provider=>create_by_url
          EXPORTING
            i_url              = im_url
          RECEIVING
            r_http_destination = lo_destination.
      CATCH cx_http_dest_provider_error.
        ex_mess = 'Error creating HTTP destination object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        cl_web_http_client_manager=>create_by_http_destination(
          EXPORTING
            i_destination = lo_destination
          RECEIVING
            r_client      = lo_client
        ).
      CATCH cx_web_http_client_error.
        ex_mess = 'Error creating HTTP client object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Get Request object
*
    lo_client->get_http_request(
      RECEIVING
        r_http_request = lo_request ).
*
* Set protocol version
*
    lo_request->set_version( if_web_http_request=>co_protocol_version_1_1 ).
*
* Set request header fields
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'Accept'
            i_value = 'application/pdf' ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'Content-Type'
            i_value = 'application/x-www-form-urlencoded' ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'x-api-key'
            i_value = im_api_key ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Set request body
*
    CONCATENATE: 'inputPayload=' im_html INTO lv_req_body.
*
    IF im_encryption_key IS NOT INITIAL.
      CONCATENATE: lv_req_body '&encryptionKey=' im_encryption_key INTO lv_req_body.
    ENDIF.
*
    IF im_landscape IS NOT INITIAL.
      CONCATENATE: lv_req_body '&landscape=' im_landscape INTO lv_req_body.
    ENDIF.
*
    IF im_format IS NOT INITIAL.
      CONCATENATE: lv_req_body '&format=' im_format INTO lv_req_body.
    ENDIF.
*
    IF im_margin_top IS NOT INITIAL.
      CONCATENATE: lv_req_body '&marginTop=' im_margin_top INTO lv_req_body.
    ENDIF.
*
    IF im_margin_right IS NOT INITIAL.
      CONCATENATE: lv_req_body '&marginRigth=' im_margin_right INTO lv_req_body.
    ENDIF.
*
    IF im_margin_bottom IS NOT INITIAL.
      CONCATENATE: lv_req_body '&marginBottom=' im_margin_bottom INTO lv_req_body.
    ENDIF.
*
    IF im_margin_left IS NOT INITIAL.
      CONCATENATE: lv_req_body '&marginLeft=' im_margin_left INTO lv_req_body.
    ENDIF.
*
    TRY.
        lo_request->set_text(
          EXPORTING
            i_text   = lv_req_body ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting request body. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Send request
*
    TRY.
        lo_client->execute(
          EXPORTING
            i_method   = if_web_http_client=>post
          RECEIVING
            r_response = lo_response ).
      CATCH cx_web_http_client_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'HTTP Post error. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Read the Response
*
    TRY.
        lo_response->get_status(
        RECEIVING
          r_value = lo_resp_status ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error reading response. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_response->get_text(
          RECEIVING
            r_value = lv_response ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error reading response. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    IF lo_resp_status-code NE '200'.
*
* Get error message back from Renda
*
      ex_subrc = 4.
      lv_json = lv_response.
      IF lv_json+0(1) NE '['.
        CONCATENATE '[' lv_json ']' INTO lv_json.
      ENDIF.
      lr_data = /ui2/cl_json=>generate( json = lv_json ).
*
      IF lr_data IS BOUND.
        ASSIGN lr_data->* TO <tab>.
        LOOP AT <tab> ASSIGNING <data>.
          ASSIGN <data>->* TO <data2>.
          ASSIGN COMPONENT 'MESSAGE' OF STRUCTURE <data2> TO <field>.
          IF <field> IS ASSIGNED.
            lr_data = <field>.
            ASSIGN lr_data->* TO <data2>.
            IF ex_mess IS INITIAL.
              ex_mess = <data2>.
            ELSE.
              CONCATENATE ex_mess ' \ ' <data2> INTO ex_mess RESPECTING BLANKS.
            ENDIF.
          ENDIF.
        ENDLOOP.
      ENDIF.
*
      IF ex_mess IS INITIAL.
        ex_mess = lv_response.
      ENDIF.
*
    ELSE.
*
      TRY.
          lo_response->get_binary(
            RECEIVING
              r_value  = ex_pdf
          ).
        CATCH cx_web_message_error.
          ex_mess = 'Error reading response. Process aborted'.
          ex_subrc = 4.
      ENDTRY.
*
    ENDIF.
*
    CALL METHOD close_client
      CHANGING
        im_client = lo_client.
*
  ENDMETHOD.
*
ENDCLASS.

Generate QR Code

CLASS zgenerate_qr_code DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
*
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
    METHODS: generate_qr_code
      IMPORTING
        VALUE(im_url)            TYPE  string
        VALUE(im_api_key)        TYPE  string
        VALUE(im_data)           TYPE  string
      EXPORTING
        VALUE(ex_subrc)          TYPE  sysubrc
        VALUE(ex_mess)           TYPE  string
        VALUE(ex_qr_code)            TYPE  xstring,
*
      close_client
        CHANGING
          im_client TYPE REF TO if_web_http_client.
*
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.
*
CLASS zgenerate_qr_code IMPLEMENTATION.
*
  METHOD if_oo_adt_classrun~main.
*
    DATA: lv_subrc   TYPE sysubrc,
          lv_mess    TYPE string,
          lv_qr_code TYPE xstring.
*
    CALL METHOD generate_qr_code
      EXPORTING
        im_url     = 'https://api-eu.renda.io/totp/generateQRCode'
        im_api_key = '<your API key>'
        im_data    = 'www.google.com'
      IMPORTING
        ex_subrc   = lv_subrc
        ex_mess    = lv_mess
        ex_qr_code = lv_qr_code.
*
    out->write( lv_subrc ).
    IF lv_mess IS NOT INITIAL.
      out->write( lv_mess ).
    ENDIF.
    out->write( lv_qr_code ).
*
  ENDMETHOD.
*
  METHOD close_client.
*
    TRY.
        CALL METHOD im_client->close.
      CATCH cx_web_http_client_error.
    ENDTRY.
    FREE im_client.
*
  ENDMETHOD.
*
  METHOD generate_qr_code.
*
    DATA: lo_client      TYPE REF TO if_web_http_client,
          lo_response    TYPE REF TO if_web_http_response,
          lo_destination TYPE REF TO if_http_destination,
          lo_request     TYPE REF TO if_web_http_request,
          lo_resp_status TYPE if_web_http_response=>http_status,
          lv_http_rc     TYPE i,
          lv_response    TYPE string,
          lv_json        TYPE /ui2/cl_json=>json,
          lr_data        TYPE REF TO data,
          lv_req_body    TYPE string.
*
    FIELD-SYMBOLS: <tab>   TYPE ANY TABLE,
                   <data>  TYPE data,
                   <data2> TYPE data,
                   <field> TYPE any.
*
* Setup IF_WEB_HTTP_CLIENT object
*
    TRY.
        CALL METHOD cl_http_destination_provider=>create_by_url
          EXPORTING
            i_url              = im_url
          RECEIVING
            r_http_destination = lo_destination.
      CATCH cx_http_dest_provider_error.
        ex_mess = 'Error creating HTTP destination object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        cl_web_http_client_manager=>create_by_http_destination(
          EXPORTING
            i_destination = lo_destination
          RECEIVING
            r_client      = lo_client
        ).
      CATCH cx_web_http_client_error.
        ex_mess = 'Error creating HTTP client object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Get Request object
*
    lo_client->get_http_request(
      RECEIVING
        r_http_request = lo_request ).
*
* Set protocol version
*
    lo_request->set_version( if_web_http_request=>co_protocol_version_1_1 ).
*
* Set request header fields
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'Accept'
            i_value = '*/*' ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'Content-Type'
            i_value = 'application/x-www-form-urlencoded' ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'x-api-key'
            i_value = im_api_key ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Set request body
*
    CONCATENATE: 'data=' im_data INTO lv_req_body.
*
    TRY.
        lo_request->set_text(
          EXPORTING
            i_text   = lv_req_body ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting request body. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Send request
*
    TRY.
        lo_client->execute(
          EXPORTING
            i_method   = if_web_http_client=>post
          RECEIVING
            r_response = lo_response ).
      CATCH cx_web_http_client_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'HTTP Post error. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Read the Response
*
    TRY.
        lo_response->get_status(
        RECEIVING
          r_value = lo_resp_status ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error reading response. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_response->get_text(
          RECEIVING
            r_value = lv_response ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error reading response. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    IF lo_resp_status-code NE '200'.
*
* Get error message back from Renda
*
      ex_subrc = 4.
      lv_json = lv_response.
      IF lv_json+0(1) NE '['.
        CONCATENATE '[' lv_json ']' INTO lv_json.
      ENDIF.
      lr_data = /ui2/cl_json=>generate( json = lv_json ).
*
      IF lr_data IS BOUND.
        ASSIGN lr_data->* TO <tab>.
        LOOP AT <tab> ASSIGNING <data>.
          ASSIGN <data>->* TO <data2>.
          ASSIGN COMPONENT 'MESSAGE' OF STRUCTURE <data2> TO <field>.
          IF <field> IS ASSIGNED.
            lr_data = <field>.
            ASSIGN lr_data->* TO <data2>.
            IF ex_mess IS INITIAL.
              ex_mess = <data2>.
            ELSE.
              CONCATENATE ex_mess ' \ ' <data2> INTO ex_mess RESPECTING BLANKS.
            ENDIF.
          ENDIF.
        ENDLOOP.
      ENDIF.
*
      IF ex_mess IS INITIAL.
        ex_mess = lv_response.
      ENDIF.
*
    ELSE.
*
      TRY.
          lo_response->get_binary(
            RECEIVING
              r_value  = ex_qr_code ).
        CATCH cx_web_message_error.
          ex_mess = 'Error reading response. Process aborted'.
          ex_subrc = 4.
      ENDTRY.
*
    ENDIF.
*
    CALL METHOD close_client
      CHANGING
        im_client = lo_client.
*
  ENDMETHOD.
*
ENDCLASS.

Onboard user for TOTP

CLASS zonboard_user DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
*
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
    METHODS: onboard_user
      IMPORTING
        VALUE(im_url)            TYPE  string
        VALUE(im_api_key)        TYPE  string
        VALUE(im_user_id)        TYPE  string
        VALUE(im_application_id) TYPE  string
        VALUE(im_token_validity) TYPE  string OPTIONAL
        VALUE(im_token_length)   TYPE  string OPTIONAL
      EXPORTING
        VALUE(ex_secret_key)     TYPE  string
        VALUE(ex_otp_url)        TYPE  string
        VALUE(ex_qr_code)        TYPE  string
        VALUE(ex_token)          TYPE  string
        VALUE(ex_mess)           TYPE  string
        VALUE(ex_subrc)          TYPE  sysubrc,
*
      close_client
        CHANGING
          im_client TYPE REF TO if_web_http_client.
*
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.
*
CLASS zonboard_user IMPLEMENTATION.
*
  METHOD if_oo_adt_classrun~main.
*
    DATA: lv_subrc      TYPE sysubrc,
          lv_mess       TYPE string,
          lv_secret_key TYPE string,
          lv_otp_url    TYPE string,
          lv_qr_code    TYPE string,
          lv_token      TYPE string.
*
    CALL METHOD onboard_user
      EXPORTING
        im_url            = 'https://api-eu.renda.io/totp/onboardUser'
        im_api_key        = '<your API key>'
        im_user_id        = 'johnSmith@gmail.com'
        im_application_id = 'Renda.io'
        im_token_validity = '120'
        im_token_length   = '7'
      IMPORTING
        ex_secret_key     = lv_secret_key
        ex_otp_url        = lv_otp_url
        ex_qr_code        = lv_qr_code
        ex_token          = lv_token
        ex_mess           = lv_mess
        ex_subrc          = lv_subrc.
*
    out->write( lv_subrc ).
    IF lv_mess IS NOT INITIAL.
      out->write( lv_mess ).
    ENDIF.
    out->write( lv_secret_key ).
    out->write( lv_otp_url ).
    out->write( lv_qr_code ).
    out->write( lv_token ).
*
  ENDMETHOD.
*
  METHOD close_client.
*
    TRY.
        CALL METHOD im_client->close.
      CATCH cx_web_http_client_error.
    ENDTRY.
    FREE im_client.
*
  ENDMETHOD.
*
  METHOD onboard_user.
*
    TYPES: BEGIN OF ltyp_response,
             secretkey TYPE string,
             otpurl    TYPE string,
             qrcode    TYPE string,
             token     TYPE string,
           END OF ltyp_response.
*
    DATA: lo_client      TYPE REF TO if_web_http_client,
          lo_response    TYPE REF TO if_web_http_response,
          lo_destination TYPE REF TO if_http_destination,
          lo_request     TYPE REF TO if_web_http_request,
          lo_resp_status TYPE if_web_http_response=>http_status,
          lv_http_rc     TYPE i,
          lv_response    TYPE string,
          lv_json        TYPE /ui2/cl_json=>json,
          lr_data        TYPE REF TO data,
          lv_req_body    TYPE string,
          ls_response    TYPE ltyp_response.
*
    FIELD-SYMBOLS: <tab>   TYPE ANY TABLE,
                   <data>  TYPE data,
                   <data2> TYPE data,
                   <field> TYPE any.
*
* Setup IF_WEB_HTTP_CLIENT object
*
    TRY.
        CALL METHOD cl_http_destination_provider=>create_by_url
          EXPORTING
            i_url              = im_url
          RECEIVING
            r_http_destination = lo_destination.
      CATCH cx_http_dest_provider_error.
        ex_mess = 'Error creating HTTP destination object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        cl_web_http_client_manager=>create_by_http_destination(
          EXPORTING
            i_destination = lo_destination
          RECEIVING
            r_client      = lo_client
        ).
      CATCH cx_web_http_client_error.
        ex_mess = 'Error creating HTTP client object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Get Request object
*
    lo_client->get_http_request(
      RECEIVING
        r_http_request = lo_request ).
*
* Set protocol version
*
    lo_request->set_version( if_web_http_request=>co_protocol_version_1_1 ).
*
* Set request header fields
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'Content-Type'
            i_value = 'application/x-www-form-urlencoded' ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_request->set_header_field(
          EXPORTING
            i_name  = 'x-api-key'
            i_value = im_api_key ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting header field. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Set request body
*
    CONCATENATE: 'userID=' im_user_id '&applicationID=' im_application_id INTO lv_req_body.
    IF im_token_validity IS NOT INITIAL.
      CONCATENATE lv_req_body '&tokenValidity=' im_token_validity INTO lv_req_body.
    ENDIF.
    IF im_token_length IS NOT INITIAL.
      CONCATENATE lv_req_body '&tokenLength=' im_token_length INTO lv_req_body.
    ENDIF.
*
    TRY.
        lo_request->set_text(
          EXPORTING
            i_text   = lv_req_body ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error setting request body. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Send request
*
    TRY.
        lo_client->execute(
          EXPORTING
            i_method   = if_web_http_client=>post
          RECEIVING
            r_response = lo_response ).
      CATCH cx_web_http_client_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'HTTP Post error. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
* Read the Response
*
    TRY.
        lo_response->get_status(
        RECEIVING
          r_value = lo_resp_status ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error reading response. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        lo_response->get_text(
          RECEIVING
            r_value = lv_response ).
      CATCH cx_web_message_error.
        CALL METHOD close_client
          CHANGING
            im_client = lo_client.
        ex_mess = 'Error reading response. Process aborted'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    IF lo_resp_status-code NE '200'.
*
* Get error message back from Renda
*
      ex_subrc = 4.
      lv_json = lv_response.
      IF lv_json+0(1) NE '['.
        CONCATENATE '[' lv_json ']' INTO lv_json.
      ENDIF.
      lr_data = /ui2/cl_json=>generate( json = lv_json ).
*
      IF lr_data IS BOUND.
        ASSIGN lr_data->* TO <tab>.
        LOOP AT <tab> ASSIGNING <data>.
          ASSIGN <data>->* TO <data2>.
          ASSIGN COMPONENT 'MESSAGE' OF STRUCTURE <data2> TO <field>.
          IF <field> IS ASSIGNED.
            lr_data = <field>.
            ASSIGN lr_data->* TO <data2>.
            IF ex_mess IS INITIAL.
              ex_mess = <data2>.
            ELSE.
              CONCATENATE ex_mess ' \ ' <data2> INTO ex_mess RESPECTING BLANKS.
            ENDIF.
          ENDIF.
        ENDLOOP.
      ENDIF.
*
      IF ex_mess IS INITIAL.
        ex_mess = lv_response.
      ENDIF.
*
    ELSE.
*
      /ui2/cl_json=>deserialize( EXPORTING json = lv_response pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = ls_response ).
      ex_secret_key = ls_response-secretkey.
      ex_otp_url = ls_response-otpurl.
      ex_qr_code = ls_response-qrcode.
      ex_token = ls_response-token.
*
    ENDIF.
*
    CALL METHOD close_client
      CHANGING
        im_client = lo_client.
*
  ENDMETHOD.
*
ENDCLASS.

Validate OTP using secret key

CLASS zverify_otp DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .
*
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
    METHODS: verify_otp
      IMPORTING
        VALUE(im_url)            TYPE  string
        VALUE(im_api_key)        TYPE  string
        VALUE(im_secret)         TYPE  string
        VALUE(im_token)          TYPE  string
        VALUE(im_token_validity) TYPE  string OPTIONAL
      EXPORTING
        VALUE(ex_is_valid)       TYPE  abap_boolean
        VALUE(ex_token)          TYPE  string
        VALUE(ex_time_used)      TYPE  string
        VALUE(ex_time_remaining) TYPE  string
        VALUE(ex_mess)           TYPE  string
        VALUE(ex_subrc)          TYPE  sysubrc,
*
      close_client
        CHANGING
          im_client TYPE REF TO if_web_http_client.
*
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.
*
CLASS zverify_otp IMPLEMENTATION.
*
  METHOD if_oo_adt_classrun~main.
*
    DATA: lv_subrc          TYPE sysubrc,
          lv_mess           TYPE string,
          lv_is_valid       TYPE abap_boolean,
          lv_token          TYPE string,
          lv_time_used      TYPE string,
          lv_time_remaining TYPE string.
*
    CALL METHOD verify_otp
      EXPORTING
        im_url            = 'https://api-eu.renda.io/totp/verifyOTP'
        im_api_key        = '<your API key>'
        im_secret         = '<secret>'
        im_token          = '<token>'
        im_token_validity = '120'
      IMPORTING
        ex_is_valid       = lv_is_valid
        ex_token          = lv_token
        ex_time_used      = lv_time_used
        ex_time_remaining = lv_time_remaining
        ex_mess           = lv_mess
        ex_subrc          = lv_subrc.
*
    out->write( lv_subrc ).
    IF lv_mess IS NOT INITIAL.
      out->write( lv_mess ).
    ENDIF.
    IF lv_subrc IS INITIAL.
      IF lv_is_valid IS INITIAL.
        out->write( 'Token is not valid' ).
      ELSE.
        out->write( 'Token is valid' ).
      ENDIF.
    ENDIF.
    out->write( lv_token ).
    out->write( lv_time_used ).
    out->write( lv_time_remaining ).
*
  ENDMETHOD.
*
  METHOD close_client.
*
    TRY.
        CALL METHOD im_client->close.
      CATCH cx_web_http_client_error.
    ENDTRY.
    FREE im_client.
*
  ENDMETHOD.
*
  METHOD verify_otp.
*
    TYPES: BEGIN OF ltyp_response,
             isvalid       TYPE abap_boolean,
             token         TYPE string,
             timeused      TYPE string,
             timeremaining TYPE string,
           END OF ltyp_response.
*
    DATA: lo_client      TYPE REF TO if_web_http_client,
          lo_response    TYPE REF TO if_web_http_response,
          lo_destination TYPE REF TO if_http_destination,
          lo_request     TYPE REF TO if_web_http_request,
          lo_resp_status TYPE if_web_http_response=>http_status,
          lv_http_rc     TYPE i,
          lv_response    TYPE string,
          lv_json        TYPE /ui2/cl_json=>json,
          lr_data        TYPE REF TO data,
          lv_req_body    TYPE string,
          ls_response    TYPE ltyp_response.
*
    FIELD-SYMBOLS: <tab>   TYPE ANY TABLE,
                   <data>  TYPE data,
                   <data2> TYPE data,
                   <field> TYPE any.
*
* Setup IF_WEB_HTTP_CLIENT object
*
    TRY.
        CALL METHOD cl_http_destination_provider=>create_by_url
          EXPORTING
            i_url              = im_url
          RECEIVING
            r_http_destination = lo_destination.
      CATCH cx_http_dest_provider_error.
        ex_mess = 'Error creating HTTP destination object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.
*
    TRY.
        cl_web_http_client_manager=>create_by_http_destination(
          EXPORTING
            i_destination = lo_destination
          RECEIVING
            r_client      = lo_client
        ).
      CATCH cx_web_http_client_error.
        ex_mess = 'Error creating HTTP client object. Process aborted.'.
        ex_subrc = 4.
        RETURN.
    ENDTRY.