merchant

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

merchant_api_post-management-instances.c (14732B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-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-management-instances-new.c
     21  * @brief Implementation of the POST /management/instances 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-management-instances.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 /management/instances operation.
     39  */
     40 struct TALER_MERCHANT_PostManagementInstancesHandle
     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_PostManagementInstancesCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_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    * Instance identifier.
     79    */
     80   char *instance_id;
     81 
     82   /**
     83    * Human-readable name.
     84    */
     85   char *name;
     86 
     87   /**
     88    * Address (JSON).
     89    */
     90   json_t *address;
     91 
     92   /**
     93    * Jurisdiction (JSON).
     94    */
     95   json_t *jurisdiction;
     96 
     97   /**
     98    * Whether to use the STEFAN curve.
     99    */
    100   bool use_stefan;
    101 
    102   /**
    103    * Default wire transfer delay (optional, zero if not set).
    104    */
    105   struct GNUNET_TIME_Relative default_wire_transfer_delay;
    106 
    107   /**
    108    * Default payment deadline (optional, zero if not set).
    109    */
    110   struct GNUNET_TIME_Relative default_pay_delay;
    111 
    112   /**
    113    * Default refund deadline (optional, zero if not set).
    114    */
    115   struct GNUNET_TIME_Relative default_refund_delay;
    116 
    117   /**
    118    * Default wire transfer rounding interval (optional, zero if not set).
    119    */
    120   enum GNUNET_TIME_RounderInterval default_wire_transfer_rounding_interval;
    121 
    122   /**
    123    * Authentication password (or NULL).
    124    */
    125   char *auth_password;
    126 
    127   /**
    128    * Optional email address.
    129    */
    130   char *email;
    131 
    132   /**
    133    * Optional phone number.
    134    */
    135   char *phone_number;
    136 
    137   /**
    138    * Optional website URL.
    139    */
    140   char *website;
    141 
    142   /**
    143    * Optional logo.
    144    */
    145   char *logo;
    146 
    147   /**
    148    * True if default_pay_delay was explicitly set via options.
    149    */
    150   bool have_default_pay_delay;
    151 
    152   /**
    153    * True if default_refund_delay was explicitly set via options.
    154    */
    155   bool have_default_refund_delay;
    156 
    157   /**
    158    * True if default_wire_transfer_delay was explicitly set via options.
    159    */
    160   bool have_default_wire_transfer_delay;
    161 
    162   /**
    163    * True if default_wire_transfer_rounding_interval was explicitly set via options.
    164    */
    165   bool have_default_wire_transfer_rounding_interval;
    166 };
    167 
    168 
    169 /**
    170  * Function called when we're done processing the
    171  * HTTP POST /management/instances request.
    172  *
    173  * @param cls the `struct TALER_MERCHANT_PostManagementInstancesHandle`
    174  * @param response_code HTTP response code, 0 on error
    175  * @param response response body, NULL if not in JSON
    176  */
    177 static void
    178 handle_post_management_instances_finished (void *cls,
    179                                            long response_code,
    180                                            const void *response)
    181 {
    182   struct TALER_MERCHANT_PostManagementInstancesHandle *pmih = cls;
    183   const json_t *json = response;
    184   struct TALER_MERCHANT_PostManagementInstancesResponse mir = {
    185     .hr.http_status = (unsigned int) response_code,
    186     .hr.reply = json
    187   };
    188 
    189   pmih->job = NULL;
    190   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    191               "POST /management/instances completed with response code %u\n",
    192               (unsigned int) response_code);
    193   switch (response_code)
    194   {
    195   case 0:
    196     mir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    197     break;
    198   case MHD_HTTP_NO_CONTENT:
    199     break;
    200   case MHD_HTTP_ACCEPTED:
    201     if (GNUNET_OK !=
    202         TALER_MERCHANT_parse_mfa_challenge_response_ (
    203           json,
    204           &mir.details.accepted))
    205     {
    206       GNUNET_break_op (0);
    207       mir.hr.http_status = 0;
    208       mir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    209     }
    210     break;
    211   case MHD_HTTP_BAD_REQUEST:
    212     mir.hr.ec = TALER_JSON_get_error_code (json);
    213     mir.hr.hint = TALER_JSON_get_error_hint (json);
    214     break;
    215   case MHD_HTTP_UNAUTHORIZED:
    216     mir.hr.ec = TALER_JSON_get_error_code (json);
    217     mir.hr.hint = TALER_JSON_get_error_hint (json);
    218     break;
    219   case MHD_HTTP_FORBIDDEN:
    220     mir.hr.ec = TALER_JSON_get_error_code (json);
    221     mir.hr.hint = TALER_JSON_get_error_hint (json);
    222     break;
    223   case MHD_HTTP_NOT_FOUND:
    224     mir.hr.ec = TALER_JSON_get_error_code (json);
    225     mir.hr.hint = TALER_JSON_get_error_hint (json);
    226     break;
    227   case MHD_HTTP_CONFLICT:
    228     mir.hr.ec = TALER_JSON_get_error_code (json);
    229     mir.hr.hint = TALER_JSON_get_error_hint (json);
    230     break;
    231   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    232     mir.hr.ec = TALER_JSON_get_error_code (json);
    233     mir.hr.hint = TALER_JSON_get_error_hint (json);
    234     break;
    235   default:
    236     TALER_MERCHANT_parse_error_details_ (json,
    237                                          response_code,
    238                                          &mir.hr);
    239     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    240                 "Unexpected response code %u/%d\n",
    241                 (unsigned int) response_code,
    242                 (int) mir.hr.ec);
    243     GNUNET_break_op (0);
    244     break;
    245   }
    246   pmih->cb (pmih->cb_cls,
    247             &mir);
    248   if (MHD_HTTP_ACCEPTED == response_code)
    249     TALER_MERCHANT_mfa_challenge_response_free (
    250       &mir.details.accepted);
    251   TALER_MERCHANT_post_management_instances_cancel (pmih);
    252 }
    253 
    254 
    255 struct TALER_MERCHANT_PostManagementInstancesHandle *
    256 TALER_MERCHANT_post_management_instances_create (
    257   struct GNUNET_CURL_Context *ctx,
    258   const char *url,
    259   const char *instance_id,
    260   const char *name,
    261   const json_t *address,
    262   const json_t *jurisdiction,
    263   bool use_stefan)
    264 {
    265   struct TALER_MERCHANT_PostManagementInstancesHandle *pmih;
    266 
    267   pmih = GNUNET_new (struct TALER_MERCHANT_PostManagementInstancesHandle);
    268   pmih->ctx = ctx;
    269   pmih->base_url = GNUNET_strdup (url);
    270   pmih->instance_id = GNUNET_strdup (instance_id);
    271   pmih->name = GNUNET_strdup (name);
    272   pmih->address = json_incref ((json_t *) address);
    273   pmih->jurisdiction = json_incref ((json_t *) jurisdiction);
    274   pmih->use_stefan = use_stefan;
    275   return pmih;
    276 }
    277 
    278 
    279 enum GNUNET_GenericReturnValue
    280 TALER_MERCHANT_post_management_instances_set_options_ (
    281   struct TALER_MERCHANT_PostManagementInstancesHandle *pmih,
    282   unsigned int num_options,
    283   const struct TALER_MERCHANT_PostManagementInstancesOptionValue *options)
    284 {
    285   for (unsigned int i = 0; i < num_options; i++)
    286   {
    287     switch (options[i].option)
    288     {
    289     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_END:
    290       return GNUNET_OK;
    291     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_PAY_DELAY:
    292       pmih->default_pay_delay = options[i].details.default_pay_delay;
    293       pmih->have_default_pay_delay = true;
    294       break;
    295     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_REFUND_DELAY:
    296       pmih->default_refund_delay = options[i].details.default_refund_delay;
    297       pmih->have_default_refund_delay = true;
    298       break;
    299     case
    300       TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_WIRE_TRANSFER_DELAY
    301       :
    302       pmih->default_wire_transfer_delay
    303         = options[i].details.default_wire_transfer_delay;
    304       pmih->have_default_wire_transfer_delay = true;
    305       break;
    306     case
    307       TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL
    308       :
    309       pmih->default_wire_transfer_rounding_interval
    310         = options[i].details.default_wire_transfer_rounding_interval;
    311       pmih->have_default_wire_transfer_rounding_interval = true;
    312       break;
    313     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_EMAIL:
    314       GNUNET_free (pmih->email);
    315       if (NULL != options[i].details.email)
    316         pmih->email = GNUNET_strdup (options[i].details.email);
    317       break;
    318     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_PHONE_NUMBER:
    319       GNUNET_free (pmih->phone_number);
    320       if (NULL != options[i].details.phone_number)
    321         pmih->phone_number = GNUNET_strdup (options[i].details.phone_number);
    322       break;
    323     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_WEBSITE:
    324       GNUNET_free (pmih->website);
    325       if (NULL != options[i].details.website)
    326         pmih->website = GNUNET_strdup (options[i].details.website);
    327       break;
    328     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_LOGO:
    329       GNUNET_free (pmih->logo);
    330       if (NULL != options[i].details.logo)
    331         pmih->logo = GNUNET_strdup (options[i].details.logo);
    332       break;
    333     case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_AUTH_PASSWORD:
    334       GNUNET_free (pmih->auth_password);
    335       if (NULL != options[i].details.auth_password)
    336         pmih->auth_password = GNUNET_strdup (
    337           options[i].details.auth_password);
    338       break;
    339     default:
    340       GNUNET_break (0);
    341       return GNUNET_SYSERR;
    342     }
    343   }
    344   return GNUNET_OK;
    345 }
    346 
    347 
    348 enum TALER_ErrorCode
    349 TALER_MERCHANT_post_management_instances_start (
    350   struct TALER_MERCHANT_PostManagementInstancesHandle *pmih,
    351   TALER_MERCHANT_PostManagementInstancesCallback cb,
    352   TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_RESULT_CLOSURE *cb_cls)
    353 {
    354   json_t *req_obj;
    355   json_t *auth_obj;
    356   CURL *eh;
    357 
    358   pmih->cb = cb;
    359   pmih->cb_cls = cb_cls;
    360   pmih->url = TALER_url_join (pmih->base_url,
    361                               "management/instances",
    362                               NULL);
    363   if (NULL == pmih->url)
    364     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    365   if (NULL != pmih->auth_password)
    366   {
    367     auth_obj = GNUNET_JSON_PACK (
    368       GNUNET_JSON_pack_string ("method",
    369                                "token"),
    370       GNUNET_JSON_pack_string ("password",
    371                                pmih->auth_password));
    372   }
    373   else
    374   {
    375     auth_obj = GNUNET_JSON_PACK (
    376       GNUNET_JSON_pack_string ("method",
    377                                "external"));
    378   }
    379   if (NULL == auth_obj)
    380   {
    381     GNUNET_break (0);
    382     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    383   }
    384   req_obj = GNUNET_JSON_PACK (
    385     GNUNET_JSON_pack_string ("id",
    386                              pmih->instance_id),
    387     GNUNET_JSON_pack_string ("name",
    388                              pmih->name),
    389     GNUNET_JSON_pack_object_incref ("address",
    390                                     pmih->address),
    391     GNUNET_JSON_pack_object_incref ("jurisdiction",
    392                                     pmih->jurisdiction),
    393     GNUNET_JSON_pack_bool ("use_stefan",
    394                            pmih->use_stefan),
    395     GNUNET_JSON_pack_allow_null (
    396       pmih->have_default_wire_transfer_delay
    397       ? GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay",
    398                                    pmih->default_wire_transfer_delay)
    399       : GNUNET_JSON_pack_string ("default_wire_transfer_delay",
    400                                  NULL)),
    401     GNUNET_JSON_pack_allow_null (
    402       pmih->have_default_pay_delay
    403       ? GNUNET_JSON_pack_time_rel ("default_pay_delay",
    404                                    pmih->default_pay_delay)
    405       : GNUNET_JSON_pack_string ("default_pay_delay",
    406                                  NULL)),
    407     GNUNET_JSON_pack_allow_null (
    408       pmih->have_default_refund_delay
    409       ? GNUNET_JSON_pack_time_rel ("default_refund_delay",
    410                                    pmih->default_refund_delay)
    411       : GNUNET_JSON_pack_string ("default_refund_delay",
    412                                  NULL)),
    413     GNUNET_JSON_pack_allow_null (
    414       pmih->have_default_wire_transfer_rounding_interval
    415       ? GNUNET_JSON_pack_time_rounder_interval (
    416         "default_wire_transfer_rounding_interval",
    417         pmih->default_wire_transfer_rounding_interval)
    418       : GNUNET_JSON_pack_string (
    419         "default_wire_transfer_rounding_interval",
    420         NULL)),
    421     GNUNET_JSON_pack_allow_null (
    422       GNUNET_JSON_pack_string ("email",
    423                                pmih->email)),
    424     GNUNET_JSON_pack_allow_null (
    425       GNUNET_JSON_pack_string ("phone_number",
    426                                pmih->phone_number)),
    427     GNUNET_JSON_pack_allow_null (
    428       GNUNET_JSON_pack_string ("website",
    429                                pmih->website)),
    430     GNUNET_JSON_pack_allow_null (
    431       GNUNET_JSON_pack_string ("logo",
    432                                pmih->logo)),
    433     GNUNET_JSON_pack_object_steal ("auth",
    434                                    auth_obj));
    435   eh = TALER_MERCHANT_curl_easy_get_ (pmih->url);
    436   if ( (NULL == eh) ||
    437        (GNUNET_OK !=
    438         TALER_curl_easy_post (&pmih->post_ctx,
    439                               eh,
    440                               req_obj)) )
    441   {
    442     GNUNET_break (0);
    443     json_decref (req_obj);
    444     if (NULL != eh)
    445       curl_easy_cleanup (eh);
    446     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    447   }
    448   json_decref (req_obj);
    449   pmih->job = GNUNET_CURL_job_add2 (pmih->ctx,
    450                                     eh,
    451                                     pmih->post_ctx.headers,
    452                                     &handle_post_management_instances_finished,
    453                                     pmih);
    454   if (NULL == pmih->job)
    455     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    456   return TALER_EC_NONE;
    457 }
    458 
    459 
    460 void
    461 TALER_MERCHANT_post_management_instances_cancel (
    462   struct TALER_MERCHANT_PostManagementInstancesHandle *pmih)
    463 {
    464   if (NULL != pmih->job)
    465   {
    466     GNUNET_CURL_job_cancel (pmih->job);
    467     pmih->job = NULL;
    468   }
    469   TALER_curl_easy_post_finished (&pmih->post_ctx);
    470   json_decref (pmih->address);
    471   json_decref (pmih->jurisdiction);
    472   GNUNET_free (pmih->instance_id);
    473   GNUNET_free (pmih->name);
    474   GNUNET_free (pmih->auth_password);
    475   GNUNET_free (pmih->email);
    476   GNUNET_free (pmih->phone_number);
    477   GNUNET_free (pmih->website);
    478   GNUNET_free (pmih->logo);
    479   GNUNET_free (pmih->url);
    480   GNUNET_free (pmih->base_url);
    481   GNUNET_free (pmih);
    482 }
    483 
    484 
    485 /* end of merchant_api_post-management-instances-new.c */