merchant_api_post-orders-ORDER_ID-paid.c (8340B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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-orders-ORDER_ID-paid-new.c 21 * @brief Implementation of the POST /orders/$ORDER_ID/paid 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-orders-ORDER_ID-paid.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 #include <taler/taler_signatures.h> 36 37 38 /** 39 * Handle for a POST /orders/$ORDER_ID/paid operation. 40 */ 41 struct TALER_MERCHANT_PostOrdersPaidHandle 42 { 43 /** 44 * Base URL of the merchant backend. 45 */ 46 char *base_url; 47 48 /** 49 * The full URL for this request. 50 */ 51 char *url; 52 53 /** 54 * Handle for the request. 55 */ 56 struct GNUNET_CURL_Job *job; 57 58 /** 59 * Function to call with the result. 60 */ 61 TALER_MERCHANT_PostOrdersPaidCallback cb; 62 63 /** 64 * Closure for @a cb. 65 */ 66 TALER_MERCHANT_POST_ORDERS_PAID_RESULT_CLOSURE *cb_cls; 67 68 /** 69 * Reference to the execution context. 70 */ 71 struct GNUNET_CURL_Context *ctx; 72 73 /** 74 * Minor context that holds body and headers. 75 */ 76 struct TALER_CURL_PostContext post_ctx; 77 78 /** 79 * Order identifier. 80 */ 81 char *order_id; 82 83 /** 84 * Session identifier. 85 */ 86 char *session_id; 87 88 /** 89 * Hash of the contract terms. 90 */ 91 struct TALER_PrivateContractHashP h_contract_terms; 92 93 /** 94 * Merchant signature over the session data. 95 */ 96 struct TALER_MerchantSignatureP merchant_sig; 97 98 }; 99 100 101 /** 102 * Function called when we're done processing the 103 * HTTP POST /orders/$ORDER_ID/paid request. 104 * 105 * @param cls the `struct TALER_MERCHANT_PostOrdersPaidHandle` 106 * @param response_code HTTP response code, 0 on error 107 * @param response response body, NULL if not in JSON 108 */ 109 static void 110 handle_paid_finished (void *cls, 111 long response_code, 112 const void *response) 113 { 114 struct TALER_MERCHANT_PostOrdersPaidHandle *poph = cls; 115 const json_t *json = response; 116 struct TALER_MERCHANT_PostOrdersPaidResponse opr = { 117 .hr.http_status = (unsigned int) response_code, 118 .hr.reply = json 119 }; 120 121 poph->job = NULL; 122 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 123 "POST /orders/$ID/paid completed with response code %u\n", 124 (unsigned int) response_code); 125 switch (response_code) 126 { 127 case 0: 128 opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 129 break; 130 case MHD_HTTP_OK: 131 { 132 struct GNUNET_JSON_Specification spec[] = { 133 GNUNET_JSON_spec_mark_optional ( 134 GNUNET_JSON_spec_string ("pos_confirmation", 135 &opr.details.ok.pos_confirmation), 136 NULL), 137 GNUNET_JSON_spec_bool ("refunded", 138 &opr.details.ok.refunded), 139 GNUNET_JSON_spec_end () 140 }; 141 142 if (GNUNET_OK != 143 GNUNET_JSON_parse (json, 144 spec, 145 NULL, NULL)) 146 { 147 opr.hr.http_status = 0; 148 opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 149 break; 150 } 151 } 152 break; 153 case MHD_HTTP_BAD_REQUEST: 154 opr.hr.ec = TALER_JSON_get_error_code (json); 155 opr.hr.hint = TALER_JSON_get_error_hint (json); 156 break; 157 case MHD_HTTP_FORBIDDEN: 158 opr.hr.ec = TALER_JSON_get_error_code (json); 159 opr.hr.hint = TALER_JSON_get_error_hint (json); 160 break; 161 case MHD_HTTP_NOT_FOUND: 162 opr.hr.ec = TALER_JSON_get_error_code (json); 163 opr.hr.hint = TALER_JSON_get_error_hint (json); 164 break; 165 case MHD_HTTP_INTERNAL_SERVER_ERROR: 166 opr.hr.ec = TALER_JSON_get_error_code (json); 167 opr.hr.hint = TALER_JSON_get_error_hint (json); 168 break; 169 default: 170 TALER_MERCHANT_parse_error_details_ (json, 171 response_code, 172 &opr.hr); 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 174 "Unexpected response code %u/%d\n", 175 (unsigned int) response_code, 176 (int) opr.hr.ec); 177 GNUNET_break_op (0); 178 break; 179 } 180 poph->cb (poph->cb_cls, 181 &opr); 182 TALER_MERCHANT_post_orders_paid_cancel (poph); 183 } 184 185 186 struct TALER_MERCHANT_PostOrdersPaidHandle * 187 TALER_MERCHANT_post_orders_paid_create ( 188 struct GNUNET_CURL_Context *ctx, 189 const char *url, 190 const char *order_id, 191 const char *session_id, 192 const struct TALER_PrivateContractHashP *h_contract_terms, 193 const struct TALER_MerchantSignatureP *merchant_sig) 194 { 195 struct TALER_MERCHANT_PostOrdersPaidHandle *poph; 196 197 poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPaidHandle); 198 poph->ctx = ctx; 199 poph->base_url = GNUNET_strdup (url); 200 poph->order_id = GNUNET_strdup (order_id); 201 poph->session_id = GNUNET_strdup (session_id); 202 poph->h_contract_terms = *h_contract_terms; 203 poph->merchant_sig = *merchant_sig; 204 return poph; 205 } 206 207 208 enum GNUNET_GenericReturnValue 209 TALER_MERCHANT_post_orders_paid_set_options_ ( 210 struct TALER_MERCHANT_PostOrdersPaidHandle *poph, 211 unsigned int num_options, 212 const struct TALER_MERCHANT_PostOrdersPaidOptionValue *options) 213 { 214 for (unsigned int i = 0; i < num_options; i++) 215 { 216 switch (options[i].option) 217 { 218 case TALER_MERCHANT_POST_ORDERS_PAID_OPTION_END: 219 return GNUNET_OK; 220 default: 221 GNUNET_break (0); 222 return GNUNET_SYSERR; 223 } 224 } 225 return GNUNET_OK; 226 } 227 228 229 enum TALER_ErrorCode 230 TALER_MERCHANT_post_orders_paid_start ( 231 struct TALER_MERCHANT_PostOrdersPaidHandle *poph, 232 TALER_MERCHANT_PostOrdersPaidCallback cb, 233 TALER_MERCHANT_POST_ORDERS_PAID_RESULT_CLOSURE *cb_cls) 234 { 235 json_t *req_obj; 236 CURL *eh; 237 238 poph->cb = cb; 239 poph->cb_cls = cb_cls; 240 { 241 char *path; 242 243 GNUNET_asprintf (&path, 244 "orders/%s/paid", 245 poph->order_id); 246 poph->url = TALER_url_join (poph->base_url, 247 path, 248 NULL); 249 GNUNET_free (path); 250 } 251 if (NULL == poph->url) 252 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 253 req_obj = GNUNET_JSON_PACK ( 254 GNUNET_JSON_pack_data_auto ("sig", 255 &poph->merchant_sig), 256 GNUNET_JSON_pack_data_auto ("h_contract", 257 &poph->h_contract_terms), 258 GNUNET_JSON_pack_string ("session_id", 259 poph->session_id)); 260 eh = TALER_MERCHANT_curl_easy_get_ (poph->url); 261 if ( (NULL == eh) || 262 (GNUNET_OK != 263 TALER_curl_easy_post (&poph->post_ctx, 264 eh, 265 req_obj)) ) 266 { 267 GNUNET_break (0); 268 json_decref (req_obj); 269 if (NULL != eh) 270 curl_easy_cleanup (eh); 271 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 272 } 273 json_decref (req_obj); 274 poph->job = GNUNET_CURL_job_add2 (poph->ctx, 275 eh, 276 poph->post_ctx.headers, 277 &handle_paid_finished, 278 poph); 279 if (NULL == poph->job) 280 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 281 return TALER_EC_NONE; 282 } 283 284 285 void 286 TALER_MERCHANT_post_orders_paid_cancel ( 287 struct TALER_MERCHANT_PostOrdersPaidHandle *poph) 288 { 289 if (NULL != poph->job) 290 { 291 GNUNET_CURL_job_cancel (poph->job); 292 poph->job = NULL; 293 } 294 TALER_curl_easy_post_finished (&poph->post_ctx); 295 GNUNET_free (poph->order_id); 296 GNUNET_free (poph->session_id); 297 GNUNET_free (poph->url); 298 GNUNET_free (poph->base_url); 299 GNUNET_free (poph); 300 } 301 302 303 /* end of merchant_api_post-orders-ORDER_ID-paid-new.c */