merchant

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

merchant_api_post-private-categories.c (8031B)


      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-categories-new.c
     21  * @brief Implementation of the POST /private/categories 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-categories.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/categories operation.
     39  */
     40 struct TALER_MERCHANT_PostPrivateCategoriesHandle
     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_PostPrivateCategoriesCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_PRIVATE_CATEGORIES_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    * Human-readable name of the category.
     79    */
     80   char *name;
     81 
     82   /**
     83    * Optional internationalized names (JSON).
     84    */
     85   const json_t *name_i18n;
     86 };
     87 
     88 
     89 /**
     90  * Function called when we're done processing the
     91  * HTTP POST /private/categories request.
     92  *
     93  * @param cls the `struct TALER_MERCHANT_PostPrivateCategoriesHandle`
     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_post_categories_finished (void *cls,
     99                                  long response_code,
    100                                  const void *response)
    101 {
    102   struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch = cls;
    103   const json_t *json = response;
    104   struct TALER_MERCHANT_PostPrivateCategoriesResponse cpr = {
    105     .hr.http_status = (unsigned int) response_code,
    106     .hr.reply = json
    107   };
    108 
    109   ppch->job = NULL;
    110   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    111               "POST /private/categories completed with response code %u\n",
    112               (unsigned int) response_code);
    113   switch (response_code)
    114   {
    115   case 0:
    116     cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    117     break;
    118   case MHD_HTTP_OK:
    119     {
    120       struct GNUNET_JSON_Specification spec[] = {
    121         GNUNET_JSON_spec_uint64 ("category_id",
    122                                  &cpr.details.ok.category_id),
    123         GNUNET_JSON_spec_end ()
    124       };
    125 
    126       if (GNUNET_OK !=
    127           GNUNET_JSON_parse (json,
    128                              spec,
    129                              NULL, NULL))
    130       {
    131         GNUNET_break_op (0);
    132         cpr.hr.http_status = 0;
    133         cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    134         break;
    135       }
    136     }
    137     break;
    138   case MHD_HTTP_BAD_REQUEST:
    139     cpr.hr.ec = TALER_JSON_get_error_code (json);
    140     cpr.hr.hint = TALER_JSON_get_error_hint (json);
    141     break;
    142   case MHD_HTTP_UNAUTHORIZED:
    143     cpr.hr.ec = TALER_JSON_get_error_code (json);
    144     cpr.hr.hint = TALER_JSON_get_error_hint (json);
    145     break;
    146   case MHD_HTTP_FORBIDDEN:
    147     cpr.hr.ec = TALER_JSON_get_error_code (json);
    148     cpr.hr.hint = TALER_JSON_get_error_hint (json);
    149     break;
    150   case MHD_HTTP_NOT_FOUND:
    151     cpr.hr.ec = TALER_JSON_get_error_code (json);
    152     cpr.hr.hint = TALER_JSON_get_error_hint (json);
    153     break;
    154   case MHD_HTTP_CONFLICT:
    155     cpr.hr.ec = TALER_JSON_get_error_code (json);
    156     cpr.hr.hint = TALER_JSON_get_error_hint (json);
    157     break;
    158   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    159     cpr.hr.ec = TALER_JSON_get_error_code (json);
    160     cpr.hr.hint = TALER_JSON_get_error_hint (json);
    161     break;
    162   default:
    163     TALER_MERCHANT_parse_error_details_ (json,
    164                                          response_code,
    165                                          &cpr.hr);
    166     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    167                 "Unexpected response code %u/%d\n",
    168                 (unsigned int) response_code,
    169                 (int) cpr.hr.ec);
    170     GNUNET_break_op (0);
    171     break;
    172   }
    173   ppch->cb (ppch->cb_cls,
    174             &cpr);
    175   TALER_MERCHANT_post_private_categories_cancel (ppch);
    176 }
    177 
    178 
    179 struct TALER_MERCHANT_PostPrivateCategoriesHandle *
    180 TALER_MERCHANT_post_private_categories_create (
    181   struct GNUNET_CURL_Context *ctx,
    182   const char *url,
    183   const char *name)
    184 {
    185   struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch;
    186 
    187   ppch = GNUNET_new (struct TALER_MERCHANT_PostPrivateCategoriesHandle);
    188   ppch->ctx = ctx;
    189   ppch->base_url = GNUNET_strdup (url);
    190   ppch->name = GNUNET_strdup (name);
    191   return ppch;
    192 }
    193 
    194 
    195 enum GNUNET_GenericReturnValue
    196 TALER_MERCHANT_post_private_categories_set_options_ (
    197   struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch,
    198   unsigned int num_options,
    199   const struct TALER_MERCHANT_PostPrivateCategoriesOptionValue *options)
    200 {
    201   for (unsigned int i = 0; i < num_options; i++)
    202   {
    203     switch (options[i].option)
    204     {
    205     case TALER_MERCHANT_POST_PRIVATE_CATEGORIES_OPTION_END:
    206       return GNUNET_OK;
    207     case TALER_MERCHANT_POST_PRIVATE_CATEGORIES_OPTION_NAME_I18N:
    208       ppch->name_i18n = options[i].details.name_i18n;
    209       break;
    210     default:
    211       GNUNET_break (0);
    212       return GNUNET_SYSERR;
    213     }
    214   }
    215   return GNUNET_OK;
    216 }
    217 
    218 
    219 enum TALER_ErrorCode
    220 TALER_MERCHANT_post_private_categories_start (
    221   struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch,
    222   TALER_MERCHANT_PostPrivateCategoriesCallback cb,
    223   TALER_MERCHANT_POST_PRIVATE_CATEGORIES_RESULT_CLOSURE *cb_cls)
    224 {
    225   json_t *req_obj;
    226   CURL *eh;
    227 
    228   ppch->cb = cb;
    229   ppch->cb_cls = cb_cls;
    230   ppch->url = TALER_url_join (ppch->base_url,
    231                               "private/categories",
    232                               NULL);
    233   if (NULL == ppch->url)
    234     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    235   req_obj = GNUNET_JSON_PACK (
    236     GNUNET_JSON_pack_string (
    237       "name",
    238       ppch->name),
    239     GNUNET_JSON_pack_allow_null (
    240       GNUNET_JSON_pack_object_incref (
    241         "name_i18n",
    242         (json_t *) ppch->name_i18n))
    243     );
    244   eh = TALER_MERCHANT_curl_easy_get_ (ppch->url);
    245   if ( (NULL == eh) ||
    246        (GNUNET_OK !=
    247         TALER_curl_easy_post (&ppch->post_ctx,
    248                               eh,
    249                               req_obj)) )
    250   {
    251     GNUNET_break (0);
    252     json_decref (req_obj);
    253     if (NULL != eh)
    254       curl_easy_cleanup (eh);
    255     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    256   }
    257   json_decref (req_obj);
    258   ppch->job = GNUNET_CURL_job_add2 (ppch->ctx,
    259                                     eh,
    260                                     ppch->post_ctx.headers,
    261                                     &handle_post_categories_finished,
    262                                     ppch);
    263   if (NULL == ppch->job)
    264     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    265   return TALER_EC_NONE;
    266 }
    267 
    268 
    269 void
    270 TALER_MERCHANT_post_private_categories_cancel (
    271   struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch)
    272 {
    273   if (NULL != ppch->job)
    274   {
    275     GNUNET_CURL_job_cancel (ppch->job);
    276     ppch->job = NULL;
    277   }
    278   TALER_curl_easy_post_finished (&ppch->post_ctx);
    279   GNUNET_free (ppch->name);
    280   GNUNET_free (ppch->url);
    281   GNUNET_free (ppch->base_url);
    282   GNUNET_free (ppch);
    283 }
    284 
    285 
    286 /* end of merchant_api_post-private-categories-new.c */