merchant

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

merchant_api_patch-private-fountains-FOUNTAIN_ID.c (9100B)


      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_patch-private-fountains-FOUNTAIN_ID.c
     21  * @brief Implementation of the PATCH /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/patch-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 PATCH /private/fountains/$FOUNTAIN_ID operation.
     39  */
     40 struct TALER_MERCHANT_PatchPrivateFountainHandle
     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_PatchPrivateFountainCallback cb;
     66 
     67   /**
     68    * Closure for @a cb.
     69    */
     70   TALER_MERCHANT_PATCH_PRIVATE_FOUNTAIN_RESULT_CLOSURE *cb_cls;
     71 
     72   /**
     73    * Reference to the execution context.
     74    */
     75   struct GNUNET_CURL_Context *ctx;
     76 
     77   /**
     78    * Minor context that holds body and headers.
     79    */
     80   struct TALER_CURL_PostContext post_ctx;
     81 
     82   /**
     83    * Request body being assembled from the options.
     84    */
     85   json_t *req_obj;
     86 };
     87 
     88 
     89 /**
     90  * Function called when we're done processing the
     91  * HTTP PATCH /private/fountains/$FOUNTAIN_ID request.
     92  *
     93  * @param cls the `struct TALER_MERCHANT_PatchPrivateFountainHandle`
     94  * @param response_code HTTP response code, 0 on error
     95  * @param response response body, NULL if not in JSON
     96  */
     97 static void
     98 handle_patch_fountain_finished (void *cls,
     99                                 long response_code,
    100                                 const void *response)
    101 {
    102   struct TALER_MERCHANT_PatchPrivateFountainHandle *ppfh = cls;
    103   const json_t *json = response;
    104   struct TALER_MERCHANT_PatchPrivateFountainResponse pfr = {
    105     .hr.http_status = (unsigned int) response_code,
    106     .hr.reply = json
    107   };
    108 
    109   ppfh->job = NULL;
    110   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    111               "PATCH /private/fountains/$ID completed with response code %u\n",
    112               (unsigned int) response_code);
    113   switch (response_code)
    114   {
    115   case 0:
    116     pfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    117     break;
    118   case MHD_HTTP_NO_CONTENT:
    119     break;
    120   case MHD_HTTP_BAD_REQUEST:
    121   case MHD_HTTP_UNAUTHORIZED:
    122   case MHD_HTTP_FORBIDDEN:
    123   case MHD_HTTP_NOT_FOUND:
    124   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    125     pfr.hr.ec = TALER_JSON_get_error_code (json);
    126     pfr.hr.hint = TALER_JSON_get_error_hint (json);
    127     break;
    128   default:
    129     TALER_MERCHANT_parse_error_details_ (json,
    130                                          response_code,
    131                                          &pfr.hr);
    132     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    133                 "Unexpected response code %u/%d\n",
    134                 (unsigned int) response_code,
    135                 (int) pfr.hr.ec);
    136     GNUNET_break_op (0);
    137     break;
    138   }
    139   ppfh->cb (ppfh->cb_cls,
    140             &pfr);
    141   TALER_MERCHANT_patch_private_fountain_cancel (ppfh);
    142 }
    143 
    144 
    145 struct TALER_MERCHANT_PatchPrivateFountainHandle *
    146 TALER_MERCHANT_patch_private_fountain_create (
    147   struct GNUNET_CURL_Context *ctx,
    148   const char *url,
    149   const char *fountain_id)
    150 {
    151   struct TALER_MERCHANT_PatchPrivateFountainHandle *ppfh;
    152 
    153   ppfh = GNUNET_new (struct TALER_MERCHANT_PatchPrivateFountainHandle);
    154   ppfh->ctx = ctx;
    155   ppfh->base_url = GNUNET_strdup (url);
    156   ppfh->fountain_id = GNUNET_strdup (fountain_id);
    157   ppfh->req_obj = json_object ();
    158   GNUNET_assert (NULL != ppfh->req_obj);
    159   return ppfh;
    160 }
    161 
    162 
    163 enum GNUNET_GenericReturnValue
    164 TALER_MERCHANT_patch_private_fountain_set_options_ (
    165   struct TALER_MERCHANT_PatchPrivateFountainHandle *ppfh,
    166   unsigned int num_options,
    167   const struct TALER_MERCHANT_PatchPrivateFountainOptionValue *options)
    168 {
    169   for (unsigned int i = 0; i < num_options; i++)
    170   {
    171     switch (options[i].option)
    172     {
    173     case TALER_MERCHANT_PATCH_PRIVATE_FOUNTAIN_OPTION_END:
    174       return GNUNET_OK;
    175     case TALER_MERCHANT_PATCH_PRIVATE_FOUNTAIN_OPTION_DESCRIPTION:
    176       GNUNET_assert (0 ==
    177                      json_object_set_new (
    178                        ppfh->req_obj,
    179                        "description",
    180                        json_string (options[i].details.description)));
    181       break;
    182     case TALER_MERCHANT_PATCH_PRIVATE_FOUNTAIN_OPTION_POLL_FREQ:
    183       GNUNET_assert (0 ==
    184                      json_object_set_new (
    185                        ppfh->req_obj,
    186                        "poll_freq",
    187                        GNUNET_JSON_from_time_rel (
    188                          options[i].details.poll_freq)));
    189       break;
    190     case TALER_MERCHANT_PATCH_PRIVATE_FOUNTAIN_OPTION_GRANTS:
    191       {
    192         json_t *jgrants;
    193 
    194         jgrants = json_array ();
    195         GNUNET_assert (NULL != jgrants);
    196         for (unsigned int j = 0;
    197              j < options[i].details.grants.num_grants;
    198              j++)
    199         {
    200           const struct TALER_MERCHANT_FountainGrant *fg
    201             = &options[i].details.grants.grants[j];
    202 
    203           GNUNET_assert (0 ==
    204                          json_array_append_new (
    205                            jgrants,
    206                            GNUNET_JSON_PACK (
    207                              GNUNET_JSON_pack_string (
    208                                "token_family_slug",
    209                                fg->token_family_slug),
    210                              GNUNET_JSON_pack_uint64 (
    211                                "tokens_per_period_limit",
    212                                fg->tokens_per_period_limit),
    213                              GNUNET_JSON_pack_uint64 (
    214                                "tokens_per_period_stash",
    215                                fg->tokens_per_period_stash),
    216                              GNUNET_JSON_pack_uint64 (
    217                                "key_window_size",
    218                                fg->key_window_size))));
    219         }
    220         GNUNET_assert (0 ==
    221                        json_object_set_new (ppfh->req_obj,
    222                                             "grants",
    223                                             jgrants));
    224         break;
    225       }
    226     default:
    227       GNUNET_break (0);
    228       return GNUNET_SYSERR;
    229     }
    230   }
    231   return GNUNET_OK;
    232 }
    233 
    234 
    235 enum TALER_ErrorCode
    236 TALER_MERCHANT_patch_private_fountain_start (
    237   struct TALER_MERCHANT_PatchPrivateFountainHandle *ppfh,
    238   TALER_MERCHANT_PatchPrivateFountainCallback cb,
    239   TALER_MERCHANT_PATCH_PRIVATE_FOUNTAIN_RESULT_CLOSURE *cb_cls)
    240 {
    241   CURL *eh;
    242 
    243   ppfh->cb = cb;
    244   ppfh->cb_cls = cb_cls;
    245   {
    246     char *path;
    247 
    248     GNUNET_asprintf (&path,
    249                      "private/fountains/%s",
    250                      ppfh->fountain_id);
    251     ppfh->url = TALER_url_join (ppfh->base_url,
    252                                 path,
    253                                 NULL);
    254     GNUNET_free (path);
    255   }
    256   if (NULL == ppfh->url)
    257     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    258   eh = TALER_MERCHANT_curl_easy_get_ (ppfh->url);
    259   if ( (NULL == eh) ||
    260        (GNUNET_OK !=
    261         TALER_curl_easy_post (&ppfh->post_ctx,
    262                               eh,
    263                               ppfh->req_obj)) )
    264   {
    265     GNUNET_break (0);
    266     if (NULL != eh)
    267       curl_easy_cleanup (eh);
    268     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    269   }
    270   GNUNET_assert (CURLE_OK ==
    271                  curl_easy_setopt (eh,
    272                                    CURLOPT_CUSTOMREQUEST,
    273                                    MHD_HTTP_METHOD_PATCH));
    274   ppfh->job = GNUNET_CURL_job_add2 (ppfh->ctx,
    275                                     eh,
    276                                     ppfh->post_ctx.headers,
    277                                     &handle_patch_fountain_finished,
    278                                     ppfh);
    279   if (NULL == ppfh->job)
    280     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    281   return TALER_EC_NONE;
    282 }
    283 
    284 
    285 void
    286 TALER_MERCHANT_patch_private_fountain_cancel (
    287   struct TALER_MERCHANT_PatchPrivateFountainHandle *ppfh)
    288 {
    289   if (NULL != ppfh->job)
    290   {
    291     GNUNET_CURL_job_cancel (ppfh->job);
    292     ppfh->job = NULL;
    293   }
    294   TALER_curl_easy_post_finished (&ppfh->post_ctx);
    295   json_decref (ppfh->req_obj);
    296   GNUNET_free (ppfh->fountain_id);
    297   GNUNET_free (ppfh->url);
    298   GNUNET_free (ppfh->base_url);
    299   GNUNET_free (ppfh);
    300 }
    301 
    302 
    303 /* end of merchant_api_patch-private-fountains-FOUNTAIN_ID.c */