merchant

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

merchant_api_get-private-units-UNIT.c (7259B)


      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 it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   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 details.
     12 
     13   You should have received a copy of the GNU Lesser General Public License along with
     14   TALER; see the file COPYING.LGPL.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file merchant_api_get-private-units-UNIT.c
     19  * @brief Implementation of the GET /private/units/$UNIT request
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include <curl/curl.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h> /* just for HTTP status codes */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include <taler/taler-merchant/get-private-units-UNIT.h>
     29 #include "merchant_api_curl_defaults.h"
     30 #include <taler/taler_json_lib.h>
     31 
     32 
     33 /**
     34  * Handle for a GET /private/units/$UNIT operation.
     35  */
     36 struct TALER_MERCHANT_GetPrivateUnitHandle
     37 {
     38   /**
     39    * Base URL of the merchant backend.
     40    */
     41   char *base_url;
     42 
     43   /**
     44    * The full URL for this request.
     45    */
     46   char *url;
     47 
     48   /**
     49    * Handle for the request.
     50    */
     51   struct GNUNET_CURL_Job *job;
     52 
     53   /**
     54    * Function to call with the result.
     55    */
     56   TALER_MERCHANT_GetPrivateUnitCallback cb;
     57 
     58   /**
     59    * Closure for @a cb.
     60    */
     61   TALER_MERCHANT_GET_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls;
     62 
     63   /**
     64    * Reference to the execution context.
     65    */
     66   struct GNUNET_CURL_Context *ctx;
     67 
     68   /**
     69    * Unit identifier.
     70    */
     71   char *unit_id;
     72 };
     73 
     74 
     75 /**
     76  * Parse the JSON response into the unit entry.
     77  *
     78  * @param json full JSON reply
     79  * @param ugr response descriptor to populate
     80  * @return #GNUNET_OK on success
     81  */
     82 static enum GNUNET_GenericReturnValue
     83 parse_unit (const json_t *json,
     84             struct TALER_MERCHANT_GetPrivateUnitResponse *ugr)
     85 {
     86   struct TALER_MERCHANT_UnitEntry *entry = &ugr->details.ok.unit;
     87   struct GNUNET_JSON_Specification spec[] = {
     88     GNUNET_JSON_spec_string ("unit",
     89                              &entry->unit),
     90     GNUNET_JSON_spec_string ("unit_name_long",
     91                              &entry->unit_name_long),
     92     GNUNET_JSON_spec_string ("unit_name_short",
     93                              &entry->unit_name_short),
     94     GNUNET_JSON_spec_mark_optional (
     95       GNUNET_JSON_spec_object_const ("unit_name_long_i18n",
     96                                      &entry->unit_name_long_i18n),
     97       NULL),
     98     GNUNET_JSON_spec_mark_optional (
     99       GNUNET_JSON_spec_object_const ("unit_name_short_i18n",
    100                                      &entry->unit_name_short_i18n),
    101       NULL),
    102     GNUNET_JSON_spec_bool ("unit_allow_fraction",
    103                            &entry->unit_allow_fraction),
    104     GNUNET_JSON_spec_uint32 ("unit_precision_level",
    105                              &entry->unit_precision_level),
    106     GNUNET_JSON_spec_bool ("unit_active",
    107                            &entry->unit_active),
    108     GNUNET_JSON_spec_bool ("unit_builtin",
    109                            &entry->unit_builtin),
    110     GNUNET_JSON_spec_uint64 ("unit_serial",
    111                              &entry->unit_serial),
    112     GNUNET_JSON_spec_end ()
    113   };
    114 
    115   memset (entry,
    116           0,
    117           sizeof (*entry));
    118   if (GNUNET_OK !=
    119       GNUNET_JSON_parse (json,
    120                          spec,
    121                          NULL,
    122                          NULL))
    123   {
    124     GNUNET_break_op (0);
    125     return GNUNET_SYSERR;
    126   }
    127   return GNUNET_OK;
    128 }
    129 
    130 
    131 /**
    132  * Function called when we're done processing the
    133  * HTTP GET /private/units/$UNIT request.
    134  *
    135  * @param cls the `struct TALER_MERCHANT_GetPrivateUnitHandle`
    136  * @param response_code HTTP response code, 0 on error
    137  * @param response response body, NULL if not in JSON
    138  */
    139 static void
    140 handle_get_unit_finished (void *cls,
    141                           long response_code,
    142                           const void *response)
    143 {
    144   struct TALER_MERCHANT_GetPrivateUnitHandle *gpu = cls;
    145   const json_t *json = response;
    146   struct TALER_MERCHANT_GetPrivateUnitResponse ugr = {
    147     .hr.http_status = (unsigned int) response_code,
    148     .hr.reply = json
    149   };
    150 
    151   gpu->job = NULL;
    152   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    153               "Got /private/units/$UNIT response with status code %u\n",
    154               (unsigned int) response_code);
    155   switch (response_code)
    156   {
    157   case MHD_HTTP_OK:
    158     if (GNUNET_OK !=
    159         parse_unit (json,
    160                     &ugr))
    161     {
    162       ugr.hr.http_status = 0;
    163       ugr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    164       break;
    165     }
    166     gpu->cb (gpu->cb_cls,
    167              &ugr);
    168     TALER_MERCHANT_get_private_unit_cancel (gpu);
    169     return;
    170   case MHD_HTTP_UNAUTHORIZED:
    171   case MHD_HTTP_FORBIDDEN:
    172   case MHD_HTTP_NOT_FOUND:
    173     ugr.hr.ec = TALER_JSON_get_error_code (json);
    174     ugr.hr.hint = TALER_JSON_get_error_hint (json);
    175     break;
    176   default:
    177     ugr.hr.ec = TALER_JSON_get_error_code (json);
    178     ugr.hr.hint = TALER_JSON_get_error_hint (json);
    179     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    180                 "Unexpected response code %u/%d\n",
    181                 (unsigned int) response_code,
    182                 (int) ugr.hr.ec);
    183     break;
    184   }
    185   gpu->cb (gpu->cb_cls,
    186            &ugr);
    187   TALER_MERCHANT_get_private_unit_cancel (gpu);
    188 }
    189 
    190 
    191 struct TALER_MERCHANT_GetPrivateUnitHandle *
    192 TALER_MERCHANT_get_private_unit_create (
    193   struct GNUNET_CURL_Context *ctx,
    194   const char *url,
    195   const char *unit_id)
    196 {
    197   struct TALER_MERCHANT_GetPrivateUnitHandle *gpu;
    198 
    199   gpu = GNUNET_new (struct TALER_MERCHANT_GetPrivateUnitHandle);
    200   gpu->ctx = ctx;
    201   gpu->base_url = GNUNET_strdup (url);
    202   gpu->unit_id = GNUNET_strdup (unit_id);
    203   return gpu;
    204 }
    205 
    206 
    207 enum TALER_ErrorCode
    208 TALER_MERCHANT_get_private_unit_start (
    209   struct TALER_MERCHANT_GetPrivateUnitHandle *gpu,
    210   TALER_MERCHANT_GetPrivateUnitCallback cb,
    211   TALER_MERCHANT_GET_PRIVATE_UNIT_RESULT_CLOSURE *cb_cls)
    212 {
    213   CURL *eh;
    214 
    215   gpu->cb = cb;
    216   gpu->cb_cls = cb_cls;
    217   {
    218     char *path;
    219 
    220     GNUNET_asprintf (&path,
    221                      "private/units/%s",
    222                      gpu->unit_id);
    223     gpu->url = TALER_url_join (gpu->base_url,
    224                                path,
    225                                NULL);
    226     GNUNET_free (path);
    227   }
    228   if (NULL == gpu->url)
    229     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    230   eh = TALER_MERCHANT_curl_easy_get_ (gpu->url);
    231   if (NULL == eh)
    232     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    233   gpu->job = GNUNET_CURL_job_add (gpu->ctx,
    234                                   eh,
    235                                   &handle_get_unit_finished,
    236                                   gpu);
    237   if (NULL == gpu->job)
    238     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    239   return TALER_EC_NONE;
    240 }
    241 
    242 
    243 void
    244 TALER_MERCHANT_get_private_unit_cancel (
    245   struct TALER_MERCHANT_GetPrivateUnitHandle *gpu)
    246 {
    247   if (NULL != gpu->job)
    248   {
    249     GNUNET_CURL_job_cancel (gpu->job);
    250     gpu->job = NULL;
    251   }
    252   GNUNET_free (gpu->url);
    253   GNUNET_free (gpu->base_url);
    254   GNUNET_free (gpu->unit_id);
    255   GNUNET_free (gpu);
    256 }
    257 
    258 
    259 /* end of merchant_api_get-private-units-UNIT.c */