LCOV - code coverage report
Current view: top level - lib - merchant_api_get_kyc.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.5 % 155 117
Test Date: 2025-11-19 16:18:51 Functions: 83.3 % 6 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023--2024 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Lesser General Public License as published by the Free Software
       7              :   Foundation; either version 2.1, 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 Lesser General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Lesser General Public License along with
      14              :   TALER; see the file COPYING.LGPL.  If not, see
      15              :   <http://www.gnu.org/licenses/>
      16              : */
      17              : /**
      18              :  * @file merchant_api_get_kyc.c
      19              :  * @brief Implementation of the GET /kyc request of the merchant's HTTP API
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <curl/curl.h>
      24              : #include <jansson.h>
      25              : #include <microhttpd.h> /* just for HTTP status codes */
      26              : #include <gnunet/gnunet_util_lib.h>
      27              : #include <gnunet/gnunet_curl_lib.h>
      28              : #include "taler_merchant_service.h"
      29              : #include "merchant_api_curl_defaults.h"
      30              : #include <taler/taler_json_lib.h>
      31              : #include <taler/taler_signatures.h>
      32              : 
      33              : 
      34              : /**
      35              :  * Maximum length of the KYC arrays supported.
      36              :  */
      37              : #define MAX_KYC 1024
      38              : 
      39              : /**
      40              :  * Handle for a GET /kyc operation.
      41              :  */
      42              : struct TALER_MERCHANT_KycGetHandle
      43              : {
      44              :   /**
      45              :    * The url for this request.
      46              :    */
      47              :   char *url;
      48              : 
      49              :   /**
      50              :    * Handle for the request.
      51              :    */
      52              :   struct GNUNET_CURL_Job *job;
      53              : 
      54              :   /**
      55              :    * Function to call with the result.
      56              :    */
      57              :   TALER_MERCHANT_KycGetCallback cb;
      58              : 
      59              :   /**
      60              :    * Closure for @a cb.
      61              :    */
      62              :   void *cb_cls;
      63              : 
      64              :   /**
      65              :    * Reference to the execution context.
      66              :    */
      67              :   struct GNUNET_CURL_Context *ctx;
      68              : 
      69              : };
      70              : 
      71              : 
      72              : /**
      73              :  * Parse @a kyc response and call the continuation on success.
      74              :  *
      75              :  * @param kyc operation handle
      76              :  * @param[in,out] kr response details
      77              :  * @param jkyc array from the reply
      78              :  * @return #GNUNET_OK on success (callback was called)
      79              :  */
      80              : static enum GNUNET_GenericReturnValue
      81            5 : parse_kyc (struct TALER_MERCHANT_KycGetHandle *kyc,
      82              :            struct TALER_MERCHANT_KycResponse *kr,
      83              :            const json_t *jkyc)
      84              : {
      85            5 :   unsigned int num_kycs = (unsigned int) json_array_size (jkyc);
      86            5 :   unsigned int num_limits = 0;
      87            5 :   unsigned int num_kycauths = 0;
      88            5 :   unsigned int pos_limits = 0;
      89            5 :   unsigned int pos_kycauths = 0;
      90              : 
      91            5 :   if ( (json_array_size (jkyc) != (size_t) num_kycs) ||
      92              :        (num_kycs > MAX_KYC) )
      93              :   {
      94            0 :     GNUNET_break (0);
      95            0 :     return GNUNET_SYSERR;
      96              :   }
      97              : 
      98           10 :   for (unsigned int i = 0; i<num_kycs; i++)
      99              :   {
     100            5 :     const json_t *jlimits = NULL;
     101            5 :     const json_t *jkycauths = NULL;
     102              :     struct GNUNET_JSON_Specification spec[] = {
     103            5 :       GNUNET_JSON_spec_mark_optional (
     104              :         GNUNET_JSON_spec_array_const (
     105              :           "limits",
     106              :           &jlimits),
     107              :         NULL),
     108            5 :       GNUNET_JSON_spec_mark_optional (
     109              :         GNUNET_JSON_spec_array_const (
     110              :           "payto_kycauths",
     111              :           &jkycauths),
     112              :         NULL),
     113            5 :       GNUNET_JSON_spec_end ()
     114              :     };
     115              : 
     116            5 :     if (GNUNET_OK !=
     117            5 :         GNUNET_JSON_parse (json_array_get (jkyc,
     118              :                                            i),
     119              :                            spec,
     120              :                            NULL, NULL))
     121              :     {
     122            0 :       GNUNET_break (0);
     123            0 :       json_dumpf (json_array_get (jkyc,
     124              :                                   i),
     125              :                   stderr,
     126              :                   JSON_INDENT (2));
     127            0 :       return GNUNET_SYSERR;
     128              :     }
     129            5 :     num_limits += json_array_size (jlimits);
     130            5 :     num_kycauths += json_array_size (jkycauths);
     131              :   }
     132              : 
     133              : 
     134            5 :   {
     135            5 :     struct TALER_MERCHANT_AccountKycRedirectDetail kycs[
     136            5 :       GNUNET_NZL (num_kycs)];
     137            5 :     struct TALER_EXCHANGE_AccountLimit limits[
     138            5 :       GNUNET_NZL (num_limits)];
     139            5 :     struct TALER_FullPayto payto_kycauths[
     140            5 :       GNUNET_NZL (num_kycauths)];
     141              : 
     142            5 :     memset (kycs,
     143              :             0,
     144              :             sizeof (kycs));
     145           10 :     for (unsigned int i = 0; i<num_kycs; i++)
     146              :     {
     147            5 :       struct TALER_MERCHANT_AccountKycRedirectDetail *rd
     148              :         = &kycs[i];
     149            5 :       const json_t *jlimits = NULL;
     150            5 :       const json_t *jkycauths = NULL;
     151              :       uint32_t hs;
     152              :       struct GNUNET_JSON_Specification spec[] = {
     153            5 :         TALER_JSON_spec_full_payto_uri (
     154              :           "payto_uri",
     155              :           &rd->payto_uri),
     156            5 :         TALER_JSON_spec_web_url (
     157              :           "exchange_url",
     158              :           &rd->exchange_url),
     159            5 :         GNUNET_JSON_spec_uint32 (
     160              :           "exchange_http_status",
     161              :           &hs),
     162            5 :         GNUNET_JSON_spec_bool (
     163              :           "no_keys",
     164              :           &rd->no_keys),
     165            5 :         GNUNET_JSON_spec_bool (
     166              :           "auth_conflict",
     167              :           &rd->auth_conflict),
     168            5 :         GNUNET_JSON_spec_mark_optional (
     169              :           TALER_JSON_spec_ec (
     170              :             "exchange_code",
     171              :             &rd->exchange_code),
     172              :           NULL),
     173            5 :         GNUNET_JSON_spec_mark_optional (
     174            5 :           GNUNET_JSON_spec_fixed_auto (
     175              :             "access_token",
     176              :             &rd->access_token),
     177              :           &rd->no_access_token),
     178            5 :         GNUNET_JSON_spec_mark_optional (
     179              :           GNUNET_JSON_spec_array_const (
     180              :             "limits",
     181              :             &jlimits),
     182              :           NULL),
     183            5 :         GNUNET_JSON_spec_mark_optional (
     184              :           GNUNET_JSON_spec_array_const (
     185              :             "payto_kycauths",
     186              :             &jkycauths),
     187              :           NULL),
     188            5 :         GNUNET_JSON_spec_end ()
     189              :       };
     190              :       size_t j;
     191              :       json_t *jlimit;
     192              :       json_t *jkycauth;
     193              : 
     194            5 :       if (GNUNET_OK !=
     195            5 :           GNUNET_JSON_parse (json_array_get (jkyc,
     196              :                                              i),
     197              :                              spec,
     198              :                              NULL, NULL))
     199              :       {
     200            0 :         GNUNET_break (0);
     201            0 :         json_dumpf (json_array_get (jkyc,
     202              :                                     i),
     203              :                     stderr,
     204              :                     JSON_INDENT (2));
     205            0 :         return GNUNET_SYSERR;
     206              :       }
     207            5 :       rd->exchange_http_status = (unsigned int) hs;
     208            5 :       rd->limits = &limits[pos_limits];
     209            5 :       rd->limits_length = json_array_size (jlimits);
     210           10 :       json_array_foreach (jlimits, j, jlimit)
     211              :       {
     212            5 :         struct TALER_EXCHANGE_AccountLimit *limit
     213              :           = &limits[pos_limits];
     214              :         struct GNUNET_JSON_Specification jspec[] = {
     215            5 :           TALER_JSON_spec_kycte (
     216              :             "operation_type",
     217              :             &limit->operation_type),
     218            5 :           GNUNET_JSON_spec_relative_time (
     219              :             "timeframe",
     220              :             &limit->timeframe),
     221            5 :           TALER_JSON_spec_amount_any (
     222              :             "threshold",
     223              :             &limit->threshold),
     224            5 :           GNUNET_JSON_spec_mark_optional (
     225              :             GNUNET_JSON_spec_bool (
     226              :               "soft_limit",
     227              :               &limit->soft_limit),
     228              :             NULL),
     229            5 :           GNUNET_JSON_spec_end ()
     230              :         };
     231              : 
     232            5 :         GNUNET_assert (pos_limits < num_limits);
     233            5 :         limit->soft_limit = false;
     234            5 :         if (GNUNET_OK !=
     235            5 :             GNUNET_JSON_parse (jlimit,
     236              :                                jspec,
     237              :                                NULL, NULL))
     238              :         {
     239            0 :           GNUNET_break (0);
     240            0 :           json_dumpf (json_array_get (jkyc,
     241              :                                       i),
     242              :                       stderr,
     243              :                       JSON_INDENT (2));
     244            0 :           return GNUNET_SYSERR;
     245              :         }
     246            5 :         pos_limits++;
     247              :       }
     248            5 :       rd->payto_kycauths = &payto_kycauths[pos_kycauths];
     249            5 :       rd->pkycauth_length = json_array_size (jkycauths);
     250            6 :       json_array_foreach (jkycauths, j, jkycauth)
     251              :       {
     252            1 :         GNUNET_assert (pos_kycauths < num_kycauths);
     253              :         payto_kycauths[pos_kycauths].full_payto
     254            1 :           = (char *) json_string_value (jkycauth);
     255            1 :         if (NULL == payto_kycauths[pos_kycauths].full_payto)
     256              :         {
     257            0 :           GNUNET_break (0);
     258            0 :           json_dumpf (json_array_get (jkyc,
     259              :                                       i),
     260              :                       stderr,
     261              :                       JSON_INDENT (2));
     262            0 :           return GNUNET_SYSERR;
     263              :         }
     264            1 :         pos_kycauths++;
     265              :       }
     266              :     }
     267            5 :     kr->details.ok.kycs = kycs;
     268            5 :     kr->details.ok.kycs_length = num_kycs;
     269            5 :     kyc->cb (kyc->cb_cls,
     270              :              kr);
     271              :   }
     272            5 :   return GNUNET_OK;
     273              : }
     274              : 
     275              : 
     276              : /**
     277              :  * Function called when we're done processing the
     278              :  * HTTP /kyc request.
     279              :  *
     280              :  * @param cls the `struct TALER_MERCHANT_KycGetHandle`
     281              :  * @param response_code HTTP response code, 0 on error
     282              :  * @param response response body, NULL if not in JSON
     283              :  */
     284              : static void
     285            7 : handle_get_kyc_finished (void *cls,
     286              :                          long response_code,
     287              :                          const void *response)
     288              : {
     289            7 :   struct TALER_MERCHANT_KycGetHandle *kyc = cls;
     290            7 :   const json_t *json = response;
     291            7 :   struct TALER_MERCHANT_KycResponse kr = {
     292            7 :     .hr.http_status = (unsigned int) response_code,
     293              :     .hr.reply = json
     294              :   };
     295              : 
     296            7 :   kyc->job = NULL;
     297            7 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     298              :               "Got /kyc response with status code %u\n",
     299              :               (unsigned int) response_code);
     300            7 :   switch (response_code)
     301              :   {
     302            5 :   case MHD_HTTP_OK:
     303              :     {
     304              :       const json_t *jkyc;
     305              :       struct GNUNET_JSON_Specification spec[] = {
     306            5 :         GNUNET_JSON_spec_array_const ("kyc_data",
     307              :                                       &jkyc),
     308            5 :         GNUNET_JSON_spec_end ()
     309              :       };
     310              : 
     311            5 :       if (GNUNET_OK !=
     312            5 :           GNUNET_JSON_parse (json,
     313              :                              spec,
     314              :                              NULL, NULL))
     315              :       {
     316            0 :         kr.hr.http_status = 0;
     317            0 :         kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     318            0 :         break;
     319              :       }
     320            5 :       if (GNUNET_OK !=
     321            5 :           parse_kyc (kyc,
     322              :                      &kr,
     323              :                      jkyc))
     324              :       {
     325            0 :         kr.hr.http_status = 0;
     326            0 :         kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     327            0 :         break;
     328              :       }
     329              :       /* parse_kyc called the continuation already */
     330            5 :       TALER_MERCHANT_kyc_get_cancel (kyc);
     331            5 :       return;
     332              :     }
     333            2 :   case MHD_HTTP_NO_CONTENT:
     334            2 :     break;
     335            0 :   case MHD_HTTP_UNAUTHORIZED:
     336            0 :     kr.hr.ec = TALER_JSON_get_error_code (json);
     337            0 :     kr.hr.hint = TALER_JSON_get_error_hint (json);
     338              :     /* Nothing really to verify, merchant says we need to authenticate. */
     339            0 :     break;
     340            0 :   case MHD_HTTP_SERVICE_UNAVAILABLE:
     341            0 :     break;
     342            0 :   default:
     343              :     /* unexpected response code */
     344            0 :     kr.hr.ec = TALER_JSON_get_error_code (json);
     345            0 :     kr.hr.hint = TALER_JSON_get_error_hint (json);
     346            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     347              :                 "Unexpected response code %u/%d\n",
     348              :                 (unsigned int) response_code,
     349              :                 (int) kr.hr.ec);
     350            0 :     break;
     351              :   }
     352            2 :   kyc->cb (kyc->cb_cls,
     353              :            &kr);
     354            2 :   TALER_MERCHANT_kyc_get_cancel (kyc);
     355              : }
     356              : 
     357              : 
     358              : /**
     359              :  * Issue a GET KYC request to the backend.
     360              :  * Returns KYC status of bank accounts.
     361              :  *
     362              :  * @param ctx execution context
     363              :  * @param[in] url URL to use for the request, consumed!
     364              :  * @param h_wire which bank account to query, NULL for all
     365              :  * @param exchange_url which exchange to query, NULL for all
     366              :  * @param lpt target for long polling
     367              :  * @param timeout how long to wait for a reply
     368              :  * @param cb function to call with the result
     369              :  * @param cb_cls closure for @a cb
     370              :  * @return handle for this operation, NULL upon errors
     371              :  */
     372              : static struct TALER_MERCHANT_KycGetHandle *
     373            7 : kyc_get (struct GNUNET_CURL_Context *ctx,
     374              :          char *url,
     375              :          const struct TALER_MerchantWireHashP *h_wire,
     376              :          const char *exchange_url,
     377              :          enum TALER_EXCHANGE_KycLongPollTarget lpt,
     378              :          struct GNUNET_TIME_Relative timeout,
     379              :          TALER_MERCHANT_KycGetCallback cb,
     380              :          void *cb_cls)
     381              : {
     382              :   struct TALER_MERCHANT_KycGetHandle *kyc;
     383              :   CURL *eh;
     384              :   char timeout_ms[32];
     385              :   char lpt_str[32];
     386              :   unsigned long long tms;
     387              : 
     388            7 :   kyc = GNUNET_new (struct TALER_MERCHANT_KycGetHandle);
     389            7 :   kyc->ctx = ctx;
     390            7 :   kyc->cb = cb;
     391            7 :   kyc->cb_cls = cb_cls;
     392            7 :   GNUNET_snprintf (lpt_str,
     393              :                    sizeof (lpt_str),
     394              :                    "%d",
     395              :                    (int) lpt);
     396           14 :   tms = timeout.rel_value_us
     397            7 :         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     398            7 :   GNUNET_snprintf (timeout_ms,
     399              :                    sizeof (timeout_ms),
     400              :                    "%llu",
     401              :                    tms);
     402              :   kyc->url
     403           16 :     = TALER_url_join (
     404              :         url,
     405              :         "kyc",
     406              :         "h_wire",
     407              :         NULL == h_wire
     408              :         ? NULL
     409            2 :         : GNUNET_h2s_full (&h_wire->hash),
     410              :         "exchange_url",
     411              :         NULL == exchange_url
     412              :         ? NULL
     413              :         : exchange_url,
     414              :         "timeout_ms",
     415            7 :         GNUNET_TIME_relative_is_zero (timeout)
     416              :         ? NULL
     417              :         : timeout_ms,
     418              :         "lpt",
     419              :         TALER_EXCHANGE_KLPT_NONE == lpt
     420              :         ? NULL
     421              :         : lpt_str,
     422              :         NULL);
     423            7 :   GNUNET_free (url);
     424            7 :   if (NULL == kyc->url)
     425              :   {
     426            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     427              :                 "Could not construct request URL.\n");
     428            0 :     GNUNET_free (kyc);
     429            0 :     return NULL;
     430              :   }
     431            7 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     432              :               "Requesting URL '%s'\n",
     433              :               kyc->url);
     434            7 :   eh = TALER_MERCHANT_curl_easy_get_ (kyc->url);
     435            7 :   if (0 != tms)
     436              :   {
     437            4 :     GNUNET_break (CURLE_OK ==
     438              :                   curl_easy_setopt (eh,
     439              :                                     CURLOPT_TIMEOUT_MS,
     440              :                                     (long) (tms + 100L)));
     441              :   }
     442              :   kyc->job
     443            7 :     = GNUNET_CURL_job_add (ctx,
     444              :                            eh,
     445              :                            &handle_get_kyc_finished,
     446              :                            kyc);
     447            7 :   return kyc;
     448              : }
     449              : 
     450              : 
     451              : struct TALER_MERCHANT_KycGetHandle *
     452            7 : TALER_MERCHANT_kyc_get (
     453              :   struct GNUNET_CURL_Context *ctx,
     454              :   const char *backend_url,
     455              :   const struct TALER_MerchantWireHashP *h_wire,
     456              :   const char *exchange_url,
     457              :   enum TALER_EXCHANGE_KycLongPollTarget lpt,
     458              :   struct GNUNET_TIME_Relative timeout,
     459              :   TALER_MERCHANT_KycGetCallback cb,
     460              :   void *cb_cls)
     461              : {
     462              :   char *url;
     463              : 
     464            7 :   GNUNET_asprintf (&url,
     465              :                    "%sprivate/",
     466              :                    backend_url);
     467            7 :   return kyc_get (ctx,
     468              :                   url, /* consumed! */
     469              :                   h_wire,
     470              :                   exchange_url,
     471              :                   lpt,
     472              :                   timeout,
     473              :                   cb,
     474              :                   cb_cls);
     475              : }
     476              : 
     477              : 
     478              : struct TALER_MERCHANT_KycGetHandle *
     479            0 : TALER_MERCHANT_management_kyc_get (
     480              :   struct GNUNET_CURL_Context *ctx,
     481              :   const char *backend_url,
     482              :   const char *instance_id,
     483              :   const struct TALER_MerchantWireHashP *h_wire,
     484              :   const char *exchange_url,
     485              :   enum TALER_EXCHANGE_KycLongPollTarget lpt,
     486              :   struct GNUNET_TIME_Relative timeout,
     487              :   TALER_MERCHANT_KycGetCallback cb,
     488              :   void *cb_cls)
     489              : {
     490              :   char *url;
     491              : 
     492            0 :   GNUNET_asprintf (&url,
     493              :                    "%smanagement/instances/%s/",
     494              :                    backend_url,
     495              :                    instance_id);
     496            0 :   return kyc_get (ctx,
     497              :                   url, /* consumed! */
     498              :                   h_wire,
     499              :                   exchange_url,
     500              :                   lpt,
     501              :                   timeout,
     502              :                   cb,
     503              :                   cb_cls);
     504              : }
     505              : 
     506              : 
     507              : void
     508            7 : TALER_MERCHANT_kyc_get_cancel (
     509              :   struct TALER_MERCHANT_KycGetHandle *kyc)
     510              : {
     511            7 :   if (NULL != kyc->job)
     512            0 :     GNUNET_CURL_job_cancel (kyc->job);
     513            7 :   GNUNET_free (kyc->url);
     514            7 :   GNUNET_free (kyc);
     515            7 : }
        

Generated by: LCOV version 2.0-1