LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-get-instances-ID-kyc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 235 348 67.5 %
Date: 2025-08-28 06:06:54 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /*
       2             :   This file is part of GNU Taler
       3             :   (C) 2021-2024 Taler Systems SA
       4             : 
       5             :   GNU Taler is free software; you can redistribute it and/or modify
       6             :   it under the terms of the GNU Affero General Public License as
       7             :   published by the Free Software Foundation; either version 3,
       8             :   or (at your option) any later version.
       9             : 
      10             :   GNU Taler is distributed in the hope that it will be useful, but
      11             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU General Public
      16             :   License along with TALER; see the file COPYING.  If not,
      17             :   see <http://www.gnu.org/licenses/>
      18             : */
      19             : 
      20             : /**
      21             :  * @file taler-merchant-httpd_private-get-instances-ID-kyc.c
      22             :  * @brief implementing GET /instances/$ID/kyc request handling
      23             :  * @author Christian Grothoff
      24             :  */
      25             : #include "platform.h"
      26             : #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
      27             : #include "taler-merchant-httpd_helper.h"
      28             : #include "taler-merchant-httpd_exchanges.h"
      29             : #include <taler/taler_json_lib.h>
      30             : #include <taler/taler_dbevents.h>
      31             : #include <regex.h>
      32             : 
      33             : /**
      34             :  * Information we keep per /kyc request.
      35             :  */
      36             : struct KycContext;
      37             : 
      38             : 
      39             : /**
      40             :  * Structure for tracking requests to the exchange's
      41             :  * ``/kyc-check`` API.
      42             :  */
      43             : struct ExchangeKycRequest
      44             : {
      45             :   /**
      46             :    * Kept in a DLL.
      47             :    */
      48             :   struct ExchangeKycRequest *next;
      49             : 
      50             :   /**
      51             :    * Kept in a DLL.
      52             :    */
      53             :   struct ExchangeKycRequest *prev;
      54             : 
      55             :   /**
      56             :    * Find operation where we connect to the respective exchange.
      57             :    */
      58             :   struct TMH_EXCHANGES_KeysOperation *fo;
      59             : 
      60             :   /**
      61             :    * JSON array of payto-URIs with KYC auth wire transfer
      62             :    * instructions.  Provided if @e auth_ok is false and
      63             :    * @e kyc_auth_conflict is false.
      64             :    */
      65             :   json_t *pkaa;
      66             : 
      67             :   /**
      68             :    * The keys of the exchange.
      69             :    */
      70             :   struct TALER_EXCHANGE_Keys *keys;
      71             : 
      72             :   /**
      73             :    * KYC request this exchange request is made for.
      74             :    */
      75             :   struct KycContext *kc;
      76             : 
      77             :   /**
      78             :    * JSON array of AccountLimits that apply, NULL if
      79             :    * unknown (and likely defaults apply).
      80             :    */
      81             :   json_t *jlimits;
      82             : 
      83             :   /**
      84             :    * Our account's payto URI.
      85             :    */
      86             :   struct TALER_FullPayto payto_uri;
      87             : 
      88             :   /**
      89             :    * Base URL of the exchange.
      90             :    */
      91             :   char *exchange_url;
      92             : 
      93             :   /**
      94             :    * Hash of the wire account (with salt) we are checking.
      95             :    */
      96             :   struct TALER_MerchantWireHashP h_wire;
      97             : 
      98             :   /**
      99             :    * Current access token for the KYC SPA. Only set
     100             :    * if @e auth_ok is true.
     101             :    */
     102             :   struct TALER_AccountAccessTokenP access_token;
     103             : 
     104             :   /**
     105             :    * Timestamp when we last got a reply from the exchange.
     106             :    */
     107             :   struct GNUNET_TIME_Timestamp last_check;
     108             : 
     109             :   /**
     110             :    * Last HTTP status code obtained via /kyc-check from
     111             :    * the exchange.
     112             :    */
     113             :   unsigned int last_http_status;
     114             : 
     115             :   /**
     116             :    * Last Taler error code returned from /kyc-check.
     117             :    */
     118             :   enum TALER_ErrorCode last_ec;
     119             : 
     120             :   /**
     121             :    * True if this account
     122             :    * cannot work at this exchange because KYC auth is
     123             :    * impossible.
     124             :    */
     125             :   bool kyc_auth_conflict;
     126             : 
     127             :   /**
     128             :    * We could not get /keys from the exchange.
     129             :    */
     130             :   bool no_keys;
     131             : 
     132             :   /**
     133             :    * True if @e access_token is available.
     134             :    */
     135             :   bool auth_ok;
     136             : 
     137             :   /**
     138             :    * True if we believe no KYC is currently required
     139             :    * for this account at this exchange.
     140             :    */
     141             :   bool kyc_ok;
     142             : 
     143             :   /**
     144             :    * True if the exchange exposed to us that the account
     145             :    * is currently under AML review.
     146             :    */
     147             :   bool in_aml_review;
     148             : 
     149             : 
     150             : };
     151             : 
     152             : 
     153             : /**
     154             :  * Information we keep per /kyc request.
     155             :  */
     156             : struct KycContext
     157             : {
     158             :   /**
     159             :    * Stored in a DLL.
     160             :    */
     161             :   struct KycContext *next;
     162             : 
     163             :   /**
     164             :    * Stored in a DLL.
     165             :    */
     166             :   struct KycContext *prev;
     167             : 
     168             :   /**
     169             :    * Connection we are handling.
     170             :    */
     171             :   struct MHD_Connection *connection;
     172             : 
     173             :   /**
     174             :    * Instance we are serving.
     175             :    */
     176             :   struct TMH_MerchantInstance *mi;
     177             : 
     178             :   /**
     179             :    * Our handler context.
     180             :    */
     181             :   struct TMH_HandlerContext *hc;
     182             : 
     183             :   /**
     184             :    * Response to return, NULL if we don't have one yet.
     185             :    */
     186             :   struct MHD_Response *response;
     187             : 
     188             :   /**
     189             :    * JSON array where we are building up the array with
     190             :    * pending KYC operations.
     191             :    */
     192             :   json_t *kycs_data;
     193             : 
     194             :   /**
     195             :    * Head of DLL of requests we are making to an
     196             :    * exchange to inquire about the latest KYC status.
     197             :    */
     198             :   struct ExchangeKycRequest *exchange_pending_head;
     199             : 
     200             :   /**
     201             :    * Tail of DLL of requests we are making to an
     202             :    * exchange to inquire about the latest KYC status.
     203             :    */
     204             :   struct ExchangeKycRequest *exchange_pending_tail;
     205             : 
     206             :   /**
     207             :    * Set to the exchange URL, or NULL to not filter by
     208             :    * exchange.
     209             :    */
     210             :   const char *exchange_url;
     211             : 
     212             :   /**
     213             :    * Notification handler from database on changes
     214             :    * to the KYC status.
     215             :    */
     216             :   struct GNUNET_DB_EventHandler *eh;
     217             : 
     218             :   /**
     219             :    * Set to the h_wire of the merchant account if
     220             :    * @a have_h_wire is true, used to filter by account.
     221             :    */
     222             :   struct TALER_MerchantWireHashP h_wire;
     223             : 
     224             :   /**
     225             :    * How long are we willing to wait for the exchange(s)?
     226             :    */
     227             :   struct GNUNET_TIME_Absolute timeout;
     228             : 
     229             :   /**
     230             :    * HTTP status code to use for the reply, i.e 200 for "OK".
     231             :    * Special value UINT_MAX is used to indicate hard errors
     232             :    * (no reply, return #MHD_NO).
     233             :    */
     234             :   unsigned int response_code;
     235             : 
     236             :   /**
     237             :    * #GNUNET_NO if the @e connection was not suspended,
     238             :    * #GNUNET_YES if the @e connection was suspended,
     239             :    * #GNUNET_SYSERR if @e connection was resumed to as
     240             :    * part of #MH_force_pc_resume during shutdown.
     241             :    */
     242             :   enum GNUNET_GenericReturnValue suspended;
     243             : 
     244             :   /**
     245             :    * What state are we long-polling for?
     246             :    */
     247             :   enum TALER_EXCHANGE_KycLongPollTarget lpt;
     248             : 
     249             :   /**
     250             :    * True if @e h_wire was given.
     251             :    */
     252             :   bool have_h_wire;
     253             : 
     254             :   /**
     255             :    * We're still waiting on the exchange to determine
     256             :    * the KYC status of our deposit(s).
     257             :    */
     258             :   bool return_immediately;
     259             : 
     260             : };
     261             : 
     262             : 
     263             : /**
     264             :  * Head of DLL.
     265             :  */
     266             : static struct KycContext *kc_head;
     267             : 
     268             : /**
     269             :  * Tail of DLL.
     270             :  */
     271             : static struct KycContext *kc_tail;
     272             : 
     273             : 
     274             : void
     275          15 : TMH_force_kyc_resume ()
     276             : {
     277          15 :   for (struct KycContext *kc = kc_head;
     278          15 :        NULL != kc;
     279           0 :        kc = kc->next)
     280             :   {
     281           0 :     if (GNUNET_YES == kc->suspended)
     282             :     {
     283           0 :       kc->suspended = GNUNET_SYSERR;
     284           0 :       MHD_resume_connection (kc->connection);
     285             :     }
     286             :   }
     287          15 : }
     288             : 
     289             : 
     290             : /**
     291             :  * Custom cleanup routine for a `struct KycContext`.
     292             :  *
     293             :  * @param cls the `struct KycContext` to clean up.
     294             :  */
     295             : static void
     296           9 : kyc_context_cleanup (void *cls)
     297             : {
     298           9 :   struct KycContext *kc = cls;
     299             :   struct ExchangeKycRequest *ekr;
     300             : 
     301           9 :   while (NULL != (ekr = kc->exchange_pending_head))
     302             :   {
     303           0 :     GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
     304             :                                  kc->exchange_pending_tail,
     305             :                                  ekr);
     306           0 :     if (NULL != ekr->fo)
     307             :     {
     308           0 :       TMH_EXCHANGES_keys4exchange_cancel (ekr->fo);
     309           0 :       ekr->fo = NULL;
     310             :     }
     311           0 :     json_decref (ekr->pkaa);
     312           0 :     json_decref (ekr->jlimits);
     313           0 :     if (NULL != ekr->keys)
     314           0 :       TALER_EXCHANGE_keys_decref (ekr->keys);
     315           0 :     GNUNET_free (ekr->exchange_url);
     316           0 :     GNUNET_free (ekr->payto_uri.full_payto);
     317           0 :     GNUNET_free (ekr);
     318             :   }
     319           9 :   if (NULL != kc->eh)
     320             :   {
     321           4 :     TMH_db->event_listen_cancel (kc->eh);
     322           4 :     kc->eh = NULL;
     323             :   }
     324           9 :   if (NULL != kc->response)
     325             :   {
     326           7 :     MHD_destroy_response (kc->response);
     327           7 :     kc->response = NULL;
     328             :   }
     329           9 :   GNUNET_CONTAINER_DLL_remove (kc_head,
     330             :                                kc_tail,
     331             :                                kc);
     332           9 :   json_decref (kc->kycs_data);
     333           9 :   GNUNET_free (kc);
     334           9 : }
     335             : 
     336             : 
     337             : /**
     338             :  * Resume the given KYC context and send the final response.  Stores the
     339             :  * response in the @a kc and signals MHD to resume the connection.  Also
     340             :  * ensures MHD runs immediately.
     341             :  *
     342             :  * @param kc KYC context
     343             :  */
     344             : static void
     345           7 : resume_kyc_with_response (struct KycContext *kc)
     346             : {
     347           7 :   kc->response_code = MHD_HTTP_OK;
     348           7 :   kc->response = TALER_MHD_MAKE_JSON_PACK (
     349             :     GNUNET_JSON_pack_array_incref ("kyc_data",
     350             :                                    kc->kycs_data));
     351           7 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     352             :               "Resuming /kyc handling as exchange interaction is done (%u)\n",
     353             :               MHD_HTTP_OK);
     354           7 :   if (GNUNET_YES == kc->suspended)
     355             :   {
     356           3 :     kc->suspended = GNUNET_NO;
     357           3 :     MHD_resume_connection (kc->connection);
     358           3 :     TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
     359             :   }
     360           7 : }
     361             : 
     362             : 
     363             : /**
     364             :  * Handle a DB event about an update relevant
     365             :  * for the processing of the kyc request.
     366             :  *
     367             :  * @param cls our `struct KycContext`
     368             :  * @param extra additional event data provided
     369             :  * @param extra_size number of bytes in @a extra
     370             :  */
     371             : static void
     372           1 : kyc_change_cb (void *cls,
     373             :                const void *extra,
     374             :                size_t extra_size)
     375             : {
     376           1 :   struct KycContext *kc = cls;
     377             : 
     378           1 :   if (GNUNET_YES == kc->suspended)
     379             :   {
     380           1 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     381             :                 "Resuming KYC with gateway timeout\n");
     382           1 :     kc->suspended = GNUNET_NO;
     383           1 :     MHD_resume_connection (kc->connection);
     384           1 :     TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
     385             :   }
     386           1 : }
     387             : 
     388             : 
     389             : /**
     390             :  * Pack the given @a limit into the JSON @a limits array.
     391             :  *
     392             :  * @param limit account limit to pack
     393             :  * @param[in,out] limits JSON array to extend
     394             :  */
     395             : static void
     396           0 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
     397             :             json_t *limits)
     398             : {
     399             :   json_t *jl;
     400             : 
     401           0 :   jl = GNUNET_JSON_PACK (
     402             :     TALER_JSON_pack_kycte ("operation_type",
     403             :                            limit->operation_type),
     404             :     GNUNET_JSON_pack_time_rel ("timeframe",
     405             :                                limit->timeframe),
     406             :     TALER_JSON_pack_amount ("threshold",
     407             :                             &limit->threshold),
     408             :     GNUNET_JSON_pack_bool ("soft_limit",
     409             :                            limit->soft_limit)
     410             :     );
     411           0 :   GNUNET_assert (0 ==
     412             :                  json_array_append_new (limits,
     413             :                                         jl));
     414           0 : }
     415             : 
     416             : 
     417             : /**
     418             :  * Return JSON array with AccountLimit objects giving
     419             :  * the current limits for this exchange.
     420             :  *
     421             :  * @param[in,out] ekr overall request context
     422             :  */
     423             : static json_t *
     424           9 : get_exchange_limits (
     425             :   struct ExchangeKycRequest *ekr)
     426             : {
     427           9 :   const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
     428             :   json_t *limits;
     429             : 
     430           9 :   if (NULL != ekr->jlimits)
     431             :   {
     432           4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     433             :                 "Returning custom KYC limits\n");
     434           4 :     return json_incref (ekr->jlimits);
     435             :   }
     436           5 :   if (NULL == keys)
     437             :   {
     438           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     439             :                 "No keys, thus no default KYC limits known\n");
     440           0 :     return NULL;
     441             :   }
     442           5 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     443             :               "Returning default KYC limits (%u/%u)\n",
     444             :               keys->hard_limits_length,
     445             :               keys->zero_limits_length);
     446           5 :   limits = json_array ();
     447           5 :   GNUNET_assert (NULL != limits);
     448           5 :   for (unsigned int i = 0; i<keys->hard_limits_length; i++)
     449             :   {
     450           0 :     const struct TALER_EXCHANGE_AccountLimit *limit
     451           0 :       = &keys->hard_limits[i];
     452             : 
     453           0 :     pack_limit (limit,
     454             :                 limits);
     455             :   }
     456           7 :   for (unsigned int i = 0; i<keys->zero_limits_length; i++)
     457             :   {
     458           2 :     const struct TALER_EXCHANGE_ZeroLimitedOperation *zlimit
     459           2 :       = &keys->zero_limits[i];
     460             :     json_t *jl;
     461             :     struct TALER_Amount zero;
     462             : 
     463           2 :     GNUNET_assert (GNUNET_OK ==
     464             :                    TALER_amount_set_zero (keys->currency,
     465             :                                           &zero));
     466           2 :     jl = GNUNET_JSON_PACK (
     467             :       TALER_JSON_pack_kycte ("operation_type",
     468             :                              zlimit->operation_type),
     469             :       GNUNET_JSON_pack_time_rel ("timeframe",
     470             :                                  GNUNET_TIME_UNIT_ZERO),
     471             :       TALER_JSON_pack_amount ("threshold",
     472             :                               &zero),
     473             :       GNUNET_JSON_pack_bool ("soft_limit",
     474             :                              true)
     475             :       );
     476           2 :     GNUNET_assert (0 ==
     477             :                    json_array_append_new (limits,
     478             :                                           jl));
     479             :   }
     480           5 :   return limits;
     481             : }
     482             : 
     483             : 
     484             : /**
     485             :  * Maps @a ekr to a status code for clients to interpret the
     486             :  * overall result.
     487             :  *
     488             :  * @param ekr request summary
     489             :  * @return status of the KYC state as a string
     490             :  */
     491             : static const char *
     492           9 : map_to_status (const struct ExchangeKycRequest *ekr)
     493             : {
     494           9 :   if (ekr->no_keys)
     495             :   {
     496           0 :     return "no-exchange-keys";
     497             :   }
     498           9 :   if (ekr->kyc_ok)
     499             :   {
     500           7 :     if (NULL != ekr->jlimits)
     501             :     {
     502             :       size_t off;
     503             :       json_t *limit;
     504           4 :       json_array_foreach (ekr->jlimits, off, limit)
     505             :       {
     506             :         struct TALER_Amount threshold;
     507             :         enum TALER_KYCLOGIC_KycTriggerEvent operation_type;
     508           4 :         bool soft = false;
     509             :         struct GNUNET_JSON_Specification spec[] = {
     510           4 :           TALER_JSON_spec_kycte ("operation_type",
     511             :                                  &operation_type),
     512           4 :           TALER_JSON_spec_amount_any ("threshold",
     513             :                                       &threshold),
     514           4 :           GNUNET_JSON_spec_mark_optional (
     515             :             GNUNET_JSON_spec_bool ("soft_limit",
     516             :                                    &soft),
     517             :             NULL),
     518           4 :           GNUNET_JSON_spec_end ()
     519             :         };
     520             : 
     521           4 :         if (GNUNET_OK !=
     522           4 :             GNUNET_JSON_parse (limit,
     523             :                                spec,
     524             :                                NULL, NULL))
     525             :         {
     526           0 :           GNUNET_break (0);
     527           4 :           return "merchant-internal-error";
     528             :         }
     529           4 :         if (! TALER_amount_is_zero (&threshold))
     530           0 :           continue; /* only care about zero-limits */
     531           4 :         if (! soft)
     532           0 :           continue; /* only care about soft limits */
     533           4 :         if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
     534           4 :              (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
     535           0 :              (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
     536             :         {
     537           4 :           if (! ekr->auth_ok)
     538             :           {
     539           0 :             if (ekr->kyc_auth_conflict)
     540           0 :               return "kyc-wire-impossible";
     541           0 :             return "kyc-wire-required";
     542             :           }
     543           4 :           return "kyc-required";
     544             :         }
     545             :       }
     546             :     }
     547           3 :     if (NULL == ekr->jlimits)
     548             :     {
     549             :       /* check default limits */
     550           3 :       const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
     551             : 
     552           3 :       for (unsigned int i = 0; i < keys->zero_limits_length; i++)
     553             :       {
     554           0 :         enum TALER_KYCLOGIC_KycTriggerEvent operation_type
     555           0 :           = keys->zero_limits[i].operation_type;
     556             : 
     557           0 :         if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
     558           0 :              (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
     559             :              (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
     560             :         {
     561           0 :           if (! ekr->auth_ok)
     562             :           {
     563           0 :             if (ekr->kyc_auth_conflict)
     564           0 :               return "kyc-wire-impossible";
     565           0 :             return "kyc-wire-required";
     566             :           }
     567           0 :           return "kyc-required";
     568             :         }
     569             :       }
     570             :     }
     571           3 :     return "ready";
     572             :   }
     573           2 :   if (! ekr->auth_ok)
     574             :   {
     575           2 :     if (ekr->kyc_auth_conflict)
     576           0 :       return "kyc-wire-impossible";
     577           2 :     return "kyc-wire-required";
     578             :   }
     579           0 :   if (ekr->in_aml_review)
     580           0 :     return "awaiting-aml-review";
     581           0 :   switch (ekr->last_http_status)
     582             :   {
     583           0 :   case 0:
     584           0 :     return "exchange-unreachable";
     585           0 :   case MHD_HTTP_OK:
     586             :     /* then we should have kyc_ok */
     587           0 :     GNUNET_break (0);
     588           0 :     return NULL;
     589           0 :   case MHD_HTTP_ACCEPTED:
     590             :     /* Then KYC is really what  is needed */
     591           0 :     return "kyc-required";
     592           0 :   case MHD_HTTP_NO_CONTENT:
     593             :     /* then we should have had kyc_ok! */
     594           0 :     GNUNET_break (0);
     595           0 :     return NULL;
     596           0 :   case MHD_HTTP_FORBIDDEN:
     597             :     /* then we should have had ! auth_ok */
     598           0 :     GNUNET_break (0);
     599           0 :     return NULL;
     600           0 :   case MHD_HTTP_NOT_FOUND:
     601             :     /* then we should have had ! auth_ok */
     602           0 :     GNUNET_break (0);
     603           0 :     return NULL;
     604           0 :   case MHD_HTTP_CONFLICT:
     605             :     /* then we should have had ! auth_ok */
     606           0 :     GNUNET_break (0);
     607           0 :     return NULL;
     608           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     609           0 :     return "exchange-internal-error";
     610           0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     611           0 :     return "exchange-gateway-timeout";
     612           0 :   default:
     613           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     614             :                 "Exchange responded with unexpected HTTP status %u to /kyc-check request!\n",
     615             :                 ekr->last_http_status);
     616           0 :     break;
     617             :   }
     618           0 :   return "exchange-status-invalid";
     619             : }
     620             : 
     621             : 
     622             : /**
     623             :  * Take data from @a ekr to expand our response.
     624             :  *
     625             :  * @param ekr exchange we are done inspecting
     626             :  */
     627             : static void
     628           9 : ekr_expand_response (struct ExchangeKycRequest *ekr)
     629             : {
     630             :   const char *status;
     631             : 
     632           9 :   status = map_to_status (ekr);
     633           9 :   if (NULL == status)
     634             :   {
     635           0 :     GNUNET_break (0);
     636           0 :     status = "logic-bug";
     637             :   }
     638           9 :   GNUNET_assert (
     639             :     0 ==
     640             :     json_array_append_new (
     641             :       ekr->kc->kycs_data,
     642             :       GNUNET_JSON_PACK (
     643             :         TALER_JSON_pack_full_payto (
     644             :           "payto_uri",
     645             :           ekr->payto_uri),
     646             :         GNUNET_JSON_pack_data_auto (
     647             :           "h_wire",
     648             :           &ekr->h_wire),
     649             :         GNUNET_JSON_pack_string (
     650             :           "status",
     651             :           status),
     652             :         GNUNET_JSON_pack_string (
     653             :           "exchange_url",
     654             :           ekr->exchange_url),
     655             :         GNUNET_JSON_pack_bool ("no_keys",
     656             :                                ekr->no_keys),
     657             :         GNUNET_JSON_pack_bool ("auth_conflict",
     658             :                                ekr->kyc_auth_conflict),
     659             :         GNUNET_JSON_pack_uint64 ("exchange_http_status",
     660             :                                  ekr->last_http_status),
     661             :         (TALER_EC_NONE == ekr->last_ec)
     662             :         ? GNUNET_JSON_pack_allow_null (
     663             :           GNUNET_JSON_pack_string (
     664             :             "dummy",
     665             :             NULL))
     666             :         : GNUNET_JSON_pack_uint64 ("exchange_code",
     667             :                                    ekr->last_ec),
     668             :         ekr->auth_ok
     669             :         ? GNUNET_JSON_pack_data_auto (
     670             :           "access_token",
     671             :           &ekr->access_token)
     672             :         : GNUNET_JSON_pack_allow_null (
     673             :           GNUNET_JSON_pack_string (
     674             :             "dummy",
     675             :             NULL)),
     676             :         GNUNET_JSON_pack_allow_null (
     677             :           GNUNET_JSON_pack_array_steal (
     678             :             "limits",
     679             :             get_exchange_limits (ekr))),
     680             :         GNUNET_JSON_pack_allow_null (
     681             :           GNUNET_JSON_pack_array_incref ("payto_kycauths",
     682             :                                          ekr->pkaa))
     683             :         )));
     684           9 : }
     685             : 
     686             : 
     687             : /**
     688             :  * We are done with the KYC request @a ekr.  Remove it from the work list and
     689             :  * check if we are done overall.
     690             :  *
     691             :  * @param[in] ekr key request that is done (and will be freed)
     692             :  */
     693             : static void
     694           9 : ekr_finished (struct ExchangeKycRequest *ekr)
     695             : {
     696           9 :   struct KycContext *kc = ekr->kc;
     697             : 
     698           9 :   ekr_expand_response (ekr);
     699           9 :   GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
     700             :                                kc->exchange_pending_tail,
     701             :                                ekr);
     702           9 :   json_decref (ekr->jlimits);
     703           9 :   json_decref (ekr->pkaa);
     704           9 :   if (NULL != ekr->keys)
     705           5 :     TALER_EXCHANGE_keys_decref (ekr->keys);
     706           9 :   GNUNET_free (ekr->exchange_url);
     707           9 :   GNUNET_free (ekr->payto_uri.full_payto);
     708           9 :   GNUNET_free (ekr);
     709             : 
     710           9 :   if (NULL != kc->exchange_pending_head)
     711           1 :     return; /* wait for more */
     712             : 
     713           8 :   if ( (! kc->return_immediately) &&
     714           1 :        (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
     715             :   {
     716           1 :     if (GNUNET_NO == kc->suspended)
     717             :     {
     718           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     719             :                   "Suspending: long poll target %d not reached\n",
     720             :                   kc->lpt);
     721           0 :       MHD_suspend_connection (kc->connection);
     722           0 :       kc->suspended = GNUNET_YES;
     723             :     }
     724             :     else
     725             :     {
     726           1 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     727             :                   "Remaining suspended: long poll target %d not reached\n",
     728             :                   kc->lpt);
     729             :     }
     730           1 :     return;
     731             :   }
     732             :   /* All exchange requests done, create final
     733             :      big response from cumulated replies */
     734           7 :   resume_kyc_with_response (kc);
     735             : }
     736             : 
     737             : 
     738             : /**
     739             :  * Figure out which exchange accounts from @a keys could
     740             :  * be used for a KYC auth wire transfer from the account
     741             :  * that @a ekr is checking. Will set the "pkaa" array
     742             :  * in @a ekr.
     743             :  *
     744             :  * @param[in,out] ekr request we are processing
     745             :  */
     746             : static void
     747           5 : determine_eligible_accounts (
     748             :   struct ExchangeKycRequest *ekr)
     749             : {
     750           5 :   struct KycContext *kc = ekr->kc;
     751           5 :   const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
     752             :   struct TALER_Amount kyc_amount;
     753             :   char *merchant_pub_str;
     754             :   struct TALER_NormalizedPayto np;
     755             : 
     756           5 :   ekr->pkaa = json_array ();
     757           5 :   GNUNET_assert (NULL != ekr->pkaa);
     758             :   {
     759             :     const struct TALER_EXCHANGE_GlobalFee *gf;
     760             : 
     761           5 :     gf = TALER_EXCHANGE_get_global_fee (keys,
     762             :                                         GNUNET_TIME_timestamp_get ());
     763           5 :     if (NULL == gf)
     764             :     {
     765           0 :       GNUNET_assert (GNUNET_OK ==
     766             :                      TALER_amount_set_zero (keys->currency,
     767             :                                             &kyc_amount));
     768             :     }
     769             :     else
     770             :     {
     771             :       /* FIXME-#9427: history fee should be globally renamed to KYC fee... */
     772           5 :       kyc_amount = gf->fees.history;
     773             :     }
     774             :   }
     775             : 
     776             :   merchant_pub_str
     777           5 :     = GNUNET_STRINGS_data_to_string_alloc (
     778           5 :         &kc->mi->merchant_pub,
     779             :         sizeof (kc->mi->merchant_pub));
     780             :   /* For all accounts of the exchange */
     781           5 :   np = TALER_payto_normalize (ekr->payto_uri);
     782          10 :   for (unsigned int i = 0; i<keys->accounts_len; i++)
     783             :   {
     784           5 :     const struct TALER_EXCHANGE_WireAccount *account
     785           5 :       = &keys->accounts[i];
     786             : 
     787             :     /* KYC auth transfers are never supported with conversion */
     788           5 :     if (NULL != account->conversion_url)
     789           0 :       continue;
     790             :     /* filter by source account by credit_restrictions */
     791           5 :     if (GNUNET_YES !=
     792           5 :         TALER_EXCHANGE_test_account_allowed (account,
     793             :                                              true, /* credit */
     794             :                                              np))
     795           0 :       continue;
     796             :     /* exchange account is allowed, add it */
     797             :     {
     798           5 :       const char *exchange_account_payto
     799             :         = account->fpayto_uri.full_payto;
     800             :       char *payto_kycauth;
     801             : 
     802           5 :       if (TALER_amount_is_zero (&kyc_amount))
     803           0 :         GNUNET_asprintf (&payto_kycauth,
     804             :                          "%s%cmessage=KYC:%s",
     805             :                          exchange_account_payto,
     806           0 :                          (NULL == strchr (exchange_account_payto,
     807             :                                           '?'))
     808             :                          ? '?'
     809             :                          : '&',
     810             :                          merchant_pub_str);
     811             :       else
     812          10 :         GNUNET_asprintf (&payto_kycauth,
     813             :                          "%s%camount=%s&message=KYC:%s",
     814             :                          exchange_account_payto,
     815           5 :                          (NULL == strchr (exchange_account_payto,
     816             :                                           '?'))
     817             :                          ? '?'
     818             :                          : '&',
     819             :                          TALER_amount2s (&kyc_amount),
     820             :                          merchant_pub_str);
     821           5 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     822             :                   "Found account %s where KYC auth is possible\n",
     823             :                   payto_kycauth);
     824           5 :       GNUNET_assert (0 ==
     825             :                      json_array_append_new (ekr->pkaa,
     826             :                                             json_string (payto_kycauth)));
     827           5 :       GNUNET_free (payto_kycauth);
     828             :     }
     829             :   }
     830           5 :   GNUNET_free (np.normalized_payto);
     831           5 :   GNUNET_free (merchant_pub_str);
     832           5 : }
     833             : 
     834             : 
     835             : /**
     836             :  * Function called with the result of a #TMH_EXCHANGES_keys4exchange()
     837             :  * operation.  Runs the KYC check against the exchange.
     838             :  *
     839             :  * @param cls closure with our `struct ExchangeKycRequest *`
     840             :  * @param keys keys of the exchange context
     841             :  * @param exchange representation of the exchange
     842             :  */
     843             : static void
     844           5 : kyc_with_exchange (void *cls,
     845             :                    struct TALER_EXCHANGE_Keys *keys,
     846             :                    struct TMH_Exchange *exchange)
     847             : {
     848           5 :   struct ExchangeKycRequest *ekr = cls;
     849             : 
     850             :   (void) exchange;
     851           5 :   ekr->fo = NULL;
     852           5 :   if (NULL == keys)
     853             :   {
     854           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     855             :                 "Failed to download `%skeys`\n",
     856             :                 ekr->exchange_url);
     857           0 :     ekr->no_keys = true;
     858           0 :     ekr_finished (ekr);
     859           0 :     return;
     860             :   }
     861           5 :   ekr->keys = TALER_EXCHANGE_keys_incref (keys);
     862           5 :   if (! ekr->auth_ok)
     863             :   {
     864           5 :     determine_eligible_accounts (ekr);
     865           5 :     if (0 == json_array_size (ekr->pkaa))
     866             :     {
     867             :       /* No KYC auth wire transfers are possible to this exchange from
     868             :          our merchant bank account, so we cannot use this account with
     869             :          this exchange if it has any KYC requirements! */
     870           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     871             :                   "KYC auth to `%s' impossible for merchant account `%s'\n",
     872             :                   ekr->exchange_url,
     873             :                   ekr->payto_uri.full_payto);
     874           0 :       ekr->kyc_auth_conflict = true;
     875             :     }
     876             :   }
     877           5 :   ekr_finished (ekr);
     878             : }
     879             : 
     880             : 
     881             : /**
     882             :  * Function called from account_kyc_get_status() with KYC status information
     883             :  * for this merchant.
     884             :  *
     885             :  * @param cls our `struct KycContext *`
     886             :  * @param h_wire hash of the wire account
     887             :  * @param payto_uri payto:// URI of the merchant's bank account
     888             :  * @param exchange_url base URL of the exchange for which this is a status
     889             :  * @param last_check when did we last get an update on our KYC status from the exchange
     890             :  * @param kyc_ok true if we satisfied the KYC requirements
     891             :  * @param access_token access token for the KYC SPA, NULL if we cannot access it yet (need KYC auth wire transfer)
     892             :  * @param last_http_status last HTTP status from /kyc-check
     893             :  * @param last_ec last Taler error code from /kyc-check
     894             :  * @param in_aml_review true if the account is pending review
     895             :  * @param jlimits JSON array of applicable AccountLimits, or NULL if unknown (like defaults apply)
     896             :  */
     897             : static void
     898           9 : kyc_status_cb (
     899             :   void *cls,
     900             :   const struct TALER_MerchantWireHashP *h_wire,
     901             :   struct TALER_FullPayto payto_uri,
     902             :   const char *exchange_url,
     903             :   struct GNUNET_TIME_Timestamp last_check,
     904             :   bool kyc_ok,
     905             :   const struct TALER_AccountAccessTokenP *access_token,
     906             :   unsigned int last_http_status,
     907             :   enum TALER_ErrorCode last_ec,
     908             :   bool in_aml_review,
     909             :   const json_t *jlimits)
     910             : {
     911           9 :   struct KycContext *kc = cls;
     912             :   struct ExchangeKycRequest *ekr;
     913             : 
     914           9 :   if (! TMH_EXCHANGES_check_trusted (exchange_url))
     915             :   {
     916           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     917             :                 "Skipping exchange `%s': not trusted\n",
     918             :                 exchange_url);
     919           0 :     return;
     920             :   }
     921           9 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     922             :               "KYC status for `%s' at `%s' is %u/%s/%s/%s\n",
     923             :               payto_uri.full_payto,
     924             :               exchange_url,
     925             :               last_http_status,
     926             :               kyc_ok ? "KYC OK" : "KYC NEEDED",
     927             :               in_aml_review ? "IN AML REVIEW" : "NO AML REVIEW",
     928             :               NULL == jlimits ? "DEFAULT LIMITS" : "CUSTOM LIMITS");
     929           9 :   switch (kc->lpt)
     930             :   {
     931           4 :   case TALER_EXCHANGE_KLPT_NONE:
     932           4 :     break;
     933           4 :   case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER:
     934           4 :     if (NULL != access_token)
     935           3 :       kc->return_immediately = true;
     936           4 :     break;
     937           0 :   case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE:
     938           0 :     if (! in_aml_review)
     939           0 :       kc->return_immediately = true;
     940           0 :     break;
     941           1 :   case TALER_EXCHANGE_KLPT_KYC_OK:
     942           1 :     if (kyc_ok)
     943           1 :       kc->return_immediately = true;
     944           1 :     break;
     945             :   }
     946           9 :   ekr = GNUNET_new (struct ExchangeKycRequest);
     947           9 :   GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
     948             :                                kc->exchange_pending_tail,
     949             :                                ekr);
     950           9 :   ekr->last_http_status = last_http_status;
     951           9 :   ekr->last_ec = last_ec;
     952           9 :   if (NULL != jlimits)
     953           4 :     ekr->jlimits = json_incref ((json_t *) jlimits);
     954           9 :   ekr->h_wire = *h_wire;
     955           9 :   ekr->exchange_url = GNUNET_strdup (exchange_url);
     956             :   ekr->payto_uri.full_payto
     957           9 :     = GNUNET_strdup (payto_uri.full_payto);
     958           9 :   ekr->last_check = last_check;
     959           9 :   ekr->kyc_ok = kyc_ok;
     960           9 :   ekr->kc = kc;
     961           9 :   ekr->in_aml_review = in_aml_review;
     962           9 :   ekr->auth_ok = (NULL != access_token);
     963           9 :   if ( (! ekr->auth_ok) ||
     964           4 :        (NULL == ekr->jlimits) )
     965             :   {
     966             :     /* Figure out wire transfer instructions */
     967           5 :     if (GNUNET_NO == kc->suspended)
     968             :     {
     969           4 :       MHD_suspend_connection (kc->connection);
     970           4 :       kc->suspended = GNUNET_YES;
     971             :     }
     972           5 :     ekr->fo = TMH_EXCHANGES_keys4exchange (
     973             :       exchange_url,
     974             :       false,
     975             :       &kyc_with_exchange,
     976             :       ekr);
     977           5 :     if (NULL == ekr->fo)
     978             :     {
     979           0 :       GNUNET_break (0);
     980           0 :       ekr_finished (ekr);
     981           0 :       return;
     982             :     }
     983           5 :     return;
     984             :   }
     985           4 :   ekr->access_token = *access_token;
     986           4 :   ekr_finished (ekr);
     987             : }
     988             : 
     989             : 
     990             : /**
     991             :  * Check the KYC status of an instance.
     992             :  *
     993             :  * @param mi instance to check KYC status of
     994             :  * @param connection the MHD connection to handle
     995             :  * @param[in,out] hc context with further information about the request
     996             :  * @return MHD result code
     997             :  */
     998             : static MHD_RESULT
     999          13 : get_instances_ID_kyc (
    1000             :   struct TMH_MerchantInstance *mi,
    1001             :   struct MHD_Connection *connection,
    1002             :   struct TMH_HandlerContext *hc)
    1003             : {
    1004          13 :   struct KycContext *kc = hc->ctx;
    1005             : 
    1006          13 :   if (NULL == kc)
    1007             :   {
    1008           9 :     kc = GNUNET_new (struct KycContext);
    1009           9 :     kc->mi = mi;
    1010           9 :     hc->ctx = kc;
    1011           9 :     hc->cc = &kyc_context_cleanup;
    1012           9 :     GNUNET_CONTAINER_DLL_insert (kc_head,
    1013             :                                  kc_tail,
    1014             :                                  kc);
    1015           9 :     kc->connection = connection;
    1016           9 :     kc->hc = hc;
    1017           9 :     kc->kycs_data = json_array ();
    1018           9 :     GNUNET_assert (NULL != kc->kycs_data);
    1019           9 :     TALER_MHD_parse_request_timeout (connection,
    1020             :                                      &kc->timeout);
    1021             :     {
    1022           9 :       uint64_t num = 0;
    1023             :       int val;
    1024             : 
    1025           9 :       TALER_MHD_parse_request_number (connection,
    1026             :                                       "lpt",
    1027             :                                       &num);
    1028           9 :       val = (int) num;
    1029           9 :       if ( (val < 0) ||
    1030             :            (val > TALER_EXCHANGE_KLPT_MAX) )
    1031             :       {
    1032             :         /* Protocol violation, but we can be graceful and
    1033             :            just ignore the long polling! */
    1034           0 :         GNUNET_break_op (0);
    1035           0 :         val = TALER_EXCHANGE_KLPT_NONE;
    1036             :       }
    1037           9 :       kc->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val;
    1038             :     }
    1039             :     kc->return_immediately
    1040           9 :       = (TALER_EXCHANGE_KLPT_NONE == kc->lpt);
    1041             :     /* process 'exchange_url' argument */
    1042           9 :     kc->exchange_url = MHD_lookup_connection_value (
    1043             :       connection,
    1044             :       MHD_GET_ARGUMENT_KIND,
    1045             :       "exchange_url");
    1046           9 :     if ( (NULL != kc->exchange_url) &&
    1047           7 :          ( (! TALER_url_valid_charset (kc->exchange_url)) ||
    1048           7 :            (! TALER_is_web_url (kc->exchange_url)) ) )
    1049             :     {
    1050           0 :       GNUNET_break_op (0);
    1051           0 :       return TALER_MHD_reply_with_error (
    1052             :         connection,
    1053             :         MHD_HTTP_BAD_REQUEST,
    1054             :         TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1055             :         "exchange_url must be a valid HTTP(s) URL");
    1056             :     }
    1057             : 
    1058           9 :     TALER_MHD_parse_request_arg_auto (connection,
    1059             :                                       "h_wire",
    1060             :                                       &kc->h_wire,
    1061             :                                       kc->have_h_wire);
    1062             : 
    1063           9 :     if ( (TALER_EXCHANGE_KLPT_NONE != kc->lpt) &&
    1064           4 :          (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
    1065             :     {
    1066           4 :       if (kc->have_h_wire)
    1067             :       {
    1068           2 :         struct TALER_MERCHANTDB_MerchantKycStatusChangeEventP ev = {
    1069           2 :           .header.size = htons (sizeof (ev)),
    1070           2 :           .header.type = htons (
    1071             :             TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_STATUS_CHANGED
    1072             :             ),
    1073             :           .h_wire = kc->h_wire
    1074             :         };
    1075             : 
    1076           4 :         kc->eh = TMH_db->event_listen (
    1077           2 :           TMH_db->cls,
    1078             :           &ev.header,
    1079             :           GNUNET_TIME_absolute_get_remaining (kc->timeout),
    1080             :           &kyc_change_cb,
    1081             :           kc);
    1082             :       }
    1083             :       else
    1084             :       {
    1085           2 :         struct GNUNET_DB_EventHeaderP hdr = {
    1086           2 :           .size = htons (sizeof (hdr)),
    1087           2 :           .type = htons (TALER_DBEVENT_MERCHANT_KYC_STATUS_CHANGED)
    1088             :         };
    1089             : 
    1090           4 :         kc->eh = TMH_db->event_listen (
    1091           2 :           TMH_db->cls,
    1092             :           &hdr,
    1093             :           GNUNET_TIME_absolute_get_remaining (kc->timeout),
    1094             :           &kyc_change_cb,
    1095             :           kc);
    1096             :       }
    1097             :     } /* end register LISTEN hooks */
    1098             :   } /* end 1st time initialization */
    1099             : 
    1100          13 :   if (GNUNET_SYSERR == kc->suspended)
    1101           0 :     return MHD_NO; /* during shutdown, we don't generate any more replies */
    1102          13 :   GNUNET_assert (GNUNET_NO == kc->suspended);
    1103             : 
    1104          13 :   if (NULL != kc->response)
    1105           3 :     return MHD_queue_response (connection,
    1106             :                                kc->response_code,
    1107             :                                kc->response);
    1108             : 
    1109             :   /* Check our database */
    1110             :   {
    1111             :     enum GNUNET_DB_QueryStatus qs;
    1112             : 
    1113          10 :     GNUNET_break (0 ==
    1114             :                   json_array_clear (kc->kycs_data));
    1115          10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1116             :                 "Checking KYC status for %s (%d/%s)\n",
    1117             :                 mi->settings.id,
    1118             :                 kc->have_h_wire,
    1119             :                 kc->exchange_url);
    1120          10 :     qs = TMH_db->account_kyc_get_status (
    1121          10 :       TMH_db->cls,
    1122          10 :       mi->settings.id,
    1123          10 :       kc->have_h_wire
    1124             :       ? &kc->h_wire
    1125             :       : NULL,
    1126             :       kc->exchange_url,
    1127             :       &kyc_status_cb,
    1128             :       kc);
    1129          10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1130             :                 "account_kyc_get_status returned %d records\n",
    1131             :                 (int) qs);
    1132          10 :     if (qs < 0)
    1133             :     {
    1134             :       /* Database error */
    1135           0 :       GNUNET_break (0);
    1136           0 :       if (GNUNET_YES == kc->suspended)
    1137             :       {
    1138             :         /* must have suspended before DB error, resume! */
    1139           0 :         MHD_resume_connection (connection);
    1140           0 :         kc->suspended = GNUNET_NO;
    1141             :       }
    1142           0 :       return TALER_MHD_reply_with_ec (
    1143             :         connection,
    1144             :         TALER_EC_GENERIC_DB_FETCH_FAILED,
    1145             :         "account_kyc_get_status");
    1146             :     }
    1147          10 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1148             :     {
    1149             :       /* no matching accounts, could not have suspended */
    1150           2 :       GNUNET_assert (GNUNET_NO == kc->suspended);
    1151           2 :       return TALER_MHD_reply_static (connection,
    1152             :                                      MHD_HTTP_NO_CONTENT,
    1153             :                                      NULL,
    1154             :                                      NULL,
    1155             :                                      0);
    1156             :     }
    1157             :   }
    1158           8 :   if (GNUNET_YES == kc->suspended)
    1159           4 :     return MHD_YES;
    1160             :   /* Should have generated a response */
    1161           4 :   GNUNET_break (NULL != kc->response);
    1162           4 :   return MHD_queue_response (connection,
    1163             :                              kc->response_code,
    1164             :                              kc->response);
    1165             : }
    1166             : 
    1167             : 
    1168             : MHD_RESULT
    1169          13 : TMH_private_get_instances_ID_kyc (
    1170             :   const struct TMH_RequestHandler *rh,
    1171             :   struct MHD_Connection *connection,
    1172             :   struct TMH_HandlerContext *hc)
    1173             : {
    1174          13 :   struct TMH_MerchantInstance *mi = hc->instance;
    1175             : 
    1176             :   (void) rh;
    1177          13 :   return get_instances_ID_kyc (mi,
    1178             :                                connection,
    1179             :                                hc);
    1180             : }
    1181             : 
    1182             : 
    1183             : MHD_RESULT
    1184           0 : TMH_private_get_instances_default_ID_kyc (
    1185             :   const struct TMH_RequestHandler *rh,
    1186             :   struct MHD_Connection *connection,
    1187             :   struct TMH_HandlerContext *hc)
    1188             : {
    1189             :   struct TMH_MerchantInstance *mi;
    1190             : 
    1191             :   (void) rh;
    1192           0 :   mi = TMH_lookup_instance (hc->infix);
    1193           0 :   if (NULL == mi)
    1194             :   {
    1195           0 :     return TALER_MHD_reply_with_error (
    1196             :       connection,
    1197             :       MHD_HTTP_NOT_FOUND,
    1198             :       TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    1199           0 :       hc->infix);
    1200             :   }
    1201           0 :   return get_instances_ID_kyc (mi,
    1202             :                                connection,
    1203             :                                hc);
    1204             : }
    1205             : 
    1206             : 
    1207             : /* end of taler-merchant-httpd_private-get-instances-ID-kyc.c */

Generated by: LCOV version 1.16