merchant

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

merchant_api_post-private-fountains.c (7849B)


      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_post-private-fountains.c
     21  * @brief Implementation of the POST /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/post-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 POST /private/fountains operation.
     39  */
     40 struct TALER_MERCHANT_PostPrivateFountainsHandle
     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    * Handle for the request.
     54    */
     55   struct GNUNET_CURL_Job *job;
     56 
     57   /**
     58    * Function to call with the result.
     59    */
     60   TALER_MERCHANT_PostPrivateFountainsCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls;
     66 
     67   /**
     68    * Reference to the execution context.
     69    */
     70   struct GNUNET_CURL_Context *ctx;
     71 
     72   /**
     73    * Minor context that holds body and headers.
     74    */
     75   struct TALER_CURL_PostContext post_ctx;
     76 
     77   /**
     78    * Request body.
     79    */
     80   json_t *req_obj;
     81 };
     82 
     83 
     84 /**
     85  * Function called when we're done processing the
     86  * HTTP POST /private/fountains request.
     87  *
     88  * @param cls the `struct TALER_MERCHANT_PostPrivateFountainsHandle`
     89  * @param response_code HTTP response code, 0 on error
     90  * @param response response body, NULL if not in JSON
     91  */
     92 static void
     93 handle_post_fountains_finished (void *cls,
     94                                 long response_code,
     95                                 const void *response)
     96 {
     97   struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh = cls;
     98   const json_t *json = response;
     99   struct TALER_MERCHANT_PostPrivateFountainsResponse pfr = {
    100     .hr.http_status = (unsigned int) response_code,
    101     .hr.reply = json
    102   };
    103 
    104   ppfh->job = NULL;
    105   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    106               "POST /private/fountains completed with response code %u\n",
    107               (unsigned int) response_code);
    108   switch (response_code)
    109   {
    110   case 0:
    111     pfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    112     break;
    113   case MHD_HTTP_OK:
    114     {
    115       struct GNUNET_JSON_Specification spec[] = {
    116         GNUNET_JSON_spec_string ("fountain_id",
    117                                  &pfr.details.ok.fountain_id),
    118         GNUNET_JSON_spec_string ("fountain_secret",
    119                                  &pfr.details.ok.fountain_secret),
    120         GNUNET_JSON_spec_end ()
    121       };
    122 
    123       if (GNUNET_OK !=
    124           GNUNET_JSON_parse (json,
    125                              spec,
    126                              NULL,
    127                              NULL))
    128       {
    129         GNUNET_break_op (0);
    130         pfr.hr.http_status = 0;
    131         pfr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    132       }
    133       break;
    134     }
    135   case MHD_HTTP_BAD_REQUEST:
    136   case MHD_HTTP_UNAUTHORIZED:
    137   case MHD_HTTP_FORBIDDEN:
    138   case MHD_HTTP_NOT_FOUND:
    139   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    140     pfr.hr.ec = TALER_JSON_get_error_code (json);
    141     pfr.hr.hint = TALER_JSON_get_error_hint (json);
    142     break;
    143   default:
    144     TALER_MERCHANT_parse_error_details_ (json,
    145                                          response_code,
    146                                          &pfr.hr);
    147     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    148                 "Unexpected response code %u/%d\n",
    149                 (unsigned int) response_code,
    150                 (int) pfr.hr.ec);
    151     GNUNET_break_op (0);
    152     break;
    153   }
    154   ppfh->cb (ppfh->cb_cls,
    155             &pfr);
    156   TALER_MERCHANT_post_private_fountains_cancel (ppfh);
    157 }
    158 
    159 
    160 struct TALER_MERCHANT_PostPrivateFountainsHandle *
    161 TALER_MERCHANT_post_private_fountains_create (
    162   struct GNUNET_CURL_Context *ctx,
    163   const char *url,
    164   const char *description,
    165   struct GNUNET_TIME_Relative poll_freq,
    166   unsigned int num_grants,
    167   const struct TALER_MERCHANT_FountainGrant *grants)
    168 {
    169   struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh;
    170   json_t *jgrants;
    171 
    172   jgrants = json_array ();
    173   GNUNET_assert (NULL != jgrants);
    174   for (unsigned int i = 0; i < num_grants; i++)
    175   {
    176     GNUNET_assert (0 ==
    177                    json_array_append_new (
    178                      jgrants,
    179                      GNUNET_JSON_PACK (
    180                        GNUNET_JSON_pack_string ("token_family_slug",
    181                                                 grants[i].token_family_slug),
    182                        GNUNET_JSON_pack_uint64 ("tokens_per_period_limit",
    183                                                 grants[i].
    184                                                 tokens_per_period_limit),
    185                        GNUNET_JSON_pack_uint64 ("tokens_per_period_stash",
    186                                                 grants[i].
    187                                                 tokens_per_period_stash),
    188                        GNUNET_JSON_pack_uint64 ("key_window_size",
    189                                                 grants[i].key_window_size))));
    190   }
    191   ppfh = GNUNET_new (struct TALER_MERCHANT_PostPrivateFountainsHandle);
    192   ppfh->ctx = ctx;
    193   ppfh->base_url = GNUNET_strdup (url);
    194   ppfh->req_obj = GNUNET_JSON_PACK (
    195     GNUNET_JSON_pack_string ("description",
    196                              description),
    197     GNUNET_JSON_pack_time_rel ("poll_freq",
    198                                poll_freq),
    199     GNUNET_JSON_pack_array_steal ("grants",
    200                                   jgrants));
    201   return ppfh;
    202 }
    203 
    204 
    205 enum TALER_ErrorCode
    206 TALER_MERCHANT_post_private_fountains_start (
    207   struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh,
    208   TALER_MERCHANT_PostPrivateFountainsCallback cb,
    209   TALER_MERCHANT_POST_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls)
    210 {
    211   CURL *eh;
    212 
    213   ppfh->cb = cb;
    214   ppfh->cb_cls = cb_cls;
    215   ppfh->url = TALER_url_join (ppfh->base_url,
    216                               "private/fountains",
    217                               NULL);
    218   if (NULL == ppfh->url)
    219     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    220   eh = TALER_MERCHANT_curl_easy_get_ (ppfh->url);
    221   if ( (NULL == eh) ||
    222        (GNUNET_OK !=
    223         TALER_curl_easy_post (&ppfh->post_ctx,
    224                               eh,
    225                               ppfh->req_obj)) )
    226   {
    227     GNUNET_break (0);
    228     if (NULL != eh)
    229       curl_easy_cleanup (eh);
    230     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    231   }
    232   ppfh->job = GNUNET_CURL_job_add2 (ppfh->ctx,
    233                                     eh,
    234                                     ppfh->post_ctx.headers,
    235                                     &handle_post_fountains_finished,
    236                                     ppfh);
    237   if (NULL == ppfh->job)
    238     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    239   return TALER_EC_NONE;
    240 }
    241 
    242 
    243 void
    244 TALER_MERCHANT_post_private_fountains_cancel (
    245   struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh)
    246 {
    247   if (NULL != ppfh->job)
    248   {
    249     GNUNET_CURL_job_cancel (ppfh->job);
    250     ppfh->job = NULL;
    251   }
    252   TALER_curl_easy_post_finished (&ppfh->post_ctx);
    253   json_decref (ppfh->req_obj);
    254   GNUNET_free (ppfh->url);
    255   GNUNET_free (ppfh->base_url);
    256   GNUNET_free (ppfh);
    257 }
    258 
    259 
    260 /* end of merchant_api_post-private-fountains.c */