merchant_api_post-private-webhooks.c (8771B)
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_post-private-webhooks-new.c 21 * @brief Implementation of the POST /private/webhooks 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-webhooks.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/webhooks operation. 39 */ 40 struct TALER_MERCHANT_PostPrivateWebhooksHandle 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_PostPrivateWebhooksCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_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 * Webhook identifier. 79 */ 80 char *webhook_id; 81 82 /** 83 * Event type that triggers this webhook. 84 */ 85 char *event_type; 86 87 /** 88 * URL to send notifications to. 89 */ 90 char *notification_url; 91 92 /** 93 * HTTP method to use for notifications. 94 */ 95 char *http_method; 96 97 /** 98 * Optional template for HTTP request headers. 99 */ 100 const char *header_template; 101 102 /** 103 * Optional template for the HTTP request body. 104 */ 105 const char *body_template; 106 }; 107 108 109 /** 110 * Function called when we're done processing the 111 * HTTP POST /private/webhooks request. 112 * 113 * @param cls the `struct TALER_MERCHANT_PostPrivateWebhooksHandle` 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_post_webhooks_finished (void *cls, 119 long response_code, 120 const void *response) 121 { 122 struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh = cls; 123 const json_t *json = response; 124 struct TALER_MERCHANT_PostPrivateWebhooksResponse wpr = { 125 .hr.http_status = (unsigned int) response_code, 126 .hr.reply = json 127 }; 128 129 ppwh->job = NULL; 130 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 131 "POST /private/webhooks 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 break; 144 case MHD_HTTP_UNAUTHORIZED: 145 wpr.hr.ec = TALER_JSON_get_error_code (json); 146 wpr.hr.hint = TALER_JSON_get_error_hint (json); 147 break; 148 case MHD_HTTP_FORBIDDEN: 149 wpr.hr.ec = TALER_JSON_get_error_code (json); 150 wpr.hr.hint = TALER_JSON_get_error_hint (json); 151 break; 152 case MHD_HTTP_NOT_FOUND: 153 wpr.hr.ec = TALER_JSON_get_error_code (json); 154 wpr.hr.hint = TALER_JSON_get_error_hint (json); 155 break; 156 case MHD_HTTP_CONFLICT: 157 wpr.hr.ec = TALER_JSON_get_error_code (json); 158 wpr.hr.hint = TALER_JSON_get_error_hint (json); 159 break; 160 case MHD_HTTP_INTERNAL_SERVER_ERROR: 161 wpr.hr.ec = TALER_JSON_get_error_code (json); 162 wpr.hr.hint = TALER_JSON_get_error_hint (json); 163 break; 164 default: 165 TALER_MERCHANT_parse_error_details_ (json, 166 response_code, 167 &wpr.hr); 168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 169 "Unexpected response code %u/%d\n", 170 (unsigned int) response_code, 171 (int) wpr.hr.ec); 172 GNUNET_break_op (0); 173 break; 174 } 175 ppwh->cb (ppwh->cb_cls, 176 &wpr); 177 TALER_MERCHANT_post_private_webhooks_cancel (ppwh); 178 } 179 180 181 struct TALER_MERCHANT_PostPrivateWebhooksHandle * 182 TALER_MERCHANT_post_private_webhooks_create ( 183 struct GNUNET_CURL_Context *ctx, 184 const char *url, 185 const char *webhook_id, 186 const char *event_type, 187 const char *notification_url, 188 const char *http_method) 189 { 190 struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh; 191 192 ppwh = GNUNET_new (struct TALER_MERCHANT_PostPrivateWebhooksHandle); 193 ppwh->ctx = ctx; 194 ppwh->base_url = GNUNET_strdup (url); 195 ppwh->webhook_id = GNUNET_strdup (webhook_id); 196 ppwh->event_type = GNUNET_strdup (event_type); 197 ppwh->notification_url = GNUNET_strdup (notification_url); 198 ppwh->http_method = GNUNET_strdup (http_method); 199 return ppwh; 200 } 201 202 203 enum GNUNET_GenericReturnValue 204 TALER_MERCHANT_post_private_webhooks_set_options_ ( 205 struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh, 206 unsigned int num_options, 207 const struct TALER_MERCHANT_PostPrivateWebhooksOptionValue *options) 208 { 209 for (unsigned int i = 0; i < num_options; i++) 210 { 211 switch (options[i].option) 212 { 213 case TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_OPTION_END: 214 return GNUNET_OK; 215 case TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_OPTION_HEADER_TEMPLATE: 216 ppwh->header_template = options[i].details.header_template; 217 break; 218 case TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_OPTION_BODY_TEMPLATE: 219 ppwh->body_template = options[i].details.body_template; 220 break; 221 default: 222 GNUNET_break (0); 223 return GNUNET_SYSERR; 224 } 225 } 226 return GNUNET_OK; 227 } 228 229 230 enum TALER_ErrorCode 231 TALER_MERCHANT_post_private_webhooks_start ( 232 struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh, 233 TALER_MERCHANT_PostPrivateWebhooksCallback cb, 234 TALER_MERCHANT_POST_PRIVATE_WEBHOOKS_RESULT_CLOSURE *cb_cls) 235 { 236 json_t *req_obj; 237 CURL *eh; 238 239 ppwh->cb = cb; 240 ppwh->cb_cls = cb_cls; 241 ppwh->url = TALER_url_join (ppwh->base_url, 242 "private/webhooks", 243 NULL); 244 if (NULL == ppwh->url) 245 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 246 req_obj = GNUNET_JSON_PACK ( 247 GNUNET_JSON_pack_string ("webhook_id", 248 ppwh->webhook_id), 249 GNUNET_JSON_pack_string ("event_type", 250 ppwh->event_type), 251 GNUNET_JSON_pack_string ("url", 252 ppwh->notification_url), 253 GNUNET_JSON_pack_string ("http_method", 254 ppwh->http_method), 255 GNUNET_JSON_pack_allow_null ( 256 GNUNET_JSON_pack_string ("header_template", 257 ppwh->header_template)), 258 GNUNET_JSON_pack_allow_null ( 259 GNUNET_JSON_pack_string ("body_template", 260 ppwh->body_template))); 261 eh = TALER_MERCHANT_curl_easy_get_ (ppwh->url); 262 if ( (NULL == eh) || 263 (GNUNET_OK != 264 TALER_curl_easy_post (&ppwh->post_ctx, 265 eh, 266 req_obj)) ) 267 { 268 GNUNET_break (0); 269 json_decref (req_obj); 270 if (NULL != eh) 271 curl_easy_cleanup (eh); 272 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 273 } 274 json_decref (req_obj); 275 ppwh->job = GNUNET_CURL_job_add2 (ppwh->ctx, 276 eh, 277 ppwh->post_ctx.headers, 278 &handle_post_webhooks_finished, 279 ppwh); 280 if (NULL == ppwh->job) 281 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 282 return TALER_EC_NONE; 283 } 284 285 286 void 287 TALER_MERCHANT_post_private_webhooks_cancel ( 288 struct TALER_MERCHANT_PostPrivateWebhooksHandle *ppwh) 289 { 290 if (NULL != ppwh->job) 291 { 292 GNUNET_CURL_job_cancel (ppwh->job); 293 ppwh->job = NULL; 294 } 295 TALER_curl_easy_post_finished (&ppwh->post_ctx); 296 GNUNET_free (ppwh->webhook_id); 297 GNUNET_free (ppwh->event_type); 298 GNUNET_free (ppwh->notification_url); 299 GNUNET_free (ppwh->http_method); 300 GNUNET_free (ppwh->url); 301 GNUNET_free (ppwh->base_url); 302 GNUNET_free (ppwh); 303 } 304 305 306 /* end of merchant_api_post-private-webhooks-new.c */