merchant_api_post-orders-ORDER_ID-claim.c (7873B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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-claim.c 21 * @brief Implementation of the POST /orders/$ID/claim 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-claim.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/claim operation. 40 */ 41 struct TALER_MERCHANT_PostOrdersClaimHandle 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_PostOrdersClaimCallback cb; 62 63 /** 64 * Closure for @a cb. 65 */ 66 TALER_MERCHANT_POST_ORDERS_CLAIM_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 * Wallet nonce for claiming. 85 */ 86 struct GNUNET_CRYPTO_EddsaPublicKey nonce; 87 88 /** 89 * Optional claim token. 90 */ 91 const struct TALER_ClaimTokenP *token; 92 }; 93 94 95 /** 96 * Function called when we're done processing the 97 * HTTP POST /orders/$ID/claim request. 98 * 99 * @param cls the `struct TALER_MERCHANT_PostOrdersClaimHandle` 100 * @param response_code HTTP response code, 0 on error 101 * @param response response body, NULL if not in JSON 102 */ 103 static void 104 handle_post_order_claim_finished (void *cls, 105 long response_code, 106 const void *response) 107 { 108 struct TALER_MERCHANT_PostOrdersClaimHandle *poch = cls; 109 const json_t *json = response; 110 struct TALER_MERCHANT_PostOrdersClaimResponse ocr = { 111 .hr.http_status = (unsigned int) response_code, 112 .hr.reply = json 113 }; 114 struct GNUNET_JSON_Specification spec[] = { 115 GNUNET_JSON_spec_object_const ( 116 "contract_terms", 117 &ocr.details.ok.contract_terms), 118 GNUNET_JSON_spec_fixed_auto ( 119 "sig", 120 &ocr.details.ok.merchant_sig), 121 GNUNET_JSON_spec_end () 122 }; 123 124 poch->job = NULL; 125 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 126 "POST /orders/$ID/claim completed with response code %u\n", 127 (unsigned int) response_code); 128 switch (response_code) 129 { 130 case MHD_HTTP_OK: 131 if (GNUNET_OK != 132 GNUNET_JSON_parse (json, 133 spec, 134 NULL, NULL)) 135 { 136 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 137 "Claiming order failed: could not parse JSON response\n"); 138 GNUNET_break_op (0); 139 ocr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 140 ocr.hr.http_status = 0; 141 break; 142 } 143 144 if (GNUNET_OK != 145 TALER_JSON_contract_hash (ocr.details.ok.contract_terms, 146 &ocr.details.ok.h_contract_terms)) 147 { 148 GNUNET_break (0); 149 ocr.hr.ec 150 = TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE; 151 ocr.hr.http_status = 0; 152 break; 153 } 154 break; 155 case MHD_HTTP_CONFLICT: 156 ocr.hr.ec = TALER_JSON_get_error_code (json); 157 ocr.hr.hint = TALER_JSON_get_error_hint (json); 158 break; 159 default: 160 ocr.hr.ec = TALER_JSON_get_error_code (json); 161 ocr.hr.hint = TALER_JSON_get_error_hint (json); 162 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 163 "Claim order failed with HTTP status code %u/%d\n", 164 (unsigned int) response_code, 165 (int) ocr.hr.ec); 166 break; 167 } 168 poch->cb (poch->cb_cls, 169 &ocr); 170 TALER_MERCHANT_post_orders_claim_cancel (poch); 171 } 172 173 174 struct TALER_MERCHANT_PostOrdersClaimHandle * 175 TALER_MERCHANT_post_orders_claim_create ( 176 struct GNUNET_CURL_Context *ctx, 177 const char *url, 178 const char *order_id, 179 const struct GNUNET_CRYPTO_EddsaPublicKey *nonce) 180 { 181 struct TALER_MERCHANT_PostOrdersClaimHandle *poch; 182 183 poch = GNUNET_new (struct TALER_MERCHANT_PostOrdersClaimHandle); 184 poch->ctx = ctx; 185 poch->base_url = GNUNET_strdup (url); 186 poch->order_id = GNUNET_strdup (order_id); 187 poch->nonce = *nonce; 188 return poch; 189 } 190 191 192 enum GNUNET_GenericReturnValue 193 TALER_MERCHANT_post_orders_claim_set_options_ ( 194 struct TALER_MERCHANT_PostOrdersClaimHandle *poch, 195 unsigned int num_options, 196 const struct TALER_MERCHANT_PostOrdersClaimOptionValue *options) 197 { 198 for (unsigned int i = 0; i < num_options; i++) 199 { 200 switch (options[i].option) 201 { 202 case TALER_MERCHANT_POST_ORDERS_CLAIM_OPTION_END: 203 return GNUNET_OK; 204 case TALER_MERCHANT_POST_ORDERS_CLAIM_OPTION_TOKEN: 205 poch->token = options[i].details.token; 206 break; 207 default: 208 GNUNET_break (0); 209 return GNUNET_SYSERR; 210 } 211 } 212 return GNUNET_OK; 213 } 214 215 216 enum TALER_ErrorCode 217 TALER_MERCHANT_post_orders_claim_start ( 218 struct TALER_MERCHANT_PostOrdersClaimHandle *poch, 219 TALER_MERCHANT_PostOrdersClaimCallback cb, 220 TALER_MERCHANT_POST_ORDERS_CLAIM_RESULT_CLOSURE *cb_cls) 221 { 222 json_t *req_obj; 223 CURL *eh; 224 225 poch->cb = cb; 226 poch->cb_cls = cb_cls; 227 { 228 char *path; 229 230 GNUNET_asprintf (&path, 231 "orders/%s/claim", 232 poch->order_id); 233 poch->url = TALER_url_join (poch->base_url, 234 path, 235 NULL); 236 GNUNET_free (path); 237 } 238 if (NULL == poch->url) 239 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 240 req_obj = GNUNET_JSON_PACK ( 241 GNUNET_JSON_pack_data_auto ("nonce", 242 &poch->nonce), 243 GNUNET_JSON_pack_allow_null ( 244 GNUNET_JSON_pack_data_auto ("token", 245 poch->token))); 246 eh = TALER_MERCHANT_curl_easy_get_ (poch->url); 247 if ( (NULL == eh) || 248 (GNUNET_OK != 249 TALER_curl_easy_post (&poch->post_ctx, 250 eh, 251 req_obj)) ) 252 { 253 GNUNET_break (0); 254 json_decref (req_obj); 255 if (NULL != eh) 256 curl_easy_cleanup (eh); 257 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 258 } 259 json_decref (req_obj); 260 poch->job = GNUNET_CURL_job_add2 (poch->ctx, 261 eh, 262 poch->post_ctx.headers, 263 &handle_post_order_claim_finished, 264 poch); 265 if (NULL == poch->job) 266 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 267 return TALER_EC_NONE; 268 } 269 270 271 void 272 TALER_MERCHANT_post_orders_claim_cancel ( 273 struct TALER_MERCHANT_PostOrdersClaimHandle *poch) 274 { 275 if (NULL != poch->job) 276 { 277 GNUNET_CURL_job_cancel (poch->job); 278 poch->job = NULL; 279 } 280 TALER_curl_easy_post_finished (&poch->post_ctx); 281 GNUNET_free (poch->order_id); 282 GNUNET_free (poch->url); 283 GNUNET_free (poch->base_url); 284 GNUNET_free (poch); 285 } 286 287 288 /* end of merchant_api_post-orders-ORDER_ID-claim.c */