merchant_api_get-orders-ORDER_ID.c (11338B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2018-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file merchant_api_get-orders-ORDER_ID-new.c 19 * @brief Implementation of the GET /orders/$ORDER_ID request (wallet-facing) 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include <curl/curl.h> 24 #include <jansson.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include <taler/taler-merchant/get-orders-ORDER_ID.h> 29 #include "merchant_api_curl_defaults.h" 30 #include "merchant_api_common.h" 31 #include <taler/taler_json_lib.h> 32 33 34 /** 35 * Handle for a GET /orders/$ORDER_ID operation (wallet-facing). 36 */ 37 struct TALER_MERCHANT_GetOrdersHandle 38 { 39 /** 40 * Base URL of the merchant backend. 41 */ 42 char *base_url; 43 44 /** 45 * The full URL for this request. 46 */ 47 char *url; 48 49 /** 50 * Handle for the request. 51 */ 52 struct GNUNET_CURL_Job *job; 53 54 /** 55 * Function to call with the result. 56 */ 57 TALER_MERCHANT_GetOrdersCallback cb; 58 59 /** 60 * Closure for @a cb. 61 */ 62 TALER_MERCHANT_GET_ORDERS_RESULT_CLOSURE *cb_cls; 63 64 /** 65 * Reference to the execution context. 66 */ 67 struct GNUNET_CURL_Context *ctx; 68 69 /** 70 * Order ID. 71 */ 72 char *order_id; 73 74 /** 75 * Hash of the contract terms (for authentication). 76 */ 77 struct TALER_PrivateContractHashP h_contract; 78 79 /** 80 * Session ID for repurchase detection, or NULL. 81 */ 82 char *session_id; 83 84 /** 85 * Long polling timeout. 86 */ 87 struct GNUNET_TIME_Relative timeout; 88 89 /** 90 * Minimum refund amount to wait for, or NULL if unset. 91 */ 92 struct TALER_Amount min_refund; 93 94 /** 95 * True if @e min_refund was set. 96 */ 97 bool have_min_refund; 98 99 /** 100 * If true, wait until refund is confirmed obtained. 101 */ 102 bool await_refund_obtained; 103 }; 104 105 106 /** 107 * Function called when we're done processing the 108 * HTTP GET /orders/$ORDER_ID request (wallet-facing). 109 * 110 * @param cls the `struct TALER_MERCHANT_GetOrdersHandle` 111 * @param response_code HTTP response code, 0 on error 112 * @param response response body, NULL if not in JSON 113 */ 114 static void 115 handle_get_order_finished (void *cls, 116 long response_code, 117 const void *response) 118 { 119 struct TALER_MERCHANT_GetOrdersHandle *oph = cls; 120 const json_t *json = response; 121 struct TALER_MERCHANT_GetOrdersResponse owgr = { 122 .hr.http_status = (unsigned int) response_code, 123 .hr.reply = json 124 }; 125 126 oph->job = NULL; 127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 128 "Got /orders/$ORDER_ID response with status code %u\n", 129 (unsigned int) response_code); 130 switch (response_code) 131 { 132 case MHD_HTTP_OK: 133 { 134 struct GNUNET_JSON_Specification spec[] = { 135 GNUNET_JSON_spec_bool ("refunded", 136 &owgr.details.ok.refunded), 137 GNUNET_JSON_spec_bool ("refund_pending", 138 &owgr.details.ok.refund_pending), 139 TALER_JSON_spec_amount_any ("refund_amount", 140 &owgr.details.ok.refund_amount), 141 GNUNET_JSON_spec_mark_optional ( 142 TALER_JSON_spec_amount_any ("refund_taken", 143 &owgr.details.ok.refund_taken), 144 NULL), 145 GNUNET_JSON_spec_end () 146 }; 147 148 if (GNUNET_OK != 149 GNUNET_JSON_parse (json, 150 spec, 151 NULL, NULL)) 152 { 153 owgr.hr.http_status = 0; 154 owgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 155 break; 156 } 157 oph->cb (oph->cb_cls, 158 &owgr); 159 TALER_MERCHANT_get_orders_cancel (oph); 160 return; 161 } 162 case MHD_HTTP_ACCEPTED: 163 { 164 struct GNUNET_JSON_Specification spec[] = { 165 GNUNET_JSON_spec_string ( 166 "public_reorder_url", 167 &owgr.details.accepted.public_reorder_url), 168 GNUNET_JSON_spec_end () 169 }; 170 171 if (GNUNET_OK != 172 GNUNET_JSON_parse (json, 173 spec, 174 NULL, NULL)) 175 { 176 owgr.hr.http_status = 0; 177 owgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 178 break; 179 } 180 oph->cb (oph->cb_cls, 181 &owgr); 182 TALER_MERCHANT_get_orders_cancel (oph); 183 return; 184 } 185 case MHD_HTTP_FOUND: 186 /* Redirect; Location header has the target URL. 187 No JSON body expected. */ 188 break; 189 case MHD_HTTP_PAYMENT_REQUIRED: 190 { 191 struct GNUNET_JSON_Specification spec[] = { 192 GNUNET_JSON_spec_string ( 193 "taler_pay_uri", 194 &owgr.details.payment_required.taler_pay_uri), 195 GNUNET_JSON_spec_mark_optional ( 196 GNUNET_JSON_spec_string ( 197 "already_paid_order_id", 198 &owgr.details.payment_required.already_paid_order_id), 199 NULL), 200 GNUNET_JSON_spec_mark_optional ( 201 GNUNET_JSON_spec_string ( 202 "fulfillment_url", 203 &owgr.details.payment_required.fulfillment_url), 204 NULL), 205 GNUNET_JSON_spec_end () 206 }; 207 208 if (GNUNET_OK != 209 GNUNET_JSON_parse (json, 210 spec, 211 NULL, NULL)) 212 { 213 owgr.hr.http_status = 0; 214 owgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 215 break; 216 } 217 oph->cb (oph->cb_cls, 218 &owgr); 219 TALER_MERCHANT_get_orders_cancel (oph); 220 return; 221 } 222 case MHD_HTTP_FORBIDDEN: 223 owgr.hr.ec = TALER_JSON_get_error_code (json); 224 owgr.hr.hint = TALER_JSON_get_error_hint (json); 225 break; 226 case MHD_HTTP_NOT_FOUND: 227 owgr.hr.ec = TALER_JSON_get_error_code (json); 228 owgr.hr.hint = TALER_JSON_get_error_hint (json); 229 break; 230 case MHD_HTTP_NOT_ACCEPTABLE: 231 owgr.hr.ec = TALER_JSON_get_error_code (json); 232 owgr.hr.hint = TALER_JSON_get_error_hint (json); 233 break; 234 default: 235 TALER_MERCHANT_parse_error_details_ (json, 236 response_code, 237 &owgr.hr); 238 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 239 "Unexpected response code %u/%d\n", 240 (unsigned int) response_code, 241 (int) owgr.hr.ec); 242 break; 243 } 244 oph->cb (oph->cb_cls, 245 &owgr); 246 TALER_MERCHANT_get_orders_cancel (oph); 247 } 248 249 250 struct TALER_MERCHANT_GetOrdersHandle * 251 TALER_MERCHANT_get_orders_create ( 252 struct GNUNET_CURL_Context *ctx, 253 const char *url, 254 const char *order_id, 255 const struct TALER_PrivateContractHashP *h_contract) 256 { 257 struct TALER_MERCHANT_GetOrdersHandle *oph; 258 259 oph = GNUNET_new (struct TALER_MERCHANT_GetOrdersHandle); 260 oph->ctx = ctx; 261 oph->base_url = GNUNET_strdup (url); 262 oph->order_id = GNUNET_strdup (order_id); 263 oph->h_contract = *h_contract; 264 return oph; 265 } 266 267 268 enum GNUNET_GenericReturnValue 269 TALER_MERCHANT_get_orders_set_options_ ( 270 struct TALER_MERCHANT_GetOrdersHandle *oph, 271 unsigned int num_options, 272 const struct TALER_MERCHANT_GetOrdersOptionValue *options) 273 { 274 for (unsigned int i = 0; i < num_options; i++) 275 { 276 const struct TALER_MERCHANT_GetOrdersOptionValue *opt = 277 &options[i]; 278 279 switch (opt->option) 280 { 281 case TALER_MERCHANT_GET_ORDERS_OPTION_END: 282 return GNUNET_OK; 283 case TALER_MERCHANT_GET_ORDERS_OPTION_TIMEOUT: 284 oph->timeout = opt->details.timeout; 285 break; 286 case TALER_MERCHANT_GET_ORDERS_OPTION_SESSION_ID: 287 GNUNET_free (oph->session_id); 288 if (NULL != opt->details.session_id) 289 oph->session_id = GNUNET_strdup (opt->details.session_id); 290 break; 291 case TALER_MERCHANT_GET_ORDERS_OPTION_MIN_REFUND: 292 oph->min_refund = opt->details.min_refund; 293 oph->have_min_refund = true; 294 break; 295 case TALER_MERCHANT_GET_ORDERS_OPTION_AWAIT_REFUND_OBTAINED: 296 oph->await_refund_obtained = opt->details.await_refund_obtained; 297 break; 298 default: 299 GNUNET_break (0); 300 return GNUNET_NO; 301 } 302 } 303 return GNUNET_OK; 304 } 305 306 307 enum TALER_ErrorCode 308 TALER_MERCHANT_get_orders_start ( 309 struct TALER_MERCHANT_GetOrdersHandle *oph, 310 TALER_MERCHANT_GetOrdersCallback cb, 311 TALER_MERCHANT_GET_ORDERS_RESULT_CLOSURE *cb_cls) 312 { 313 CURL *eh; 314 unsigned int tms; 315 316 oph->cb = cb; 317 oph->cb_cls = cb_cls; 318 tms = (unsigned int) (oph->timeout.rel_value_us 319 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); 320 { 321 struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s; 322 char *path; 323 char timeout_ms[32]; 324 325 GNUNET_CRYPTO_hash_to_enc (&oph->h_contract.hash, 326 &h_contract_s); 327 GNUNET_snprintf (timeout_ms, 328 sizeof (timeout_ms), 329 "%u", 330 tms); 331 GNUNET_asprintf (&path, 332 "orders/%s", 333 oph->order_id); 334 oph->url = TALER_url_join (oph->base_url, 335 path, 336 "h_contract", 337 h_contract_s.encoding, 338 "session_id", 339 oph->session_id, 340 "timeout_ms", 341 (0 != tms) 342 ? timeout_ms 343 : NULL, 344 "refund", 345 oph->have_min_refund 346 ? TALER_amount2s (&oph->min_refund) 347 : NULL, 348 "await_refund_obtained", 349 oph->await_refund_obtained 350 ? "yes" 351 : NULL, 352 NULL); 353 GNUNET_free (path); 354 } 355 if (NULL == oph->url) 356 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 357 eh = TALER_MERCHANT_curl_easy_get_ (oph->url); 358 if (NULL == eh) 359 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 360 if (0 != tms) 361 { 362 GNUNET_break (CURLE_OK == 363 curl_easy_setopt (eh, 364 CURLOPT_TIMEOUT_MS, 365 (long) (tms + 100L))); 366 } 367 oph->job = GNUNET_CURL_job_add (oph->ctx, 368 eh, 369 &handle_get_order_finished, 370 oph); 371 if (NULL == oph->job) 372 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 373 return TALER_EC_NONE; 374 } 375 376 377 void 378 TALER_MERCHANT_get_orders_cancel ( 379 struct TALER_MERCHANT_GetOrdersHandle *oph) 380 { 381 if (NULL != oph->job) 382 { 383 GNUNET_CURL_job_cancel (oph->job); 384 oph->job = NULL; 385 } 386 GNUNET_free (oph->url); 387 GNUNET_free (oph->order_id); 388 GNUNET_free (oph->session_id); 389 GNUNET_free (oph->base_url); 390 GNUNET_free (oph); 391 } 392 393 394 /* end of merchant_api_get-orders-ORDER_ID-new.c */