LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_kyc-proof.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 119 160 74.4 %
Date: 2025-07-14 11:22:44 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2021-2023 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_kyc-proof.c
      18             :  * @brief Handle request for proof for KYC check.
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "taler/platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include <gnunet/gnunet_json_lib.h>
      24             : #include <jansson.h>
      25             : #include <microhttpd.h>
      26             : #include "taler/taler_attributes.h"
      27             : #include "taler/taler_json_lib.h"
      28             : #include "taler/taler_kyclogic_lib.h"
      29             : #include "taler/taler_mhd_lib.h"
      30             : #include "taler/taler_templating_lib.h"
      31             : #include "taler-exchange-httpd_common_kyc.h"
      32             : #include "taler-exchange-httpd_kyc-proof.h"
      33             : #include "taler-exchange-httpd_responses.h"
      34             : 
      35             : 
      36             : /**
      37             :  * Context for the proof.
      38             :  */
      39             : struct KycProofContext
      40             : {
      41             : 
      42             :   /**
      43             :    * Kept in a DLL while suspended.
      44             :    */
      45             :   struct KycProofContext *next;
      46             : 
      47             :   /**
      48             :    * Kept in a DLL while suspended.
      49             :    */
      50             :   struct KycProofContext *prev;
      51             : 
      52             :   /**
      53             :    * Details about the connection we are processing.
      54             :    */
      55             :   struct TEH_RequestContext *rc;
      56             : 
      57             :   /**
      58             :    * Proof logic to run.
      59             :    */
      60             :   struct TALER_KYCLOGIC_Plugin *logic;
      61             : 
      62             :   /**
      63             :    * Configuration for @a logic.
      64             :    */
      65             :   struct TALER_KYCLOGIC_ProviderDetails *pd;
      66             : 
      67             :   /**
      68             :    * Asynchronous operation with the proof system.
      69             :    */
      70             :   struct TALER_KYCLOGIC_ProofHandle *ph;
      71             : 
      72             :   /**
      73             :    * KYC AML trigger operation.
      74             :    */
      75             :   struct TEH_KycMeasureRunContext *kat;
      76             : 
      77             :   /**
      78             :    * Process information about the user for the plugin from the database, can
      79             :    * be NULL.
      80             :    */
      81             :   char *provider_user_id;
      82             : 
      83             :   /**
      84             :    * Process information about the legitimization process for the plugin from the
      85             :    * database, can be NULL.
      86             :    */
      87             :   char *provider_legitimization_id;
      88             : 
      89             :   /**
      90             :    * Hash of payment target URI this is about.
      91             :    */
      92             :   struct TALER_NormalizedPaytoHashP h_payto;
      93             : 
      94             :   /**
      95             :    * Final HTTP response to return.
      96             :    */
      97             :   struct MHD_Response *response;
      98             : 
      99             :   /**
     100             :    * Final HTTP response code to return.
     101             :    */
     102             :   unsigned int response_code;
     103             : 
     104             :   /**
     105             :    * HTTP response from the KYC provider plugin.
     106             :    */
     107             :   struct MHD_Response *proof_response;
     108             : 
     109             :   /**
     110             :    * HTTP response code from the KYC provider plugin.
     111             :    */
     112             :   unsigned int proof_response_code;
     113             : 
     114             :   /**
     115             :    * Provider configuration section name of the logic we are running.
     116             :    */
     117             :   const char *provider_name;
     118             : 
     119             :   /**
     120             :    * Row in the database for this legitimization operation.
     121             :    */
     122             :   uint64_t process_row;
     123             : 
     124             :   /**
     125             :    * True if we are suspended,
     126             :    */
     127             :   bool suspended;
     128             : 
     129             :   /**
     130             :    * True if @e h_payto is for a wallet.
     131             :    */
     132             :   bool is_wallet;
     133             : 
     134             : };
     135             : 
     136             : 
     137             : /**
     138             :  * Contexts are kept in a DLL while suspended.
     139             :  */
     140             : static struct KycProofContext *kpc_head;
     141             : 
     142             : /**
     143             :  * Contexts are kept in a DLL while suspended.
     144             :  */
     145             : static struct KycProofContext *kpc_tail;
     146             : 
     147             : 
     148             : /**
     149             :  * Generate HTML error for @a connection using @a template.
     150             :  *
     151             :  * @param connection HTTP client connection
     152             :  * @param template template to expand
     153             :  * @param[in,out] http_status HTTP status of the response
     154             :  * @param ec Taler error code to return
     155             :  * @param message extended message to return
     156             :  * @return MHD response object
     157             :  */
     158             : static struct MHD_Response *
     159           1 : make_html_error (struct MHD_Connection *connection,
     160             :                  const char *template,
     161             :                  unsigned int *http_status,
     162             :                  enum TALER_ErrorCode ec,
     163             :                  const char *message)
     164             : {
     165           1 :   struct MHD_Response *response = NULL;
     166             :   json_t *body;
     167             : 
     168           1 :   body = GNUNET_JSON_PACK (
     169             :     GNUNET_JSON_pack_allow_null (
     170             :       GNUNET_JSON_pack_string ("message",
     171             :                                message)),
     172             :     TALER_JSON_pack_ec (
     173             :       ec));
     174           1 :   GNUNET_break (
     175             :     GNUNET_SYSERR !=
     176             :     TALER_TEMPLATING_build (connection,
     177             :                             http_status,
     178             :                             template,
     179             :                             NULL,
     180             :                             NULL,
     181             :                             body,
     182             :                             &response));
     183           1 :   json_decref (body);
     184           1 :   return response;
     185             : }
     186             : 
     187             : 
     188             : /**
     189             :  * Resume processing the @a kpc request.
     190             :  *
     191             :  * @param kpc request to resume
     192             :  */
     193             : static void
     194          11 : kpc_resume (struct KycProofContext *kpc)
     195             : {
     196          11 :   GNUNET_assert (GNUNET_YES == kpc->suspended);
     197          11 :   kpc->suspended = false;
     198          11 :   GNUNET_CONTAINER_DLL_remove (kpc_head,
     199             :                                kpc_tail,
     200             :                                kpc);
     201          11 :   MHD_resume_connection (kpc->rc->connection);
     202          11 :   TALER_MHD_daemon_trigger ();
     203          11 : }
     204             : 
     205             : 
     206             : void
     207          21 : TEH_kyc_proof_cleanup (void)
     208             : {
     209             :   struct KycProofContext *kpc;
     210             : 
     211          21 :   while (NULL != (kpc = kpc_head))
     212             :   {
     213           0 :     if (NULL != kpc->ph)
     214             :     {
     215           0 :       kpc->logic->proof_cancel (kpc->ph);
     216           0 :       kpc->ph = NULL;
     217             :     }
     218           0 :     kpc_resume (kpc);
     219             :   }
     220          21 : }
     221             : 
     222             : 
     223             : /**
     224             :  * Function called after the KYC-AML trigger is done.
     225             :  *
     226             :  * @param cls closure
     227             :  * @param ec error code or 0 on success
     228             :  * @param detail error message or NULL on success / no info
     229             :  */
     230             : static void
     231          11 : proof_finish (
     232             :   void *cls,
     233             :   enum TALER_ErrorCode ec,
     234             :   const char *detail)
     235             : {
     236          11 :   struct KycProofContext *kpc = cls;
     237             : 
     238          11 :   kpc->kat = NULL;
     239          11 :   if (TALER_EC_NONE != ec)
     240             :   {
     241           1 :     kpc->response_code = TALER_ErrorCode_get_http_status (ec);
     242           1 :     GNUNET_break (5 != kpc->response_code / 100);
     243           1 :     GNUNET_assert (kpc->response_code != UINT_MAX);
     244           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     245             :                 "Templating error response for %d and HTTP status %u (%s)\n",
     246             :                 (int) ec,
     247             :                 kpc->response_code,
     248             :                 detail);
     249           1 :     kpc->response = make_html_error (
     250           1 :       kpc->rc->connection,
     251             :       "kyc-proof-internal-error",
     252             :       &kpc->response_code,
     253             :       ec,
     254             :       detail);
     255             :   }
     256             :   else
     257             :   {
     258          10 :     GNUNET_assert (NULL != kpc->proof_response);
     259          10 :     kpc->response_code = kpc->proof_response_code;
     260          10 :     kpc->response = kpc->proof_response;
     261          10 :     kpc->proof_response = NULL;
     262          10 :     kpc->proof_response_code = 0;
     263             :   }
     264          11 :   GNUNET_assert (NULL != kpc->response);
     265          11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     266             :               "Resuming with response %p and status %u\n",
     267             :               kpc->response,
     268             :               kpc->response_code);
     269          11 :   kpc_resume (kpc);
     270          11 : }
     271             : 
     272             : 
     273             : /**
     274             :  * Respond with an HTML message on the given @a rc.
     275             :  *
     276             :  * @param[in,out] rc request to respond to
     277             :  * @param http_status HTTP status code to use
     278             :  * @param template template to fill in
     279             :  * @param ec error code to use for the template
     280             :  * @param message additional message to return
     281             :  * @return MHD result code
     282             :  */
     283             : static MHD_RESULT
     284           0 : respond_html_ec (struct TEH_RequestContext *rc,
     285             :                  unsigned int http_status,
     286             :                  const char *template,
     287             :                  enum TALER_ErrorCode ec,
     288             :                  const char *message)
     289             : {
     290             :   struct MHD_Response *response;
     291             :   MHD_RESULT res;
     292             : 
     293           0 :   response = make_html_error (rc->connection,
     294             :                               template,
     295             :                               &http_status,
     296             :                               ec,
     297             :                               message);
     298           0 :   res = MHD_queue_response (rc->connection,
     299             :                             http_status,
     300             :                             response);
     301           0 :   MHD_destroy_response (response);
     302           0 :   return res;
     303             : }
     304             : 
     305             : 
     306             : /**
     307             :  * Function called with the result of a proof check operation.
     308             :  *
     309             :  * Note that the "decref" for the @a response
     310             :  * will be done by the callee and MUST NOT be done by the plugin.
     311             :  *
     312             :  * @param cls closure
     313             :  * @param status KYC status
     314             :  * @param provider_name name of the provider
     315             :  * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
     316             :  * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
     317             :  * @param expiration until when is the KYC check valid
     318             :  * @param attributes user attributes returned by the provider
     319             :  * @param http_status HTTP status code of @a response
     320             :  * @param[in] response to return to the HTTP client
     321             :  */
     322             : static void
     323          11 : proof_cb (
     324             :   void *cls,
     325             :   enum TALER_KYCLOGIC_KycStatus status,
     326             :   const char *provider_name,
     327             :   const char *provider_user_id,
     328             :   const char *provider_legitimization_id,
     329             :   struct GNUNET_TIME_Absolute expiration,
     330             :   const json_t *attributes,
     331             :   unsigned int http_status,
     332             :   struct MHD_Response *response)
     333             : {
     334          11 :   struct KycProofContext *kpc = cls;
     335          11 :   struct TEH_RequestContext *rc = kpc->rc;
     336             :   struct GNUNET_AsyncScopeSave old_scope;
     337             :   enum GNUNET_DB_QueryStatus qs;
     338             : 
     339          11 :   kpc->ph = NULL;
     340          11 :   kpc->proof_response = response;
     341          11 :   kpc->proof_response_code = http_status;
     342          11 :   GNUNET_async_scope_enter (&rc->async_scope_id,
     343             :                             &old_scope);
     344          11 :   switch (status)
     345             :   {
     346           9 :   case TALER_KYCLOGIC_STATUS_SUCCESS:
     347           9 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     348             :                 "KYC process #%llu succeeded with KYC provider\n",
     349             :                 (unsigned long long) kpc->process_row);
     350           9 :     qs = TEH_kyc_store_attributes (
     351             :       kpc->process_row,
     352           9 :       &kpc->h_payto,
     353             :       provider_name,
     354             :       provider_user_id,
     355             :       provider_legitimization_id,
     356             :       expiration,
     357             :       attributes);
     358           9 :     if (0 >= qs)
     359             :     {
     360           0 :       GNUNET_break (0);
     361           0 :       proof_finish (kpc,
     362             :                     TALER_EC_GENERIC_DB_STORE_FAILED,
     363             :                     "kyc_store_attributes");
     364           0 :       break;
     365             :     }
     366             : 
     367          18 :     kpc->kat = TEH_kyc_run_measure_for_attributes (
     368           9 :       &rc->async_scope_id,
     369             :       kpc->process_row,
     370           9 :       &kpc->h_payto,
     371           9 :       kpc->is_wallet,
     372             :       &proof_finish,
     373             :       kpc);
     374           9 :     if (NULL == kpc->kat)
     375             :     {
     376           0 :       GNUNET_break_op (0);
     377           0 :       proof_finish (kpc,
     378             :                     TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
     379             :                     NULL);
     380             :     }
     381           9 :     break;
     382           2 :   case TALER_KYCLOGIC_STATUS_FAILED:
     383             :   case TALER_KYCLOGIC_STATUS_PROVIDER_FAILED:
     384             :   case TALER_KYCLOGIC_STATUS_USER_ABORTED:
     385             :   case TALER_KYCLOGIC_STATUS_ABORTED:
     386           2 :     GNUNET_assert (NULL == kpc->kat);
     387           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     388             :                 "KYC process %s/%s (Row #%llu) failed: %d\n",
     389             :                 provider_user_id,
     390             :                 provider_legitimization_id,
     391             :                 (unsigned long long) kpc->process_row,
     392             :                 status);
     393           2 :     if (5 == http_status / 100)
     394             :     {
     395             :       char *msg;
     396             : 
     397             :       /* OAuth2 server had a problem, do NOT log this as a KYC failure */
     398           1 :       GNUNET_break (0);
     399           1 :       GNUNET_asprintf (&msg,
     400             :                        "Failure by KYC provider (HTTP status %u)\n",
     401             :                        http_status);
     402           1 :       proof_finish (
     403             :         kpc,
     404             :         TALER_EC_EXCHANGE_KYC_GENERIC_PROVIDER_UNEXPECTED_REPLY,
     405             :         msg);
     406           1 :       GNUNET_free (msg);
     407           1 :       break;
     408             :     }
     409           2 :     if (! TEH_kyc_failed (
     410             :           kpc->process_row,
     411           1 :           &kpc->h_payto,
     412             :           kpc->provider_name,
     413             :           provider_user_id,
     414             :           provider_legitimization_id,
     415             :           TALER_KYCLOGIC_status2s (status),
     416             :           TALER_EC_EXCHANGE_GENERIC_KYC_FAILED))
     417             :     {
     418           0 :       GNUNET_break (0);
     419           0 :       proof_finish (
     420             :         kpc,
     421             :         TALER_EC_GENERIC_DB_STORE_FAILED,
     422             :         "TEH_kyc_failed");
     423           0 :       break;
     424             :     }
     425           1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     426             :                 "KYC process #%llu failed with status %d\n",
     427             :                 (unsigned long long) kpc->process_row,
     428             :                 status);
     429           1 :     proof_finish (kpc,
     430             :                   TALER_EC_NONE,
     431             :                   NULL);
     432           1 :     break;
     433           0 :   default:
     434           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     435             :                 "KYC status of %s/%s (Row #%llu) is %d\n",
     436             :                 provider_user_id,
     437             :                 provider_legitimization_id,
     438             :                 (unsigned long long) kpc->process_row,
     439             :                 (int) status);
     440           0 :     break;
     441             :   }
     442          11 :   GNUNET_async_scope_restore (&old_scope);
     443          11 : }
     444             : 
     445             : 
     446             : /**
     447             :  * Function called to clean up a context.
     448             :  *
     449             :  * @param rc request context
     450             :  */
     451             : static void
     452          11 : clean_kpc (struct TEH_RequestContext *rc)
     453             : {
     454          11 :   struct KycProofContext *kpc = rc->rh_ctx;
     455             : 
     456          11 :   if (NULL != kpc->ph)
     457             :   {
     458           0 :     kpc->logic->proof_cancel (kpc->ph);
     459           0 :     kpc->ph = NULL;
     460             :   }
     461          11 :   if (NULL != kpc->kat)
     462             :   {
     463           0 :     TEH_kyc_run_measure_cancel (kpc->kat);
     464           0 :     kpc->kat = NULL;
     465             :   }
     466          11 :   if (NULL != kpc->response)
     467             :   {
     468          11 :     MHD_destroy_response (kpc->response);
     469          11 :     kpc->response = NULL;
     470             :   }
     471          11 :   if (NULL != kpc->proof_response)
     472             :   {
     473           1 :     MHD_destroy_response (kpc->proof_response);
     474           1 :     kpc->proof_response = NULL;
     475             :   }
     476          11 :   GNUNET_free (kpc->provider_user_id);
     477          11 :   GNUNET_free (kpc->provider_legitimization_id);
     478          11 :   GNUNET_free (kpc);
     479          11 : }
     480             : 
     481             : 
     482             : MHD_RESULT
     483          22 : TEH_handler_kyc_proof (
     484             :   struct TEH_RequestContext *rc,
     485             :   const char *const args[1])
     486             : {
     487          22 :   struct KycProofContext *kpc = rc->rh_ctx;
     488          22 :   const char *provider_name_or_logic = args[0];
     489             : 
     490          22 :   if (NULL == kpc)
     491             :   {
     492             :     /* first time */
     493          11 :     if (NULL == provider_name_or_logic)
     494             :     {
     495           0 :       GNUNET_break_op (0);
     496           0 :       return respond_html_ec (
     497             :         rc,
     498             :         MHD_HTTP_NOT_FOUND,
     499             :         "kyc-proof-endpoint-unknown",
     500             :         TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
     501             :         "'/kyc-proof/$PROVIDER_NAME?state=$H_PAYTO' required");
     502             :     }
     503          11 :     kpc = GNUNET_new (struct KycProofContext);
     504          11 :     kpc->rc = rc;
     505          11 :     rc->rh_ctx = kpc;
     506          11 :     rc->rh_cleaner = &clean_kpc;
     507          11 :     TALER_MHD_parse_request_arg_auto_t (rc->connection,
     508             :                                         "state",
     509             :                                         &kpc->h_payto);
     510          11 :     if (GNUNET_OK !=
     511          11 :         TALER_KYCLOGIC_lookup_logic (
     512             :           provider_name_or_logic,
     513             :           &kpc->logic,
     514             :           &kpc->pd,
     515             :           &kpc->provider_name))
     516             :     {
     517           0 :       GNUNET_break_op (0);
     518           0 :       return respond_html_ec (
     519             :         rc,
     520             :         MHD_HTTP_NOT_FOUND,
     521             :         "kyc-proof-target-unknown",
     522             :         TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
     523             :         provider_name_or_logic);
     524             :     }
     525          11 :     if (NULL != kpc->provider_name)
     526             :     {
     527             :       enum GNUNET_DB_QueryStatus qs;
     528             :       struct GNUNET_TIME_Absolute expiration;
     529             : 
     530          11 :       if (0 != strcmp (provider_name_or_logic,
     531             :                        kpc->provider_name))
     532             :       {
     533           0 :         GNUNET_break_op (0);
     534           0 :         return respond_html_ec (
     535             :           rc,
     536             :           MHD_HTTP_BAD_REQUEST,
     537             :           "kyc-proof-bad-request",
     538             :           TALER_EC_GENERIC_PARAMETER_MALFORMED,
     539             :           "PROVIDER_NAME");
     540             :       }
     541             : 
     542          11 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     543             :                   "Looking for KYC process at %s\n",
     544             :                   kpc->provider_name);
     545          11 :       qs = TEH_plugin->lookup_kyc_process_by_account (
     546          11 :         TEH_plugin->cls,
     547             :         kpc->provider_name,
     548          11 :         &kpc->h_payto,
     549             :         &kpc->process_row,
     550             :         &expiration,
     551             :         &kpc->provider_user_id,
     552             :         &kpc->provider_legitimization_id,
     553             :         &kpc->is_wallet);
     554          11 :       switch (qs)
     555             :       {
     556           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
     557             :       case GNUNET_DB_STATUS_SOFT_ERROR:
     558           0 :         GNUNET_break (0);
     559           0 :         return respond_html_ec (
     560             :           rc,
     561             :           MHD_HTTP_INTERNAL_SERVER_ERROR,
     562             :           "kyc-proof-internal-error",
     563             :           TALER_EC_GENERIC_DB_FETCH_FAILED,
     564             :           "lookup_kyc_process_by_account");
     565           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     566           0 :         GNUNET_break_op (0);
     567           0 :         return respond_html_ec (
     568             :           rc,
     569             :           MHD_HTTP_NOT_FOUND,
     570             :           "kyc-proof-target-unknown",
     571             :           TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
     572             :           kpc->provider_name);
     573          11 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     574          11 :         break;
     575             :       }
     576          11 :       if (GNUNET_TIME_absolute_is_future (expiration))
     577             :       {
     578             :         /* KYC not required */
     579           0 :         return respond_html_ec (
     580             :           rc,
     581             :           MHD_HTTP_OK,
     582             :           "kyc-proof-already-done",
     583             :           TALER_EC_NONE,
     584             :           NULL);
     585             :       }
     586             :     }
     587          22 :     kpc->ph = kpc->logic->proof (
     588          11 :       kpc->logic->cls,
     589          11 :       kpc->pd,
     590             :       rc->connection,
     591          11 :       &kpc->h_payto,
     592             :       kpc->process_row,
     593          11 :       kpc->provider_user_id,
     594          11 :       kpc->provider_legitimization_id,
     595             :       &proof_cb,
     596             :       kpc);
     597          11 :     if (NULL == kpc->ph)
     598             :     {
     599           0 :       GNUNET_break (0);
     600           0 :       return respond_html_ec (
     601             :         rc,
     602             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     603             :         "kyc-proof-internal-error",
     604             :         TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     605             :         "could not start proof with KYC logic");
     606             :     }
     607             : 
     608             : 
     609          11 :     kpc->suspended = true;
     610          11 :     GNUNET_CONTAINER_DLL_insert (kpc_head,
     611             :                                  kpc_tail,
     612             :                                  kpc);
     613          11 :     MHD_suspend_connection (rc->connection);
     614          11 :     return MHD_YES;
     615             :   }
     616             : 
     617          11 :   if (NULL == kpc->response)
     618             :   {
     619           0 :     GNUNET_break (0);
     620           0 :     return respond_html_ec (
     621             :       rc,
     622             :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     623             :       "kyc-proof-internal-error",
     624             :       TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     625             :       "handler resumed without response");
     626             :   }
     627             : 
     628             :   /* return response from KYC logic */
     629          11 :   return MHD_queue_response (rc->connection,
     630             :                              kpc->response_code,
     631             :                              kpc->response);
     632             : }
     633             : 
     634             : 
     635             : /* end of taler-exchange-httpd_kyc-proof.c */

Generated by: LCOV version 1.16