merchant

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

merchant_api_patch-private-webhooks-WEBHOOK_ID.c (9499B)


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