merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

merchant_api_get-private-kyc.c (17455B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2023-2026 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 src/lib/merchant_api_get-private-kyc.c
     19  * @brief Implementation of the GET /private/kyc request
     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/get-private-kyc.h>
     29 #include "merchant_api_curl_defaults.h"
     30 #include <taler/taler_json_lib.h>
     31 
     32 
     33 /**
     34  * Maximum length of the KYC arrays supported.
     35  */
     36 #define MAX_KYC 1024
     37 
     38 
     39 /**
     40  * Handle for a GET /private/kyc operation.
     41  */
     42 struct TALER_MERCHANT_GetPrivateKycHandle
     43 {
     44   /**
     45    * Base URL of the merchant backend.
     46    */
     47   char *base_url;
     48 
     49   /**
     50    * The full URL for this request.
     51    */
     52   char *url;
     53 
     54   /**
     55    * Handle for the request.
     56    */
     57   struct GNUNET_CURL_Job *job;
     58 
     59   /**
     60    * Function to call with the result.
     61    */
     62   TALER_MERCHANT_GetPrivateKycCallback cb;
     63 
     64   /**
     65    * Closure for @a cb.
     66    */
     67   TALER_MERCHANT_GET_PRIVATE_KYC_RESULT_CLOSURE *cb_cls;
     68 
     69   /**
     70    * Reference to the execution context.
     71    */
     72   struct GNUNET_CURL_Context *ctx;
     73 
     74   /**
     75    * Hash of the wire account to filter by, or NULL.
     76    */
     77   const struct TALER_MerchantWireHashP *h_wire;
     78 
     79   /**
     80    * Storage for the h_wire value (if set).
     81    */
     82   struct TALER_MerchantWireHashP h_wire_val;
     83 
     84   /**
     85    * True if @e h_wire was set.
     86    */
     87   bool have_h_wire;
     88 
     89   /**
     90    * Exchange URL filter, or NULL.
     91    */
     92   char *exchange_url;
     93 
     94   /**
     95    * Long-poll target.
     96    */
     97   enum TALER_EXCHANGE_KycLongPollTarget lpt;
     98 
     99   /**
    100    * Long polling timeout.
    101    */
    102   struct GNUNET_TIME_Relative timeout;
    103 
    104   /**
    105    * Instance ID for management mode, or NULL.
    106    */
    107   char *instance_id;
    108 
    109   /**
    110    * Long-poll status filter, or NULL.
    111    */
    112   char *lp_status;
    113 
    114   /**
    115    * Long-poll negated status filter, or NULL.
    116    */
    117   char *lp_not_status;
    118 
    119   /**
    120    * Long-poll ETag to suppress unchanged responses.
    121    */
    122   struct GNUNET_ShortHashCode lp_not_etag;
    123 
    124   /**
    125    * True if @e lp_not_etag was set.
    126    */
    127   bool have_lp_not_etag;
    128 };
    129 
    130 
    131 /**
    132  * Parse @a jkyc response and call the continuation on success.
    133  *
    134  * @param kyc operation handle
    135  * @param[in,out] kr response details
    136  * @param jkyc array from the reply
    137  * @return #GNUNET_OK on success (callback was called)
    138  */
    139 static enum GNUNET_GenericReturnValue
    140 parse_kyc (struct TALER_MERCHANT_GetPrivateKycHandle *kyc,
    141            struct TALER_MERCHANT_GetPrivateKycResponse *kr,
    142            const json_t *jkyc)
    143 {
    144   unsigned int num_kycs = (unsigned int) json_array_size (jkyc);
    145   unsigned int num_limits = 0;
    146   unsigned int num_kycauths = 0;
    147   unsigned int pos_limits = 0;
    148   unsigned int pos_kycauths = 0;
    149 
    150   if ( (json_array_size (jkyc) != (size_t) num_kycs) ||
    151        (num_kycs > MAX_KYC) )
    152   {
    153     GNUNET_break_op (0);
    154     return GNUNET_SYSERR;
    155   }
    156 
    157   for (unsigned int i = 0; i<num_kycs; i++)
    158   {
    159     const json_t *jlimits = NULL;
    160     const json_t *jkycauths = NULL;
    161     struct GNUNET_JSON_Specification spec[] = {
    162       GNUNET_JSON_spec_mark_optional (
    163         GNUNET_JSON_spec_array_const (
    164           "limits",
    165           &jlimits),
    166         NULL),
    167       GNUNET_JSON_spec_mark_optional (
    168         GNUNET_JSON_spec_array_const (
    169           "payto_kycauths",
    170           &jkycauths),
    171         NULL),
    172       GNUNET_JSON_spec_end ()
    173     };
    174 
    175     if (GNUNET_OK !=
    176         GNUNET_JSON_parse (json_array_get (jkyc,
    177                                            i),
    178                            spec,
    179                            NULL, NULL))
    180     {
    181       GNUNET_break_op (0);
    182       return GNUNET_SYSERR;
    183     }
    184     num_limits += json_array_size (jlimits);
    185     num_kycauths += json_array_size (jkycauths);
    186     if ( (num_limits > MAX_KYC) ||
    187          (num_kycauths > MAX_KYC) )
    188     {
    189       /* Bound the stack VLAs declared below by an untrusted response. */
    190       GNUNET_break_op (0);
    191       return GNUNET_SYSERR;
    192     }
    193   }
    194 
    195   {
    196     struct TALER_MERCHANT_GetPrivateKycRedirectDetail kycs[
    197       GNUNET_NZL (num_kycs)];
    198     struct TALER_EXCHANGE_AccountLimit limits[
    199       GNUNET_NZL (num_limits)];
    200     struct TALER_FullPayto payto_kycauths[
    201       GNUNET_NZL (num_kycauths)];
    202 
    203     memset (kycs,
    204             0,
    205             sizeof (kycs));
    206     for (unsigned int i = 0; i<num_kycs; i++)
    207     {
    208       struct TALER_MERCHANT_GetPrivateKycRedirectDetail *rd
    209         = &kycs[i];
    210       const json_t *jlimits = NULL;
    211       const json_t *jkycauths = NULL;
    212       uint32_t hs;
    213       struct GNUNET_JSON_Specification spec[] = {
    214         TALER_JSON_spec_full_payto_uri (
    215           "payto_uri",
    216           &rd->payto_uri),
    217         TALER_JSON_spec_web_url (
    218           "exchange_url",
    219           &rd->exchange_url),
    220         GNUNET_JSON_spec_uint32 (
    221           "exchange_http_status",
    222           &hs),
    223         GNUNET_JSON_spec_bool (
    224           "no_keys",
    225           &rd->no_keys),
    226         GNUNET_JSON_spec_bool (
    227           "auth_conflict",
    228           &rd->auth_conflict),
    229         GNUNET_JSON_spec_mark_optional (
    230           TALER_JSON_spec_ec (
    231             "exchange_code",
    232             &rd->exchange_code),
    233           NULL),
    234         GNUNET_JSON_spec_mark_optional (
    235           GNUNET_JSON_spec_fixed_auto (
    236             "access_token",
    237             &rd->access_token),
    238           &rd->no_access_token),
    239         GNUNET_JSON_spec_fixed_auto (
    240           "h_wire",
    241           &rd->h_wire),
    242         GNUNET_JSON_spec_mark_optional (
    243           GNUNET_JSON_spec_string (
    244             "status",
    245             &rd->status),
    246           NULL),
    247         /* Mandatory since **v25** */
    248         GNUNET_JSON_spec_mark_optional (
    249           GNUNET_JSON_spec_string (
    250             "exchange_currency",
    251             &rd->exchange_currency),
    252           NULL),
    253         GNUNET_JSON_spec_mark_optional (
    254           GNUNET_JSON_spec_array_const (
    255             "limits",
    256             &jlimits),
    257           NULL),
    258         GNUNET_JSON_spec_mark_optional (
    259           GNUNET_JSON_spec_array_const (
    260             "payto_kycauths",
    261             &jkycauths),
    262           NULL),
    263         GNUNET_JSON_spec_end ()
    264       };
    265       size_t j;
    266       json_t *jlimit;
    267       json_t *jkycauth;
    268 
    269       if (GNUNET_OK !=
    270           GNUNET_JSON_parse (json_array_get (jkyc,
    271                                              i),
    272                              spec,
    273                              NULL, NULL))
    274       {
    275         GNUNET_break_op (0);
    276         return GNUNET_SYSERR;
    277       }
    278       rd->exchange_http_status = (unsigned int) hs;
    279       rd->limits = &limits[pos_limits];
    280       rd->limits_length = json_array_size (jlimits);
    281       json_array_foreach (jlimits, j, jlimit)
    282       {
    283         struct TALER_EXCHANGE_AccountLimit *limit
    284           = &limits[pos_limits];
    285         struct GNUNET_JSON_Specification jspec[] = {
    286           TALER_JSON_spec_kycte (
    287             "operation_type",
    288             &limit->operation_type),
    289           GNUNET_JSON_spec_relative_time (
    290             "timeframe",
    291             &limit->timeframe),
    292           TALER_JSON_spec_amount_any (
    293             "threshold",
    294             &limit->threshold),
    295           GNUNET_JSON_spec_mark_optional (
    296             GNUNET_JSON_spec_bool (
    297               "soft_limit",
    298               &limit->soft_limit),
    299             NULL),
    300           GNUNET_JSON_spec_end ()
    301         };
    302 
    303         GNUNET_assert (pos_limits < num_limits);
    304         limit->soft_limit = false;
    305         if (GNUNET_OK !=
    306             GNUNET_JSON_parse (jlimit,
    307                                jspec,
    308                                NULL, NULL))
    309         {
    310           GNUNET_break_op (0);
    311           return GNUNET_SYSERR;
    312         }
    313         pos_limits++;
    314       }
    315       rd->payto_kycauths = &payto_kycauths[pos_kycauths];
    316       rd->pkycauth_length = json_array_size (jkycauths);
    317       json_array_foreach (jkycauths, j, jkycauth)
    318       {
    319         GNUNET_assert (pos_kycauths < num_kycauths);
    320         payto_kycauths[pos_kycauths].full_payto
    321           = (char *) json_string_value (jkycauth);
    322         if (NULL == payto_kycauths[pos_kycauths].full_payto)
    323         {
    324           GNUNET_break_op (0);
    325           return GNUNET_SYSERR;
    326         }
    327         pos_kycauths++;
    328       }
    329     }
    330     kr->details.ok.kycs = kycs;
    331     kr->details.ok.kycs_length = num_kycs;
    332     kyc->cb (kyc->cb_cls,
    333              kr);
    334     kyc->cb = NULL;
    335   }
    336   return GNUNET_OK;
    337 }
    338 
    339 
    340 /**
    341  * Function called when we're done processing the
    342  * HTTP GET /private/kyc request.
    343  *
    344  * @param cls the `struct TALER_MERCHANT_GetPrivateKycHandle`
    345  * @param response_code HTTP response code, 0 on error
    346  * @param response response body, NULL if not in JSON
    347  */
    348 static void
    349 handle_get_kyc_finished (void *cls,
    350                          long response_code,
    351                          const void *response)
    352 {
    353   struct TALER_MERCHANT_GetPrivateKycHandle *kyc = cls;
    354   const json_t *json = response;
    355   struct TALER_MERCHANT_GetPrivateKycResponse kr = {
    356     .hr.http_status = (unsigned int) response_code,
    357     .hr.reply = json
    358   };
    359 
    360   kyc->job = NULL;
    361   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    362               "Got /private/kyc response with status code %u\n",
    363               (unsigned int) response_code);
    364   switch (response_code)
    365   {
    366   case MHD_HTTP_OK:
    367     {
    368       const json_t *jkyc;
    369       struct GNUNET_JSON_Specification spec[] = {
    370         GNUNET_JSON_spec_array_const ("kyc_data",
    371                                       &jkyc),
    372         GNUNET_JSON_spec_end ()
    373       };
    374 
    375       if (GNUNET_OK !=
    376           GNUNET_JSON_parse (json,
    377                              spec,
    378                              NULL, NULL))
    379       {
    380         kr.hr.http_status = 0;
    381         kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    382         break;
    383       }
    384       if (GNUNET_OK !=
    385           parse_kyc (kyc,
    386                      &kr,
    387                      jkyc))
    388       {
    389         kr.hr.http_status = 0;
    390         kr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    391         break;
    392       }
    393       /* parse_kyc called the continuation already */
    394       TALER_MERCHANT_get_private_kyc_cancel (kyc);
    395       return;
    396     }
    397   case MHD_HTTP_NO_CONTENT:
    398     break;
    399   case MHD_HTTP_NOT_MODIFIED:
    400     /* ETag matched; nothing changed. No body expected. */
    401     break;
    402   case MHD_HTTP_BAD_REQUEST:
    403     kr.hr.ec = TALER_JSON_get_error_code (json);
    404     kr.hr.hint = TALER_JSON_get_error_hint (json);
    405     break;
    406   case MHD_HTTP_UNAUTHORIZED:
    407     kr.hr.ec = TALER_JSON_get_error_code (json);
    408     kr.hr.hint = TALER_JSON_get_error_hint (json);
    409     break;
    410   case MHD_HTTP_NOT_FOUND:
    411     kr.hr.ec = TALER_JSON_get_error_code (json);
    412     kr.hr.hint = TALER_JSON_get_error_hint (json);
    413     break;
    414   case MHD_HTTP_NOT_ACCEPTABLE:
    415     kr.hr.ec = TALER_JSON_get_error_code (json);
    416     kr.hr.hint = TALER_JSON_get_error_hint (json);
    417     break;
    418   case MHD_HTTP_SERVICE_UNAVAILABLE:
    419     break;
    420   default:
    421     kr.hr.ec = TALER_JSON_get_error_code (json);
    422     kr.hr.hint = TALER_JSON_get_error_hint (json);
    423     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    424                 "Unexpected response code %u/%d\n",
    425                 (unsigned int) response_code,
    426                 (int) kr.hr.ec);
    427     break;
    428   }
    429   kyc->cb (kyc->cb_cls,
    430            &kr);
    431   TALER_MERCHANT_get_private_kyc_cancel (kyc);
    432 }
    433 
    434 
    435 struct TALER_MERCHANT_GetPrivateKycHandle *
    436 TALER_MERCHANT_get_private_kyc_create (
    437   struct GNUNET_CURL_Context *ctx,
    438   const char *url)
    439 {
    440   struct TALER_MERCHANT_GetPrivateKycHandle *kyc;
    441 
    442   kyc = GNUNET_new (struct TALER_MERCHANT_GetPrivateKycHandle);
    443   kyc->ctx = ctx;
    444   kyc->base_url = GNUNET_strdup (url);
    445   kyc->lpt = TALER_EXCHANGE_KLPT_NONE;
    446   return kyc;
    447 }
    448 
    449 
    450 enum GNUNET_GenericReturnValue
    451 TALER_MERCHANT_get_private_kyc_set_options_ (
    452   struct TALER_MERCHANT_GetPrivateKycHandle *kyc,
    453   unsigned int num_options,
    454   const struct TALER_MERCHANT_GetPrivateKycOptionValue *options)
    455 {
    456   for (unsigned int i = 0; i < num_options; i++)
    457   {
    458     const struct TALER_MERCHANT_GetPrivateKycOptionValue *opt =
    459       &options[i];
    460 
    461     switch (opt->option)
    462     {
    463     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_END:
    464       return GNUNET_OK;
    465     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_H_WIRE:
    466       if (NULL != opt->details.h_wire)
    467       {
    468         kyc->h_wire_val = *opt->details.h_wire;
    469         kyc->h_wire = &kyc->h_wire_val;
    470         kyc->have_h_wire = true;
    471       }
    472       break;
    473     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_EXCHANGE_URL:
    474       GNUNET_free (kyc->exchange_url);
    475       if (NULL != opt->details.exchange_url)
    476         kyc->exchange_url = GNUNET_strdup (opt->details.exchange_url);
    477       break;
    478     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_LPT:
    479       kyc->lpt = opt->details.lpt;
    480       break;
    481     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_TIMEOUT:
    482       kyc->timeout = opt->details.timeout;
    483       break;
    484     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_INSTANCE_ID:
    485       GNUNET_free (kyc->instance_id);
    486       if (NULL != opt->details.instance_id)
    487         kyc->instance_id = GNUNET_strdup (opt->details.instance_id);
    488       break;
    489     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_LP_STATUS:
    490       GNUNET_free (kyc->lp_status);
    491       if (NULL != opt->details.lp_status)
    492         kyc->lp_status = GNUNET_strdup (opt->details.lp_status);
    493       break;
    494     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_LP_NOT_STATUS:
    495       GNUNET_free (kyc->lp_not_status);
    496       if (NULL != opt->details.lp_not_status)
    497         kyc->lp_not_status = GNUNET_strdup (opt->details.lp_not_status);
    498       break;
    499     case TALER_MERCHANT_GET_PRIVATE_KYC_OPTION_LP_NOT_ETAG:
    500       if (NULL != opt->details.lp_not_etag)
    501       {
    502         kyc->lp_not_etag = *opt->details.lp_not_etag;
    503         kyc->have_lp_not_etag = true;
    504       }
    505       break;
    506     default:
    507       GNUNET_break (0);
    508       return GNUNET_NO;
    509     }
    510   }
    511   return GNUNET_OK;
    512 }
    513 
    514 
    515 enum TALER_ErrorCode
    516 TALER_MERCHANT_get_private_kyc_start (
    517   struct TALER_MERCHANT_GetPrivateKycHandle *kyc,
    518   TALER_MERCHANT_GetPrivateKycCallback cb,
    519   TALER_MERCHANT_GET_PRIVATE_KYC_RESULT_CLOSURE *cb_cls)
    520 {
    521   CURL *eh;
    522   unsigned long long tms;
    523   char timeout_ms[32];
    524   char lpt_str[32];
    525   char *base_path;
    526 
    527   kyc->cb = cb;
    528   kyc->cb_cls = cb_cls;
    529 
    530   /* Build the base path depending on whether instance_id is set */
    531   if (NULL != kyc->instance_id)
    532   {
    533     GNUNET_asprintf (&base_path,
    534                      "%smanagement/instances/%s/",
    535                      kyc->base_url,
    536                      kyc->instance_id);
    537   }
    538   else
    539   {
    540     GNUNET_asprintf (&base_path,
    541                      "%sprivate/",
    542                      kyc->base_url);
    543   }
    544 
    545   GNUNET_snprintf (lpt_str,
    546                    sizeof (lpt_str),
    547                    "%d",
    548                    (int) kyc->lpt);
    549   tms = kyc->timeout.rel_value_us
    550         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
    551   GNUNET_snprintf (timeout_ms,
    552                    sizeof (timeout_ms),
    553                    "%llu",
    554                    tms);
    555   {
    556     char etag_str[sizeof (struct GNUNET_ShortHashCode) * 2 + 1];
    557 
    558     if (kyc->have_lp_not_etag)
    559     {
    560       char *end;
    561 
    562       end = GNUNET_STRINGS_data_to_string (
    563         &kyc->lp_not_etag,
    564         sizeof (kyc->lp_not_etag),
    565         etag_str,
    566         sizeof (etag_str) - 1);
    567       *end = '\0';
    568     }
    569     kyc->url
    570       = TALER_url_join (
    571           base_path,
    572           "kyc",
    573           "h_wire",
    574           kyc->have_h_wire
    575           ? GNUNET_h2s_full (&kyc->h_wire_val.hash)
    576           : NULL,
    577           "exchange_url",
    578           kyc->exchange_url,
    579           "timeout_ms",
    580           GNUNET_TIME_relative_is_zero (kyc->timeout)
    581           ? NULL
    582           : timeout_ms,
    583           "lpt",
    584           TALER_EXCHANGE_KLPT_NONE == kyc->lpt
    585           ? NULL
    586           : lpt_str,
    587           "lp_status",
    588           kyc->lp_status,
    589           "lp_not_status",
    590           kyc->lp_not_status,
    591           "lp_not_etag",
    592           kyc->have_lp_not_etag
    593           ? etag_str
    594           : NULL,
    595           NULL);
    596   }
    597   GNUNET_free (base_path);
    598   if (NULL == kyc->url)
    599     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    600   eh = TALER_MERCHANT_curl_easy_get_ (kyc->url);
    601   if (NULL == eh)
    602     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    603   if (0 != tms)
    604   {
    605     GNUNET_break (CURLE_OK ==
    606                   curl_easy_setopt (eh,
    607                                     CURLOPT_TIMEOUT_MS,
    608                                     (long) (tms + 100L)));
    609   }
    610   kyc->job = GNUNET_CURL_job_add (kyc->ctx,
    611                                   eh,
    612                                   &handle_get_kyc_finished,
    613                                   kyc);
    614   if (NULL == kyc->job)
    615     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    616   return TALER_EC_NONE;
    617 }
    618 
    619 
    620 void
    621 TALER_MERCHANT_get_private_kyc_cancel (
    622   struct TALER_MERCHANT_GetPrivateKycHandle *kyc)
    623 {
    624   if (NULL != kyc->job)
    625   {
    626     GNUNET_CURL_job_cancel (kyc->job);
    627     kyc->job = NULL;
    628   }
    629   GNUNET_free (kyc->url);
    630   GNUNET_free (kyc->exchange_url);
    631   GNUNET_free (kyc->instance_id);
    632   GNUNET_free (kyc->lp_status);
    633   GNUNET_free (kyc->lp_not_status);
    634   GNUNET_free (kyc->base_url);
    635   GNUNET_free (kyc);
    636 }
    637 
    638 
    639 /* end of merchant_api_get-private-kyc-new.c */