merchant

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

merchant_api_post-private-units.c (9803B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2025-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 merchant_api_post-private-units-new.c
     21  * @brief Implementation of the POST /private/units request
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/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/taler-merchant/post-private-units.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/units operation.
     39  */
     40 struct TALER_MERCHANT_PostPrivateUnitsHandle
     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_PostPrivateUnitsCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_PRIVATE_UNITS_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    * Unit identifier.
     79    */
     80   char *unit_id;
     81 
     82   /**
     83    * Long human-readable name.
     84    */
     85   char *unit_name_long;
     86 
     87   /**
     88    * Short symbol for the unit.
     89    */
     90   char *unit_name_short;
     91 
     92   /**
     93    * Whether fractional quantities are allowed.
     94    */
     95   bool unit_allow_fraction;
     96 
     97   /**
     98    * Precision level for fractional quantities.
     99    */
    100   uint32_t unit_precision_level;
    101 
    102   /**
    103    * Whether the unit is active.
    104    */
    105   bool unit_active;
    106 
    107   /**
    108    * Whether @e unit_allow_fraction was explicitly set via options.
    109    */
    110   bool have_unit_allow_fraction;
    111 
    112   /**
    113    * Whether @e unit_precision_level was explicitly set via options.
    114    */
    115   bool have_unit_precision_level;
    116 
    117   /**
    118    * Whether @e unit_active was explicitly set via options.
    119    */
    120   bool have_unit_active;
    121 
    122   /**
    123    * Optional internationalized long names (JSON).
    124    */
    125   json_t *unit_name_long_i18n;
    126 
    127   /**
    128    * Optional internationalized short names (JSON).
    129    */
    130   json_t *unit_name_short_i18n;
    131 };
    132 
    133 
    134 /**
    135  * Function called when we're done processing the
    136  * HTTP POST /private/units request.
    137  *
    138  * @param cls the `struct TALER_MERCHANT_PostPrivateUnitsHandle`
    139  * @param response_code HTTP response code, 0 on error
    140  * @param response response body, NULL if not in JSON
    141  */
    142 static void
    143 handle_post_units_finished (void *cls,
    144                             long response_code,
    145                             const void *response)
    146 {
    147   struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh = cls;
    148   const json_t *json = response;
    149   struct TALER_MERCHANT_PostPrivateUnitsResponse pur = {
    150     .hr.http_status = (unsigned int) response_code,
    151     .hr.reply = json
    152   };
    153 
    154   ppuh->job = NULL;
    155   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    156               "POST /private/units completed with response code %u\n",
    157               (unsigned int) response_code);
    158   switch (response_code)
    159   {
    160   case 0:
    161     pur.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    162     break;
    163   case MHD_HTTP_NO_CONTENT:
    164     break;
    165   case MHD_HTTP_BAD_REQUEST:
    166   case MHD_HTTP_UNAUTHORIZED:
    167   case MHD_HTTP_FORBIDDEN:
    168   case MHD_HTTP_NOT_FOUND:
    169   case MHD_HTTP_CONFLICT:
    170   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    171     pur.hr.ec = TALER_JSON_get_error_code (json);
    172     pur.hr.hint = TALER_JSON_get_error_hint (json);
    173     break;
    174   default:
    175     TALER_MERCHANT_parse_error_details_ (json,
    176                                          response_code,
    177                                          &pur.hr);
    178     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    179                 "Unexpected response code %u/%d\n",
    180                 (unsigned int) response_code,
    181                 (int) pur.hr.ec);
    182     GNUNET_break_op (0);
    183     break;
    184   }
    185   ppuh->cb (ppuh->cb_cls,
    186             &pur);
    187   TALER_MERCHANT_post_private_units_cancel (ppuh);
    188 }
    189 
    190 
    191 struct TALER_MERCHANT_PostPrivateUnitsHandle *
    192 TALER_MERCHANT_post_private_units_create (
    193   struct GNUNET_CURL_Context *ctx,
    194   const char *url,
    195   const char *unit_id,
    196   const char *unit_name_long,
    197   const char *unit_name_short)
    198 {
    199   struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh;
    200 
    201   ppuh = GNUNET_new (struct TALER_MERCHANT_PostPrivateUnitsHandle);
    202   ppuh->ctx = ctx;
    203   ppuh->base_url = GNUNET_strdup (url);
    204   ppuh->unit_id = GNUNET_strdup (unit_id);
    205   ppuh->unit_name_long = GNUNET_strdup (unit_name_long);
    206   ppuh->unit_name_short = GNUNET_strdup (unit_name_short);
    207   return ppuh;
    208 }
    209 
    210 
    211 enum GNUNET_GenericReturnValue
    212 TALER_MERCHANT_post_private_units_set_options_ (
    213   struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh,
    214   unsigned int num_options,
    215   const struct TALER_MERCHANT_PostPrivateUnitsOptionValue *options)
    216 {
    217   for (unsigned int i = 0; i < num_options; i++)
    218   {
    219     switch (options[i].option)
    220     {
    221     case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_END:
    222       return GNUNET_OK;
    223     case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_NAME_LONG_I18N:
    224       ppuh->unit_name_long_i18n
    225         = json_incref ((json_t *) options[i].details.unit_name_long_i18n);
    226       break;
    227     case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_NAME_SHORT_I18N:
    228       ppuh->unit_name_short_i18n
    229         = json_incref ((json_t *) options[i].details.unit_name_short_i18n);
    230       break;
    231     case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_ALLOW_FRACTION:
    232       ppuh->unit_allow_fraction = options[i].details.unit_allow_fraction;
    233       ppuh->have_unit_allow_fraction = true;
    234       break;
    235     case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_PRECISION_LEVEL:
    236       ppuh->unit_precision_level = options[i].details.unit_precision_level;
    237       ppuh->have_unit_precision_level = true;
    238       break;
    239     case TALER_MERCHANT_POST_PRIVATE_UNITS_OPTION_UNIT_ACTIVE:
    240       ppuh->unit_active = options[i].details.unit_active;
    241       ppuh->have_unit_active = true;
    242       break;
    243     default:
    244       GNUNET_break (0);
    245       return GNUNET_SYSERR;
    246     }
    247   }
    248   return GNUNET_OK;
    249 }
    250 
    251 
    252 enum TALER_ErrorCode
    253 TALER_MERCHANT_post_private_units_start (
    254   struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh,
    255   TALER_MERCHANT_PostPrivateUnitsCallback cb,
    256   TALER_MERCHANT_POST_PRIVATE_UNITS_RESULT_CLOSURE *cb_cls)
    257 {
    258   json_t *req_obj;
    259   CURL *eh;
    260 
    261   ppuh->cb = cb;
    262   ppuh->cb_cls = cb_cls;
    263   ppuh->url = TALER_url_join (ppuh->base_url,
    264                               "private/units",
    265                               NULL);
    266   if (NULL == ppuh->url)
    267     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    268   req_obj = GNUNET_JSON_PACK (
    269     GNUNET_JSON_pack_string ("unit",
    270                              ppuh->unit_id),
    271     GNUNET_JSON_pack_string ("unit_name_long",
    272                              ppuh->unit_name_long),
    273     GNUNET_JSON_pack_string ("unit_name_short",
    274                              ppuh->unit_name_short),
    275     GNUNET_JSON_pack_bool ("unit_allow_fraction",
    276                            ppuh->have_unit_allow_fraction
    277                            ? ppuh->unit_allow_fraction
    278                            : false),
    279     GNUNET_JSON_pack_uint64 ("unit_precision_level",
    280                              ppuh->have_unit_precision_level
    281                              ? (uint64_t) ppuh->unit_precision_level
    282                              : 0),
    283     GNUNET_JSON_pack_bool ("unit_active",
    284                            ppuh->have_unit_active
    285                            ? ppuh->unit_active
    286                            : true),
    287     GNUNET_JSON_pack_allow_null (
    288       GNUNET_JSON_pack_object_incref ("unit_name_long_i18n",
    289                                       ppuh->unit_name_long_i18n)),
    290     GNUNET_JSON_pack_allow_null (
    291       GNUNET_JSON_pack_object_incref ("unit_name_short_i18n",
    292                                       ppuh->unit_name_short_i18n)));
    293   eh = TALER_MERCHANT_curl_easy_get_ (ppuh->url);
    294   if ( (NULL == eh) ||
    295        (GNUNET_OK !=
    296         TALER_curl_easy_post (&ppuh->post_ctx,
    297                               eh,
    298                               req_obj)) )
    299   {
    300     GNUNET_break (0);
    301     json_decref (req_obj);
    302     if (NULL != eh)
    303       curl_easy_cleanup (eh);
    304     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    305   }
    306   json_decref (req_obj);
    307   ppuh->job = GNUNET_CURL_job_add2 (ppuh->ctx,
    308                                     eh,
    309                                     ppuh->post_ctx.headers,
    310                                     &handle_post_units_finished,
    311                                     ppuh);
    312   if (NULL == ppuh->job)
    313     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    314   return TALER_EC_NONE;
    315 }
    316 
    317 
    318 void
    319 TALER_MERCHANT_post_private_units_cancel (
    320   struct TALER_MERCHANT_PostPrivateUnitsHandle *ppuh)
    321 {
    322   if (NULL != ppuh->job)
    323   {
    324     GNUNET_CURL_job_cancel (ppuh->job);
    325     ppuh->job = NULL;
    326   }
    327   TALER_curl_easy_post_finished (&ppuh->post_ctx);
    328   json_decref (ppuh->unit_name_long_i18n);
    329   json_decref (ppuh->unit_name_short_i18n);
    330   GNUNET_free (ppuh->unit_id);
    331   GNUNET_free (ppuh->unit_name_long);
    332   GNUNET_free (ppuh->unit_name_short);
    333   GNUNET_free (ppuh->url);
    334   GNUNET_free (ppuh->base_url);
    335   GNUNET_free (ppuh);
    336 }
    337 
    338 
    339 /* end of merchant_api_post-private-units-new.c */