merchant

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

merchant_api_get_donau_instance.c (8797B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2024 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 <http://www.gnu.org/licenses/>
     15  */
     16 /**
     17  * @file merchant_api_get_donau_instance.c
     18  * @brief Implementation of the GET /donau request of the merchant's HTTP API
     19  * @author Bohdan Potuzhnyi
     20  * @author Vlada Svirsh
     21  */
     22 
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <jansson.h>
     26 #include <microhttpd.h> /* for HTTP status codes */
     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 #include <taler/taler_signatures.h>
     33 /* DONAU RELATED IMPORTS */
     34 #include "taler_merchant_donau.h"
     35 #include <donau/donau_service.h>
     36 
     37 /**
     38  * Handle for a GET /donau operation.
     39  */
     40 struct TALER_MERCHANT_DonauInstanceGetHandle
     41 {
     42   /**
     43    * The URL for this request.
     44    */
     45   char *url;
     46 
     47   /**
     48    * Handle for the request.
     49    */
     50   struct GNUNET_CURL_Job *job;
     51 
     52   /**
     53    * Function to call with the result.
     54    */
     55   TALER_MERCHANT_DonauInstanceGetCallback cb;
     56 
     57   /**
     58    * Closure for @a cb.
     59    */
     60   void *cb_cls;
     61 
     62   /**
     63    * Reference to the execution context.
     64    */
     65   struct GNUNET_CURL_Context *ctx;
     66 };
     67 
     68 /**
     69  * Parse Donau instance information from @a ia.
     70  *
     71  * @param ia JSON array (or NULL!) with Donau instance data
     72  * @param igr response to fill
     73  * @param dgh operation handle
     74  * @return #GNUNET_OK on success
     75  */
     76 static enum GNUNET_GenericReturnValue
     77 parse_donau_instances (const json_t *ia,
     78                        struct TALER_MERCHANT_DonauInstanceGetResponse *igr,
     79                        struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
     80 {
     81   unsigned int instances_len = (unsigned int) json_array_size (ia);
     82   struct TALER_MERCHANT_DonauInstanceEntry
     83     instances[GNUNET_NZL (instances_len)];
     84   size_t index;
     85   json_t *value;
     86   struct DONAU_Keys *donau_keys_ptr = NULL;
     87 
     88   if ((json_array_size (ia) != (size_t) instances_len))
     89   {
     90     GNUNET_break (0);
     91     return GNUNET_SYSERR;
     92   }
     93 
     94   json_array_foreach (ia,
     95                       index,
     96                       value)
     97   {
     98     struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index];
     99     const json_t *donau_keys_json = NULL;
    100     struct GNUNET_JSON_Specification spec[] = {
    101       GNUNET_JSON_spec_uint64 ("donau_instance_serial",
    102                                &instance->donau_instance_serial),
    103       GNUNET_JSON_spec_string ("donau_url",
    104                                &instance->donau_url),
    105       GNUNET_JSON_spec_string ("charity_name",
    106                                &instance->charity_name),
    107       GNUNET_JSON_spec_fixed_auto ("charity_pub_key",
    108                                    &instance->charity_pub_key),
    109       GNUNET_JSON_spec_uint64 ("charity_id",
    110                                &instance->charity_id),
    111       TALER_JSON_spec_amount_any ("charity_max_per_year",
    112                                   &instance->charity_max_per_year),
    113       TALER_JSON_spec_amount_any ("charity_receipts_to_date",
    114                                   &instance->charity_receipts_to_date),
    115       GNUNET_JSON_spec_int64 ("current_year",
    116                               &instance->current_year),
    117       GNUNET_JSON_spec_mark_optional (
    118         GNUNET_JSON_spec_object_const ("donau_keys_json",
    119                                        &donau_keys_json),
    120         NULL),
    121       GNUNET_JSON_spec_end ()
    122     };
    123 
    124     if (GNUNET_OK !=
    125         GNUNET_JSON_parse (value,
    126                            spec,
    127                            NULL,
    128                            NULL))
    129     {
    130       GNUNET_break_op (0);
    131       return GNUNET_SYSERR;
    132     }
    133 
    134     /* Parse the Donau keys */
    135     if (NULL != donau_keys_json)
    136     {
    137       donau_keys_ptr = DONAU_keys_from_json (donau_keys_json);
    138       if (NULL == donau_keys_ptr)
    139       {
    140         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    141                     "Failed to convert donau keys from JSON\n");
    142         return GNUNET_SYSERR;
    143       }
    144       instance->donau_keys = donau_keys_ptr;
    145     }
    146   }
    147 
    148   igr->details.ok.donau_instances_length = instances_len;
    149   igr->details.ok.donau_instances = instances;
    150   dgh->cb (dgh->cb_cls,
    151            igr);
    152   dgh->cb = NULL;
    153   if (NULL != donau_keys_ptr)
    154   {
    155     DONAU_keys_decref (donau_keys_ptr);
    156     donau_keys_ptr= NULL;
    157   }
    158   return GNUNET_OK;
    159 }
    160 
    161 
    162 /**
    163  * Function called when we're done processing the
    164  * HTTP /donau request.
    165  *
    166  * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle`
    167  * @param response_code HTTP response code, 0 on error
    168  * @param response response body, NULL if not in JSON
    169  */
    170 static void
    171 handle_get_donau_instances_finished (void *cls,
    172                                      long response_code,
    173                                      const void *response)
    174 {
    175   struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls;
    176   const json_t *json = response;
    177   struct TALER_MERCHANT_DonauInstanceGetResponse igr = {
    178     .hr.http_status = (unsigned int) response_code,
    179     .hr.reply = json
    180   };
    181 
    182   dgh->job = NULL;
    183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    184               "Got /donau response with status code %u\n",
    185               (unsigned int) response_code);
    186 
    187   switch (response_code)
    188   {
    189   case MHD_HTTP_OK:
    190     {
    191       const json_t *donau_instances;
    192       struct GNUNET_JSON_Specification spec[] = {
    193         GNUNET_JSON_spec_array_const ("donau_instances",
    194                                       &donau_instances),
    195         GNUNET_JSON_spec_end ()
    196       };
    197 
    198       if (GNUNET_OK !=
    199           GNUNET_JSON_parse (json,
    200                              spec,
    201                              NULL,
    202                              NULL))
    203       {
    204         igr.hr.http_status = 0;
    205         igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    206         break;
    207       }
    208 
    209       if (GNUNET_OK ==
    210           parse_donau_instances (donau_instances,
    211                                  &igr,
    212                                  dgh))
    213       {
    214         TALER_MERCHANT_donau_instances_get_cancel (dgh);
    215         return;
    216       }
    217 
    218       igr.hr.http_status = 0;
    219       igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    220       break;
    221     }
    222 
    223   case MHD_HTTP_UNAUTHORIZED:
    224   case MHD_HTTP_NOT_FOUND:
    225   default:
    226     igr.hr.ec = TALER_JSON_get_error_code (json);
    227     igr.hr.hint = TALER_JSON_get_error_hint (json);
    228     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    229                 "Unexpected response code %u/%d\n",
    230                 (unsigned int) response_code,
    231                 (int) igr.hr.ec);
    232     break;
    233   }
    234 
    235   dgh->cb (dgh->cb_cls,
    236            &igr);
    237   TALER_MERCHANT_donau_instances_get_cancel (dgh);
    238 }
    239 
    240 
    241 /**
    242  * Initiate the GET /donau request.
    243  *
    244  * @param ctx CURL context
    245  * @param backend_url base URL for the backend
    246  * @param cb callback function to handle the response
    247  * @param cb_cls closure for the callback function
    248  * @return the handle for the operation, or NULL on error
    249  */
    250 struct TALER_MERCHANT_DonauInstanceGetHandle *
    251 TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx,
    252                                     const char *backend_url,
    253                                     TALER_MERCHANT_DonauInstanceGetCallback cb,
    254                                     void *cb_cls)
    255 {
    256   struct TALER_MERCHANT_DonauInstanceGetHandle *dgh;
    257   CURL *eh;
    258 
    259   dgh = GNUNET_new (struct TALER_MERCHANT_DonauInstanceGetHandle);
    260   dgh->ctx = ctx;
    261   dgh->cb = cb;
    262   dgh->cb_cls = cb_cls;
    263   dgh->url = TALER_url_join (backend_url,
    264                              "private/donau",
    265                              NULL);
    266   if (NULL == dgh->url)
    267   {
    268     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    269                 "Could not construct request URL.\n");
    270     GNUNET_free (dgh);
    271     return NULL;
    272   }
    273   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    274               "Requesting URL '%s'\n",
    275               dgh->url);
    276   eh = TALER_MERCHANT_curl_easy_get_ (dgh->url);
    277   dgh->job = GNUNET_CURL_job_add (ctx,
    278                                   eh,
    279                                   &handle_get_donau_instances_finished,
    280                                   dgh);
    281 
    282   return dgh;
    283 }
    284 
    285 
    286 /**
    287  * Cancel the GET /donau instances operation.
    288  *
    289  * @param dgh request to cancel.
    290  */
    291 void
    292 TALER_MERCHANT_donau_instances_get_cancel (
    293   struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
    294 {
    295   if (NULL != dgh->job)
    296     GNUNET_CURL_job_cancel (dgh->job);
    297   GNUNET_free (dgh->url);
    298   GNUNET_free (dgh);
    299 }