merchant_api_post-templates-TEMPLATE_ID.c (9126B)
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-templates-TEMPLATE_ID-new.c 21 * @brief Implementation of the POST /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/post-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 POST /templates/$TEMPLATE_ID operation. 39 */ 40 struct TALER_MERCHANT_PostTemplatesHandle 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_PostTemplatesCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_TEMPLATES_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 * Template identifier. 79 */ 80 char *template_id; 81 82 /** 83 * Optional summary string. 84 */ 85 const char *summary; 86 87 /** 88 * Optional amount. 89 */ 90 const struct TALER_Amount *amount; 91 92 /** 93 * Optional detailed contract customization (JSON). 94 */ 95 const json_t *details; 96 }; 97 98 99 /** 100 * Function called when we're done processing the 101 * HTTP POST /templates/$TEMPLATE_ID request. 102 * 103 * @param cls the `struct TALER_MERCHANT_PostTemplatesHandle` 104 * @param response_code HTTP response code, 0 on error 105 * @param response response body, NULL if not in JSON 106 */ 107 static void 108 handle_post_templates_finished (void *cls, 109 long response_code, 110 const void *response) 111 { 112 struct TALER_MERCHANT_PostTemplatesHandle *pth = cls; 113 const json_t *json = response; 114 struct TALER_MERCHANT_PostTemplatesResponse ptr = { 115 .hr.http_status = (unsigned int) response_code, 116 .hr.reply = json 117 }; 118 struct TALER_ClaimTokenP token; 119 120 pth->job = NULL; 121 switch (response_code) 122 { 123 case 0: 124 ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 125 break; 126 case MHD_HTTP_OK: 127 { 128 bool no_token; 129 bool no_pay_deadline; 130 struct GNUNET_JSON_Specification spec[] = { 131 GNUNET_JSON_spec_string ("order_id", 132 &ptr.details.ok.order_id), 133 GNUNET_JSON_spec_mark_optional ( 134 GNUNET_JSON_spec_fixed_auto ("token", 135 &token), 136 &no_token), 137 GNUNET_JSON_spec_mark_optional ( 138 GNUNET_JSON_spec_timestamp ("pay_deadline", 139 &ptr.details.ok.pay_deadline), 140 &no_pay_deadline), 141 GNUNET_JSON_spec_end () 142 }; 143 144 if (GNUNET_OK != 145 GNUNET_JSON_parse (json, 146 spec, 147 NULL, NULL)) 148 { 149 GNUNET_break_op (0); 150 ptr.hr.http_status = 0; 151 ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 152 break; 153 } 154 if (! no_token) 155 ptr.details.ok.token = &token; 156 if (no_pay_deadline) 157 ptr.details.ok.pay_deadline = GNUNET_TIME_UNIT_ZERO_TS; 158 break; 159 } 160 case MHD_HTTP_BAD_REQUEST: 161 ptr.hr.ec = TALER_JSON_get_error_code (json); 162 ptr.hr.hint = TALER_JSON_get_error_hint (json); 163 break; 164 case MHD_HTTP_UNAUTHORIZED: 165 ptr.hr.ec = TALER_JSON_get_error_code (json); 166 ptr.hr.hint = TALER_JSON_get_error_hint (json); 167 break; 168 case MHD_HTTP_FORBIDDEN: 169 ptr.hr.ec = TALER_JSON_get_error_code (json); 170 ptr.hr.hint = TALER_JSON_get_error_hint (json); 171 break; 172 case MHD_HTTP_NOT_FOUND: 173 ptr.hr.ec = TALER_JSON_get_error_code (json); 174 ptr.hr.hint = TALER_JSON_get_error_hint (json); 175 break; 176 case MHD_HTTP_CONFLICT: 177 ptr.hr.ec = TALER_JSON_get_error_code (json); 178 ptr.hr.hint = TALER_JSON_get_error_hint (json); 179 break; 180 case MHD_HTTP_INTERNAL_SERVER_ERROR: 181 ptr.hr.ec = TALER_JSON_get_error_code (json); 182 ptr.hr.hint = TALER_JSON_get_error_hint (json); 183 break; 184 default: 185 ptr.hr.ec = TALER_JSON_get_error_code (json); 186 ptr.hr.hint = TALER_JSON_get_error_hint (json); 187 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 188 "Unexpected response code %u/%d\n", 189 (unsigned int) response_code, 190 (int) ptr.hr.ec); 191 GNUNET_break_op (0); 192 break; 193 } 194 pth->cb (pth->cb_cls, 195 &ptr); 196 TALER_MERCHANT_post_templates_cancel (pth); 197 } 198 199 200 struct TALER_MERCHANT_PostTemplatesHandle * 201 TALER_MERCHANT_post_templates_create ( 202 struct GNUNET_CURL_Context *ctx, 203 const char *url, 204 const char *template_id) 205 { 206 struct TALER_MERCHANT_PostTemplatesHandle *pth; 207 208 pth = GNUNET_new (struct TALER_MERCHANT_PostTemplatesHandle); 209 pth->ctx = ctx; 210 pth->base_url = GNUNET_strdup (url); 211 pth->template_id = GNUNET_strdup (template_id); 212 return pth; 213 } 214 215 216 enum GNUNET_GenericReturnValue 217 TALER_MERCHANT_post_templates_set_options_ ( 218 struct TALER_MERCHANT_PostTemplatesHandle *pth, 219 unsigned int num_options, 220 const struct TALER_MERCHANT_PostTemplatesOptionValue *options) 221 { 222 for (unsigned int i = 0; i < num_options; i++) 223 { 224 switch (options[i].option) 225 { 226 case TALER_MERCHANT_POST_TEMPLATES_OPTION_END: 227 return GNUNET_OK; 228 case TALER_MERCHANT_POST_TEMPLATES_OPTION_SUMMARY: 229 pth->summary = options[i].details.summary; 230 break; 231 case TALER_MERCHANT_POST_TEMPLATES_OPTION_AMOUNT: 232 pth->amount = options[i].details.amount; 233 break; 234 case TALER_MERCHANT_POST_TEMPLATES_OPTION_DETAILS: 235 pth->details = options[i].details.details; 236 break; 237 default: 238 GNUNET_break (0); 239 return GNUNET_SYSERR; 240 } 241 } 242 return GNUNET_OK; 243 } 244 245 246 enum TALER_ErrorCode 247 TALER_MERCHANT_post_templates_start ( 248 struct TALER_MERCHANT_PostTemplatesHandle *pth, 249 TALER_MERCHANT_PostTemplatesCallback cb, 250 TALER_MERCHANT_POST_TEMPLATES_RESULT_CLOSURE *cb_cls) 251 { 252 json_t *req_obj; 253 CURL *eh; 254 255 pth->cb = cb; 256 pth->cb_cls = cb_cls; 257 { 258 char *path; 259 260 GNUNET_asprintf (&path, 261 "templates/%s", 262 pth->template_id); 263 pth->url = TALER_url_join (pth->base_url, 264 path, 265 NULL); 266 GNUNET_free (path); 267 } 268 if (NULL == pth->url) 269 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 270 if (NULL != pth->details) 271 { 272 /* If detailed contract customization is provided, send it directly. */ 273 req_obj = json_incref ((json_t *) pth->details); 274 } 275 else 276 { 277 /* Build fixed-order request with optional summary and amount. */ 278 req_obj = GNUNET_JSON_PACK ( 279 GNUNET_JSON_pack_string ("template_type", 280 "fixed-order"), 281 GNUNET_JSON_pack_allow_null ( 282 GNUNET_JSON_pack_string ("summary", 283 pth->summary)), 284 GNUNET_JSON_pack_allow_null ( 285 TALER_JSON_pack_amount ("amount", 286 pth->amount))); 287 } 288 eh = TALER_MERCHANT_curl_easy_get_ (pth->url); 289 if ( (NULL == eh) || 290 (GNUNET_OK != 291 TALER_curl_easy_post (&pth->post_ctx, 292 eh, 293 req_obj)) ) 294 { 295 GNUNET_break (0); 296 json_decref (req_obj); 297 if (NULL != eh) 298 curl_easy_cleanup (eh); 299 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 300 } 301 json_decref (req_obj); 302 pth->job = GNUNET_CURL_job_add2 (pth->ctx, 303 eh, 304 pth->post_ctx.headers, 305 &handle_post_templates_finished, 306 pth); 307 if (NULL == pth->job) 308 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 309 return TALER_EC_NONE; 310 } 311 312 313 void 314 TALER_MERCHANT_post_templates_cancel ( 315 struct TALER_MERCHANT_PostTemplatesHandle *pth) 316 { 317 if (NULL != pth->job) 318 { 319 GNUNET_CURL_job_cancel (pth->job); 320 pth->job = NULL; 321 } 322 TALER_curl_easy_post_finished (&pth->post_ctx); 323 GNUNET_free (pth->template_id); 324 GNUNET_free (pth->url); 325 GNUNET_free (pth->base_url); 326 GNUNET_free (pth); 327 } 328 329 330 /* end of merchant_api_post-templates-TEMPLATE_ID-new.c */