merchant

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

merchant_api_patch-private-templates-TEMPLATE_ID.c (9345B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022-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_patch-private-templates-TEMPLATE_ID-new.c
     21  * @brief Implementation of the PATCH /private/templates/$TEMPLATE_ID 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/patch-private-templates-TEMPLATE_ID.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 PATCH /private/templates/$TEMPLATE_ID operation.
     39  */
     40 struct TALER_MERCHANT_PatchPrivateTemplateHandle
     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_PatchPrivateTemplateCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_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    * Identifier of the template to update.
     79    */
     80   char *template_id;
     81 
     82   /**
     83    * New human-readable description.
     84    */
     85   char *template_description;
     86 
     87   /**
     88    * New OTP device ID, or NULL to remove association.
     89    */
     90   char *otp_id;
     91 
     92   /**
     93    * New template contract (JSON).
     94    */
     95   json_t *template_contract;
     96 
     97   /**
     98    * Optional editable defaults (JSON object).
     99    */
    100   json_t *editable_defaults;
    101 };
    102 
    103 
    104 /**
    105  * Function called when we're done processing the
    106  * HTTP PATCH /private/templates/$TEMPLATE_ID request.
    107  *
    108  * @param cls the `struct TALER_MERCHANT_PatchPrivateTemplateHandle`
    109  * @param response_code HTTP response code, 0 on error
    110  * @param response response body, NULL if not in JSON
    111  */
    112 static void
    113 handle_patch_template_finished (void *cls,
    114                                 long response_code,
    115                                 const void *response)
    116 {
    117   struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph = cls;
    118   const json_t *json = response;
    119   struct TALER_MERCHANT_PatchPrivateTemplateResponse tpr = {
    120     .hr.http_status = (unsigned int) response_code,
    121     .hr.reply = json
    122   };
    123 
    124   tph->job = NULL;
    125   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    126               "PATCH /private/templates/$TEMPLATE_ID completed with response code %u\n",
    127               (unsigned int) response_code);
    128   switch (response_code)
    129   {
    130   case 0:
    131     tpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    132     break;
    133   case MHD_HTTP_NO_CONTENT:
    134     break;
    135   case MHD_HTTP_BAD_REQUEST:
    136     tpr.hr.ec = TALER_JSON_get_error_code (json);
    137     tpr.hr.hint = TALER_JSON_get_error_hint (json);
    138     GNUNET_break_op (0);
    139     /* This should never happen, either us
    140      * or the merchant is buggy (or API version conflict);
    141      * just pass JSON reply to the application */
    142     break;
    143   case MHD_HTTP_UNAUTHORIZED:
    144     tpr.hr.ec = TALER_JSON_get_error_code (json);
    145     tpr.hr.hint = TALER_JSON_get_error_hint (json);
    146     /* Nothing really to verify, merchant says we need to authenticate. */
    147     break;
    148   case MHD_HTTP_FORBIDDEN:
    149     tpr.hr.ec = TALER_JSON_get_error_code (json);
    150     tpr.hr.hint = TALER_JSON_get_error_hint (json);
    151     break;
    152   case MHD_HTTP_NOT_FOUND:
    153     tpr.hr.ec = TALER_JSON_get_error_code (json);
    154     tpr.hr.hint = TALER_JSON_get_error_hint (json);
    155     break;
    156   case MHD_HTTP_CONFLICT:
    157     tpr.hr.ec = TALER_JSON_get_error_code (json);
    158     tpr.hr.hint = TALER_JSON_get_error_hint (json);
    159     break;
    160   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    161     tpr.hr.ec = TALER_JSON_get_error_code (json);
    162     tpr.hr.hint = TALER_JSON_get_error_hint (json);
    163     /* Server had an internal issue; we should retry,
    164        but this API leaves this to the application */
    165     break;
    166   default:
    167     TALER_MERCHANT_parse_error_details_ (json,
    168                                          response_code,
    169                                          &tpr.hr);
    170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    171                 "Unexpected response code %u/%d\n",
    172                 (unsigned int) response_code,
    173                 (int) tpr.hr.ec);
    174     GNUNET_break_op (0);
    175     break;
    176   }
    177   tph->cb (tph->cb_cls,
    178            &tpr);
    179   TALER_MERCHANT_patch_private_template_cancel (tph);
    180 }
    181 
    182 
    183 struct TALER_MERCHANT_PatchPrivateTemplateHandle *
    184 TALER_MERCHANT_patch_private_template_create (
    185   struct GNUNET_CURL_Context *ctx,
    186   const char *url,
    187   const char *template_id,
    188   const char *template_description,
    189   const char *otp_id,
    190   json_t *template_contract)
    191 {
    192   struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph;
    193 
    194   tph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateTemplateHandle);
    195   tph->ctx = ctx;
    196   tph->base_url = GNUNET_strdup (url);
    197   tph->template_id = GNUNET_strdup (template_id);
    198   tph->template_description = GNUNET_strdup (template_description);
    199   if (NULL != otp_id)
    200     tph->otp_id = GNUNET_strdup (otp_id);
    201   tph->template_contract = json_incref (template_contract);
    202   return tph;
    203 }
    204 
    205 
    206 enum GNUNET_GenericReturnValue
    207 TALER_MERCHANT_patch_private_template_set_options_ (
    208   struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph,
    209   unsigned int num_options,
    210   const struct TALER_MERCHANT_PatchPrivateTemplateOptionValue *options)
    211 {
    212   for (unsigned int i = 0; i < num_options; i++)
    213   {
    214     switch (options[i].option)
    215     {
    216     case TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_OPTION_END:
    217       return GNUNET_OK;
    218     case TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_OPTION_EDITABLE_DEFAULTS:
    219       tph->editable_defaults
    220         = json_incref ((json_t *) options[i].details.editable_defaults);
    221       break;
    222     default:
    223       GNUNET_break (0);
    224       return GNUNET_SYSERR;
    225     }
    226   }
    227   return GNUNET_OK;
    228 }
    229 
    230 
    231 enum TALER_ErrorCode
    232 TALER_MERCHANT_patch_private_template_start (
    233   struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph,
    234   TALER_MERCHANT_PatchPrivateTemplateCallback cb,
    235   TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls)
    236 {
    237   json_t *req_obj;
    238   CURL *eh;
    239 
    240   tph->cb = cb;
    241   tph->cb_cls = cb_cls;
    242   {
    243     char *path;
    244 
    245     GNUNET_asprintf (&path,
    246                      "private/templates/%s",
    247                      tph->template_id);
    248     tph->url = TALER_url_join (tph->base_url,
    249                                path,
    250                                NULL);
    251     GNUNET_free (path);
    252   }
    253   if (NULL == tph->url)
    254     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    255   req_obj = GNUNET_JSON_PACK (
    256     GNUNET_JSON_pack_string ("template_description",
    257                              tph->template_description),
    258     GNUNET_JSON_pack_allow_null (
    259       GNUNET_JSON_pack_string ("otp_id",
    260                                tph->otp_id)),
    261     GNUNET_JSON_pack_object_incref ("template_contract",
    262                                     (json_t *) tph->template_contract),
    263     GNUNET_JSON_pack_allow_null (
    264       GNUNET_JSON_pack_object_incref ("editable_defaults",
    265                                       tph->editable_defaults)));
    266   eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
    267   if ( (NULL == eh) ||
    268        (GNUNET_OK !=
    269         TALER_curl_easy_post (&tph->post_ctx,
    270                               eh,
    271                               req_obj)) )
    272   {
    273     GNUNET_break (0);
    274     json_decref (req_obj);
    275     if (NULL != eh)
    276       curl_easy_cleanup (eh);
    277     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    278   }
    279   json_decref (req_obj);
    280   GNUNET_assert (CURLE_OK ==
    281                  curl_easy_setopt (eh,
    282                                    CURLOPT_CUSTOMREQUEST,
    283                                    MHD_HTTP_METHOD_PATCH));
    284   tph->job = GNUNET_CURL_job_add2 (tph->ctx,
    285                                    eh,
    286                                    tph->post_ctx.headers,
    287                                    &handle_patch_template_finished,
    288                                    tph);
    289   if (NULL == tph->job)
    290     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    291   return TALER_EC_NONE;
    292 }
    293 
    294 
    295 void
    296 TALER_MERCHANT_patch_private_template_cancel (
    297   struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph)
    298 {
    299   if (NULL != tph->job)
    300   {
    301     GNUNET_CURL_job_cancel (tph->job);
    302     tph->job = NULL;
    303   }
    304   TALER_curl_easy_post_finished (&tph->post_ctx);
    305   json_decref (tph->template_contract);
    306   GNUNET_free (tph->url);
    307   GNUNET_free (tph->base_url);
    308   GNUNET_free (tph->template_id);
    309   GNUNET_free (tph->template_description);
    310   GNUNET_free (tph->otp_id);
    311   json_decref (tph->editable_defaults);
    312   GNUNET_free (tph);
    313 }
    314 
    315 
    316 /* end of merchant_api_patch-private-templates-TEMPLATE_ID-new.c */