merchant

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

merchant_api_get-private-fountains-FOUNTAIN_ID.c (8531B)


      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-FOUNTAIN_ID.c
     21  * @brief Implementation of the GET /private/fountains/$FOUNTAIN_ID 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-FOUNTAIN_ID.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/$FOUNTAIN_ID operation.
     39  */
     40 struct TALER_MERCHANT_GetPrivateFountainHandle
     41 {
     42   /**
     43    * Base URL of the merchant backend.
     44    */
     45   char *base_url;
     46 
     47   /**
     48    * The full URL for this request.
     49    */
     50   char *url;
     51 
     52   /**
     53    * Fountain identifier.
     54    */
     55   char *fountain_id;
     56 
     57   /**
     58    * Handle for the request.
     59    */
     60   struct GNUNET_CURL_Job *job;
     61 
     62   /**
     63    * Function to call with the result.
     64    */
     65   TALER_MERCHANT_GetPrivateFountainCallback cb;
     66 
     67   /**
     68    * Closure for @a cb.
     69    */
     70   TALER_MERCHANT_GET_PRIVATE_FOUNTAIN_RESULT_CLOSURE *cb_cls;
     71 
     72   /**
     73    * Reference to the execution context.
     74    */
     75   struct GNUNET_CURL_Context *ctx;
     76 };
     77 
     78 
     79 /**
     80  * Parse the fountain details and invoke the callback.
     81  *
     82  * @param gpfh operation handle
     83  * @param[in,out] gfr response to complete and pass to the callback
     84  */
     85 static void
     86 handle_ok (struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh,
     87            struct TALER_MERCHANT_GetPrivateFountainResponse *gfr)
     88 {
     89   const json_t *jgrants;
     90   struct GNUNET_JSON_Specification spec[] = {
     91     GNUNET_JSON_spec_string ("description",
     92                              &gfr->details.ok.description),
     93     GNUNET_JSON_spec_relative_time ("poll_freq",
     94                                     &gfr->details.ok.poll_freq),
     95     GNUNET_JSON_spec_array_const ("grants",
     96                                   &jgrants),
     97     GNUNET_JSON_spec_end ()
     98   };
     99 
    100   if (GNUNET_OK !=
    101       GNUNET_JSON_parse (gfr->hr.reply,
    102                          spec,
    103                          NULL,
    104                          NULL))
    105   {
    106     GNUNET_break_op (0);
    107     gfr->hr.http_status = 0;
    108     gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    109     gpfh->cb (gpfh->cb_cls,
    110               gfr);
    111     return;
    112   }
    113   if (json_array_size (jgrants) > TALER_MERCHANT_MAX_FOUNTAIN_RESPONSE_ITEMS)
    114   {
    115     gfr->hr.http_status = 0;
    116     gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    117     gpfh->cb (gpfh->cb_cls,
    118               gfr);
    119     return;
    120   }
    121   {
    122     unsigned int len = (unsigned int) json_array_size (jgrants);
    123     struct TALER_MERCHANT_FountainGrant *grants
    124       = GNUNET_new_array (GNUNET_NZL (len),
    125                           struct TALER_MERCHANT_FountainGrant);
    126     size_t idx;
    127     json_t *jg;
    128 
    129     json_array_foreach ((json_t *) jgrants, idx, jg)
    130     {
    131       struct TALER_MERCHANT_FountainGrant *fg = &grants[idx];
    132       uint32_t window;
    133       struct GNUNET_JSON_Specification ispec[] = {
    134         GNUNET_JSON_spec_string ("token_family_slug",
    135                                  &fg->token_family_slug),
    136         GNUNET_JSON_spec_uint64 ("tokens_per_period_limit",
    137                                  &fg->tokens_per_period_limit),
    138         GNUNET_JSON_spec_uint64 ("tokens_per_period_stash",
    139                                  &fg->tokens_per_period_stash),
    140         GNUNET_JSON_spec_uint32 ("key_window_size",
    141                                  &window),
    142         GNUNET_JSON_spec_end ()
    143       };
    144 
    145       if (GNUNET_OK !=
    146           GNUNET_JSON_parse (jg,
    147                              ispec,
    148                              NULL,
    149                              NULL))
    150       {
    151         GNUNET_break_op (0);
    152         gfr->hr.http_status = 0;
    153         gfr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    154         GNUNET_free (grants);
    155         gpfh->cb (gpfh->cb_cls,
    156                   gfr);
    157         return;
    158       }
    159       fg->key_window_size = window;
    160     }
    161     gfr->details.ok.grants = grants;
    162     gfr->details.ok.grants_len = len;
    163     gpfh->cb (gpfh->cb_cls,
    164               gfr);
    165     GNUNET_free (grants);
    166   }
    167 }
    168 
    169 
    170 /**
    171  * Function called when we're done processing the
    172  * HTTP GET /private/fountains/$FOUNTAIN_ID request.
    173  *
    174  * @param cls the `struct TALER_MERCHANT_GetPrivateFountainHandle`
    175  * @param response_code HTTP response code, 0 on error
    176  * @param response response body, NULL if not in JSON
    177  */
    178 static void
    179 handle_get_fountain_finished (void *cls,
    180                               long response_code,
    181                               const void *response)
    182 {
    183   struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh = cls;
    184   const json_t *json = response;
    185   struct TALER_MERCHANT_GetPrivateFountainResponse gfr = {
    186     .hr.http_status = (unsigned int) response_code,
    187     .hr.reply = json
    188   };
    189 
    190   gpfh->job = NULL;
    191   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    192               "GET /private/fountains/$ID completed with response code %u\n",
    193               (unsigned int) response_code);
    194   switch (response_code)
    195   {
    196   case 0:
    197     gfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    198     break;
    199   case MHD_HTTP_OK:
    200     handle_ok (gpfh,
    201                &gfr);
    202     TALER_MERCHANT_get_private_fountain_cancel (gpfh);
    203     return;
    204   case MHD_HTTP_UNAUTHORIZED:
    205   case MHD_HTTP_FORBIDDEN:
    206   case MHD_HTTP_NOT_FOUND:
    207   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    208     gfr.hr.ec = TALER_JSON_get_error_code (json);
    209     gfr.hr.hint = TALER_JSON_get_error_hint (json);
    210     break;
    211   default:
    212     TALER_MERCHANT_parse_error_details_ (json,
    213                                          response_code,
    214                                          &gfr.hr);
    215     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    216                 "Unexpected response code %u/%d\n",
    217                 (unsigned int) response_code,
    218                 (int) gfr.hr.ec);
    219     GNUNET_break_op (0);
    220     break;
    221   }
    222   gpfh->cb (gpfh->cb_cls,
    223             &gfr);
    224   TALER_MERCHANT_get_private_fountain_cancel (gpfh);
    225 }
    226 
    227 
    228 struct TALER_MERCHANT_GetPrivateFountainHandle *
    229 TALER_MERCHANT_get_private_fountain_create (
    230   struct GNUNET_CURL_Context *ctx,
    231   const char *url,
    232   const char *fountain_id)
    233 {
    234   struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh;
    235 
    236   gpfh = GNUNET_new (struct TALER_MERCHANT_GetPrivateFountainHandle);
    237   gpfh->ctx = ctx;
    238   gpfh->base_url = GNUNET_strdup (url);
    239   gpfh->fountain_id = GNUNET_strdup (fountain_id);
    240   return gpfh;
    241 }
    242 
    243 
    244 enum TALER_ErrorCode
    245 TALER_MERCHANT_get_private_fountain_start (
    246   struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh,
    247   TALER_MERCHANT_GetPrivateFountainCallback cb,
    248   TALER_MERCHANT_GET_PRIVATE_FOUNTAIN_RESULT_CLOSURE *cb_cls)
    249 {
    250   CURL *eh;
    251 
    252   gpfh->cb = cb;
    253   gpfh->cb_cls = cb_cls;
    254   {
    255     char *path;
    256 
    257     GNUNET_asprintf (&path,
    258                      "private/fountains/%s",
    259                      gpfh->fountain_id);
    260     gpfh->url = TALER_url_join (gpfh->base_url,
    261                                 path,
    262                                 NULL);
    263     GNUNET_free (path);
    264   }
    265   if (NULL == gpfh->url)
    266     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    267   eh = TALER_MERCHANT_curl_easy_get_ (gpfh->url);
    268   if (NULL == eh)
    269   {
    270     GNUNET_break (0);
    271     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    272   }
    273   gpfh->job = GNUNET_CURL_job_add (gpfh->ctx,
    274                                    eh,
    275                                    &handle_get_fountain_finished,
    276                                    gpfh);
    277   if (NULL == gpfh->job)
    278     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    279   return TALER_EC_NONE;
    280 }
    281 
    282 
    283 void
    284 TALER_MERCHANT_get_private_fountain_cancel (
    285   struct TALER_MERCHANT_GetPrivateFountainHandle *gpfh)
    286 {
    287   if (NULL != gpfh->job)
    288   {
    289     GNUNET_CURL_job_cancel (gpfh->job);
    290     gpfh->job = NULL;
    291   }
    292   GNUNET_free (gpfh->fountain_id);
    293   GNUNET_free (gpfh->url);
    294   GNUNET_free (gpfh->base_url);
    295   GNUNET_free (gpfh);
    296 }
    297 
    298 
    299 /* end of merchant_api_get-private-fountains-FOUNTAIN_ID.c */