merchant

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

merchant_api_get-private-fountains.c (8555B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Lesser General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General
     16   Public License along with TALER; see the file COPYING.LGPL.
     17   If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file src/lib/merchant_api_get-private-fountains.c
     21  * @brief Implementation of the GET /private/fountains request
     22  * @author Bohdan Potuzhnyi
     23  */
     24 #include "platform.h"
     25 #include <curl/curl.h>
     26 #include <jansson.h>
     27 #include <microhttpd.h> /* just for HTTP status codes */
     28 #include <gnunet/gnunet_util_lib.h>
     29 #include <gnunet/gnunet_curl_lib.h>
     30 #include <taler/merchant/get-private-fountains.h>
     31 #include "merchant_api_curl_defaults.h"
     32 #include "merchant_api_common.h"
     33 #include <taler/taler_json_lib.h>
     34 #include <taler/taler_curl_lib.h>
     35 
     36 
     37 /**
     38  * Handle for a GET /private/fountains operation.
     39  */
     40 struct TALER_MERCHANT_GetPrivateFountainsHandle
     41 {
     42   /**
     43    * Base URL of the merchant backend.
     44    */
     45   char *base_url;
     46 
     47   int64_t limit;
     48 
     49   uint64_t offset;
     50 
     51   /**
     52    * The full URL for this request.
     53    */
     54   char *url;
     55 
     56   /**
     57    * Handle for the request.
     58    */
     59   struct GNUNET_CURL_Job *job;
     60 
     61   /**
     62    * Function to call with the result.
     63    */
     64   TALER_MERCHANT_GetPrivateFountainsCallback cb;
     65 
     66   /**
     67    * Closure for @a cb.
     68    */
     69   TALER_MERCHANT_GET_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls;
     70 
     71   /**
     72    * Reference to the execution context.
     73    */
     74   struct GNUNET_CURL_Context *ctx;
     75 };
     76 
     77 
     78 /**
     79  * Parse the "fountains" array and invoke the callback.
     80  *
     81  * @param gpfh operation handle
     82  * @param[in,out] gfr response to complete and pass to the callback
     83  */
     84 static void
     85 handle_ok (struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh,
     86            struct TALER_MERCHANT_GetPrivateFountainsResponse *gfr)
     87 {
     88   const json_t *jfountains;
     89   struct GNUNET_JSON_Specification spec[] = {
     90     GNUNET_JSON_spec_array_const ("fountains",
     91                                   &jfountains),
     92     GNUNET_JSON_spec_end ()
     93   };
     94 
     95   if (GNUNET_OK !=
     96       GNUNET_JSON_parse (gfr->hr.reply,
     97                          spec,
     98                          NULL,
     99                          NULL))
    100   {
    101     GNUNET_break_op (0);
    102     gfr->hr.http_status = 0;
    103     gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    104     gpfh->cb (gpfh->cb_cls,
    105               gfr);
    106     return;
    107   }
    108   /* Reject responses exceeding the server page cap before allocating. */
    109   if (json_array_size (jfountains) > 100)
    110   {
    111     gfr->hr.http_status = 0;
    112     gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    113     gpfh->cb (gpfh->cb_cls,
    114               gfr);
    115     return;
    116   }
    117   {
    118     unsigned int len = (unsigned int) json_array_size (jfountains);
    119     struct TALER_MERCHANT_FountainEntry *fountains
    120       = GNUNET_new_array (GNUNET_NZL (len),
    121                           struct TALER_MERCHANT_FountainEntry);
    122     size_t idx;
    123     json_t *jf;
    124 
    125     json_array_foreach ((json_t *) jfountains, idx, jf)
    126     {
    127       struct TALER_MERCHANT_FountainEntry *fe = &fountains[idx];
    128       struct GNUNET_JSON_Specification ispec[] = {
    129         GNUNET_JSON_spec_uint64 ("fountain_serial",
    130                                  &fe->fountain_serial),
    131         GNUNET_JSON_spec_string ("fountain_id",
    132                                  &fe->fountain_id),
    133         GNUNET_JSON_spec_string ("description",
    134                                  &fe->description),
    135         GNUNET_JSON_spec_end ()
    136       };
    137 
    138       if (GNUNET_OK !=
    139           GNUNET_JSON_parse (jf,
    140                              ispec,
    141                              NULL,
    142                              NULL))
    143       {
    144         GNUNET_break_op (0);
    145         gfr->hr.http_status = 0;
    146         gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    147         GNUNET_free (fountains);
    148         gpfh->cb (gpfh->cb_cls,
    149                   gfr);
    150         return;
    151       }
    152     }
    153     gfr->details.ok.fountains = fountains;
    154     gfr->details.ok.fountains_len = len;
    155     gpfh->cb (gpfh->cb_cls,
    156               gfr);
    157     GNUNET_free (fountains);
    158   }
    159 }
    160 
    161 
    162 /**
    163  * Function called when we're done processing the
    164  * HTTP GET /private/fountains request.
    165  *
    166  * @param cls the `struct TALER_MERCHANT_GetPrivateFountainsHandle`
    167  * @param response_code HTTP response code, 0 on error
    168  * @param response response body, NULL if not in JSON
    169  */
    170 static void
    171 handle_get_fountains_finished (void *cls,
    172                                long response_code,
    173                                const void *response)
    174 {
    175   struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh = cls;
    176   const json_t *json = response;
    177   struct TALER_MERCHANT_GetPrivateFountainsResponse gfr = {
    178     .hr.http_status = (unsigned int) response_code,
    179     .hr.reply = json
    180   };
    181 
    182   gpfh->job = NULL;
    183   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    184               "GET /private/fountains completed with response code %u\n",
    185               (unsigned int) response_code);
    186   switch (response_code)
    187   {
    188   case 0:
    189     gfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    190     break;
    191   case MHD_HTTP_OK:
    192     handle_ok (gpfh,
    193                &gfr);
    194     TALER_MERCHANT_get_private_fountains_cancel (gpfh);
    195     return;
    196   case MHD_HTTP_UNAUTHORIZED:
    197   case MHD_HTTP_FORBIDDEN:
    198   case MHD_HTTP_NOT_FOUND:
    199   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    200     gfr.hr.ec = TALER_JSON_get_error_code (json);
    201     gfr.hr.hint = TALER_JSON_get_error_hint (json);
    202     break;
    203   default:
    204     TALER_MERCHANT_parse_error_details_ (json,
    205                                          response_code,
    206                                          &gfr.hr);
    207     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    208                 "Unexpected response code %u/%d\n",
    209                 (unsigned int) response_code,
    210                 (int) gfr.hr.ec);
    211     GNUNET_break_op (0);
    212     break;
    213   }
    214   gpfh->cb (gpfh->cb_cls,
    215             &gfr);
    216   TALER_MERCHANT_get_private_fountains_cancel (gpfh);
    217 }
    218 
    219 
    220 struct TALER_MERCHANT_GetPrivateFountainsHandle *
    221 TALER_MERCHANT_get_private_fountains_create (
    222   struct GNUNET_CURL_Context *ctx,
    223   const char *url)
    224 {
    225   struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh;
    226 
    227   gpfh = GNUNET_new (struct TALER_MERCHANT_GetPrivateFountainsHandle);
    228   gpfh->ctx = ctx;
    229   gpfh->limit = 20;
    230   gpfh->base_url = GNUNET_strdup (url);
    231   return gpfh;
    232 }
    233 
    234 
    235 enum TALER_ErrorCode
    236 TALER_MERCHANT_get_private_fountains_set_pagination (
    237   struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh,
    238   int64_t limit,
    239   uint64_t offset)
    240 {
    241   if ( (offset > INT64_MAX) || (NULL != gpfh->job) )
    242     return TALER_EC_GENERIC_PARAMETER_MALFORMED;
    243   gpfh->limit = limit;
    244   gpfh->offset = offset;
    245   return TALER_EC_NONE;
    246 }
    247 
    248 
    249 enum TALER_ErrorCode
    250 TALER_MERCHANT_get_private_fountains_start (
    251   struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh,
    252   TALER_MERCHANT_GetPrivateFountainsCallback cb,
    253   TALER_MERCHANT_GET_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls)
    254 {
    255   CURL *eh;
    256   char limit_s[32];
    257   char offset_s[32];
    258 
    259   gpfh->cb = cb;
    260   gpfh->cb_cls = cb_cls;
    261   GNUNET_snprintf (limit_s, sizeof (limit_s), "%lld",
    262                    (long long) gpfh->limit);
    263   GNUNET_snprintf (offset_s, sizeof (offset_s), "%llu",
    264                    (unsigned long long) gpfh->offset);
    265   gpfh->url = TALER_url_join (gpfh->base_url,
    266                               "private/fountains",
    267                               "limit", limit_s,
    268                               "offset", offset_s,
    269                               NULL);
    270   if (NULL == gpfh->url)
    271     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    272   eh = TALER_MERCHANT_curl_easy_get_ (gpfh->url);
    273   if (NULL == eh)
    274   {
    275     GNUNET_break (0);
    276     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    277   }
    278   gpfh->job = GNUNET_CURL_job_add (gpfh->ctx,
    279                                    eh,
    280                                    &handle_get_fountains_finished,
    281                                    gpfh);
    282   if (NULL == gpfh->job)
    283     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    284   return TALER_EC_NONE;
    285 }
    286 
    287 
    288 void
    289 TALER_MERCHANT_get_private_fountains_cancel (
    290   struct TALER_MERCHANT_GetPrivateFountainsHandle *gpfh)
    291 {
    292   if (NULL != gpfh->job)
    293   {
    294     GNUNET_CURL_job_cancel (gpfh->job);
    295     gpfh->job = NULL;
    296   }
    297   GNUNET_free (gpfh->url);
    298   GNUNET_free (gpfh->base_url);
    299   GNUNET_free (gpfh);
    300 }
    301 
    302 
    303 /* end of merchant_api_get-private-fountains.c */