merchant

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

merchant_api_get_units.c (8790B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free
      7   Software Foundation; either version 2.1, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
     12   details.
     13 
     14   You should have received a copy of the GNU Lesser General Public License along
     15   with TALER; see the file COPYING.LGPL.  If not, see
     16   <http://www.gnu.org/licenses/>
     17 */
     18 /**
     19  * @file merchant_api_get_units.c
     20  * @brief Implementation of GET /private/units
     21  * @author Bohdan Potuzhnyi
     22  */
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <jansson.h>
     26 #include <microhttpd.h>
     27 #include <gnunet/gnunet_util_lib.h>
     28 #include <gnunet/gnunet_curl_lib.h>
     29 #include "taler_merchant_service.h"
     30 #include "merchant_api_curl_defaults.h"
     31 #include <taler/taler_json_lib.h>
     32 
     33 
     34 /**
     35  * Maximum number of units returned in a single response.
     36  */
     37 #define MAX_UNITS 1024
     38 
     39 
     40 /**
     41  * Handle for a GET /private/units operation.
     42  */
     43 struct TALER_MERCHANT_UnitsGetHandle
     44 {
     45   /**
     46    * Fully qualified request URL.
     47    */
     48   char *url;
     49 
     50   /**
     51    * In-flight job handle.
     52    */
     53   struct GNUNET_CURL_Job *job;
     54 
     55   /**
     56    * Callback to invoke with the outcome.
     57    */
     58   TALER_MERCHANT_UnitsGetCallback cb;
     59 
     60   /**
     61    * Closure for @e cb.
     62    */
     63   void *cb_cls;
     64 
     65   /**
     66    * Execution context.
     67    */
     68   struct GNUNET_CURL_Context *ctx;
     69 };
     70 
     71 
     72 /**
     73  * Parse an individual unit entry from @a value.
     74  *
     75  * @param value JSON object describing the unit
     76  * @param[out] ue set to the parsed values
     77  * @return #GNUNET_OK on success
     78  */
     79 static enum GNUNET_GenericReturnValue
     80 parse_unit_entry (const json_t *value,
     81                   struct TALER_MERCHANT_UnitEntry *ue)
     82 {
     83   const char *unit;
     84   const char *unit_name_long;
     85   const char *unit_name_short;
     86   const json_t *unit_name_long_i18n = NULL;
     87   const json_t *unit_name_short_i18n = NULL;
     88   bool unit_allow_fraction;
     89   bool unit_active;
     90   bool unit_builtin;
     91   uint32_t unit_precision_level;
     92   struct GNUNET_JSON_Specification spec[] = {
     93     GNUNET_JSON_spec_string ("unit",
     94                              &unit),
     95     GNUNET_JSON_spec_string ("unit_name_long",
     96                              &unit_name_long),
     97     GNUNET_JSON_spec_string ("unit_name_short",
     98                              &unit_name_short),
     99     GNUNET_JSON_spec_mark_optional (
    100       GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
    101                                      &unit_name_long_i18n),
    102       NULL),
    103     GNUNET_JSON_spec_mark_optional (
    104       GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
    105                                      &unit_name_short_i18n),
    106       NULL),
    107     GNUNET_JSON_spec_bool ("unit_allow_fraction",
    108                            &unit_allow_fraction),
    109     GNUNET_JSON_spec_uint32 ("unit_precision_level",
    110                              &unit_precision_level),
    111     GNUNET_JSON_spec_bool ("unit_active",
    112                            &unit_active),
    113     GNUNET_JSON_spec_bool ("unit_builtin",
    114                            &unit_builtin),
    115     GNUNET_JSON_spec_end ()
    116   };
    117 
    118   if (GNUNET_OK !=
    119       GNUNET_JSON_parse (value,
    120                          spec,
    121                          NULL,
    122                          NULL))
    123   {
    124     GNUNET_break_op (0);
    125     GNUNET_JSON_parse_free (spec);
    126     return GNUNET_SYSERR;
    127   }
    128   GNUNET_JSON_parse_free (spec);
    129   ue->unit = unit;
    130   ue->unit_name_long = unit_name_long;
    131   ue->unit_name_short = unit_name_short;
    132   ue->unit_name_long_i18n = unit_name_long_i18n;
    133   ue->unit_name_short_i18n = unit_name_short_i18n;
    134   ue->unit_allow_fraction = unit_allow_fraction;
    135   ue->unit_precision_level = unit_precision_level;
    136   ue->unit_active = unit_active;
    137   ue->unit_builtin = unit_builtin;
    138   return GNUNET_OK;
    139 }
    140 
    141 
    142 /**
    143  * Parse the list of units from @a units and call the callback.
    144  *
    145  * @param json complete response JSON
    146  * @param units array of units
    147  * @param ugh ongoing operation handle
    148  * @return #GNUNET_OK on success
    149  */
    150 static enum GNUNET_GenericReturnValue
    151 parse_units (const json_t *json,
    152              const json_t *units,
    153              struct TALER_MERCHANT_UnitsGetHandle *ugh)
    154 {
    155   size_t len;
    156 
    157   len = json_array_size (units);
    158   if (len > MAX_UNITS)
    159   {
    160     GNUNET_break_op (0);
    161     return GNUNET_SYSERR;
    162   }
    163 
    164   {
    165     struct TALER_MERCHANT_UnitEntry entries[GNUNET_NZL (len)];
    166     size_t idx;
    167     json_t *value;
    168 
    169     json_array_foreach (units, idx, value) {
    170       if (GNUNET_OK !=
    171           parse_unit_entry (value,
    172                             &entries[idx]))
    173       {
    174         GNUNET_break_op (0);
    175         return GNUNET_SYSERR;
    176       }
    177     }
    178     {
    179       struct TALER_MERCHANT_UnitsGetResponse ugr = {
    180         .hr.http_status = MHD_HTTP_OK,
    181         .hr.reply = json,
    182         .details = {
    183           .ok = {
    184             .units = entries,
    185             .units_length = (unsigned int) len
    186           }
    187 
    188 
    189         }
    190 
    191 
    192       };
    193 
    194       ugh->cb (ugh->cb_cls,
    195                &ugr);
    196     }
    197   }
    198   return GNUNET_OK;
    199 }
    200 
    201 
    202 /**
    203  * Called when the HTTP transfer finishes.
    204  *
    205  * @param cls closure, the operation handle
    206  * @param response_code HTTP status (0 on network errors)
    207  * @param response parsed JSON body (NULL if parsing failed)
    208  */
    209 static void
    210 handle_get_units_finished (void *cls,
    211                            long response_code,
    212                            const void *response)
    213 {
    214   struct TALER_MERCHANT_UnitsGetHandle *ugh = cls;
    215   const json_t *json = response;
    216   struct TALER_MERCHANT_UnitsGetResponse ugr = {
    217     .hr.http_status = (unsigned int) response_code,
    218     .hr.reply = json
    219   };
    220 
    221   ugh->job = NULL;
    222   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    223               "GET /private/units finished with status %u\n",
    224               (unsigned int) response_code);
    225   switch (response_code)
    226   {
    227   case MHD_HTTP_OK:
    228     {
    229       const json_t *units;
    230       struct GNUNET_JSON_Specification spec[] = {
    231         GNUNET_JSON_spec_array_const ("units",
    232                                       &units),
    233         GNUNET_JSON_spec_end ()
    234       };
    235 
    236       if (GNUNET_OK !=
    237           GNUNET_JSON_parse (json,
    238                              spec,
    239                              NULL,
    240                              NULL))
    241       {
    242         GNUNET_break_op (0);
    243         ugr.hr.http_status = 0;
    244         ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    245         break;
    246       }
    247       if (GNUNET_OK ==
    248           parse_units (json,
    249                        units,
    250                        ugh))
    251       {
    252         TALER_MERCHANT_units_get_cancel (ugh);
    253         return;
    254       }
    255       ugr.hr.http_status = 0;
    256       ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    257       break;
    258     }
    259   case MHD_HTTP_UNAUTHORIZED:
    260   case MHD_HTTP_FORBIDDEN:
    261   case MHD_HTTP_NOT_FOUND:
    262   case MHD_HTTP_CONFLICT:
    263     ugr.hr.ec = TALER_JSON_get_error_code (json);
    264     ugr.hr.hint = TALER_JSON_get_error_hint (json);
    265     break;
    266   case 0:
    267     ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    268     break;
    269   default:
    270     ugr.hr.ec = TALER_JSON_get_error_code (json);
    271     ugr.hr.hint = TALER_JSON_get_error_hint (json);
    272     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    273                 "Unexpected response code %u/%d for GET /private/units\n",
    274                 (unsigned int) response_code,
    275                 (int) ugr.hr.ec);
    276     break;
    277   }
    278   ugh->cb (ugh->cb_cls,
    279            &ugr);
    280   TALER_MERCHANT_units_get_cancel (ugh);
    281 }
    282 
    283 
    284 struct TALER_MERCHANT_UnitsGetHandle *
    285 TALER_MERCHANT_units_get (struct GNUNET_CURL_Context *ctx,
    286                           const char *backend_url,
    287                           TALER_MERCHANT_UnitsGetCallback cb,
    288                           void *cb_cls)
    289 {
    290   struct TALER_MERCHANT_UnitsGetHandle *ugh;
    291   CURL *eh;
    292 
    293   ugh = GNUNET_new (struct TALER_MERCHANT_UnitsGetHandle);
    294   ugh->ctx = ctx;
    295   ugh->cb = cb;
    296   ugh->cb_cls = cb_cls;
    297   ugh->url = TALER_url_join (backend_url,
    298                              "private/units",
    299                              NULL);
    300   if (NULL == ugh->url)
    301   {
    302     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    303                 "Failed to build /private/units URL\n");
    304     GNUNET_free (ugh);
    305     return NULL;
    306   }
    307   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    308               "Requesting URL '%s'\n",
    309               ugh->url);
    310   eh = TALER_MERCHANT_curl_easy_get_ (ugh->url);
    311   ugh->job = GNUNET_CURL_job_add (ctx,
    312                                   eh,
    313                                   &handle_get_units_finished,
    314                                   ugh);
    315   return ugh;
    316 }
    317 
    318 
    319 void
    320 TALER_MERCHANT_units_get_cancel (struct TALER_MERCHANT_UnitsGetHandle *ugh)
    321 {
    322   if (NULL != ugh->job)
    323     GNUNET_CURL_job_cancel (ugh->job);
    324   GNUNET_free (ugh->url);
    325   GNUNET_free (ugh);
    326 }
    327 
    328 
    329 /* end of merchant_api_get_units.c */