merchant_api_post_order_paid.c (7751B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-2021 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_order_paid.c 21 * @brief Implementation of the POST /order/$ID/paid request 22 * of the merchant's HTTP API 23 * @author Jonathan Buchanan 24 */ 25 #include "platform.h" 26 #include <curl/curl.h> 27 #include <jansson.h> 28 #include <microhttpd.h> /* just for HTTP status codes */ 29 #include <gnunet/gnunet_util_lib.h> 30 #include <gnunet/gnunet_curl_lib.h> 31 #include "taler_merchant_service.h" 32 #include "merchant_api_curl_defaults.h" 33 #include "merchant_api_common.h" 34 #include <taler/taler_json_lib.h> 35 #include <taler/taler_signatures.h> 36 #include <taler/taler_exchange_service.h> 37 #include <taler/taler_curl_lib.h> 38 39 40 /** 41 * @brief Handle to a POST /orders/$ID/paid operation at a merchant. 42 */ 43 struct TALER_MERCHANT_OrderPaidHandle 44 { 45 46 /** 47 * The url for this request. 48 */ 49 char *url; 50 51 /** 52 * Handle for the request. 53 */ 54 struct GNUNET_CURL_Job *job; 55 56 /** 57 * Function to call with the result. 58 */ 59 TALER_MERCHANT_OrderPaidCallback paid_cb; 60 61 /** 62 * Closure for @a paid_cb. 63 */ 64 void *paid_cb_cls; 65 66 /** 67 * Reference to the execution context. 68 */ 69 struct GNUNET_CURL_Context *ctx; 70 71 /** 72 * Minor context that holds body and headers. 73 */ 74 struct TALER_CURL_PostContext post_ctx; 75 }; 76 77 78 /** 79 * Function called when we're done processing the 80 * HTTP /paid request. 81 * 82 * @param cls the `struct TALER_MERCHANT_OrderPaidHandle` 83 * @param response_code HTTP response code, 0 on error 84 * @param response response body, NULL if not in JSON 85 */ 86 static void 87 handle_paid_finished (void *cls, 88 long response_code, 89 const void *response) 90 { 91 struct TALER_MERCHANT_OrderPaidHandle *oph = cls; 92 const json_t *json = response; 93 struct TALER_MERCHANT_OrderPaidResponse opr = { 94 .hr.http_status = (unsigned int) response_code, 95 .hr.reply = json 96 }; 97 98 oph->job = NULL; 99 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 100 "/paid completed with response code %u\n", 101 (unsigned int) response_code); 102 switch (response_code) 103 { 104 case 0: 105 opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 106 break; 107 case MHD_HTTP_OK: 108 { 109 bool refunded; 110 struct GNUNET_JSON_Specification spec[] = { 111 GNUNET_JSON_spec_bool ("refunded", 112 &refunded), 113 GNUNET_JSON_spec_end () 114 }; 115 116 if (GNUNET_OK != 117 GNUNET_JSON_parse (opr.hr.reply, 118 spec, 119 NULL, 120 NULL)) 121 { 122 GNUNET_break_op (0); 123 opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 124 break; 125 } 126 break; 127 } 128 break; 129 case MHD_HTTP_BAD_REQUEST: 130 opr.hr.ec = TALER_JSON_get_error_code (json); 131 opr.hr.hint = TALER_JSON_get_error_hint (json); 132 /* This should never happen, either us 133 * or the merchant is buggy (or API version conflict); 134 * just pass JSON reply to the application */ 135 break; 136 case MHD_HTTP_FORBIDDEN: 137 opr.hr.ec = TALER_JSON_get_error_code (json); 138 opr.hr.hint = TALER_JSON_get_error_hint (json); 139 /* The signature provided was invalid */ 140 break; 141 case MHD_HTTP_NOT_FOUND: 142 opr.hr.ec = TALER_JSON_get_error_code (json); 143 opr.hr.hint = TALER_JSON_get_error_hint (json); 144 /* Nothing really to verify, this should never 145 happen, we should pass the JSON reply to the 146 application */ 147 break; 148 case MHD_HTTP_CONFLICT: 149 opr.hr.ec = TALER_JSON_get_error_code (json); 150 opr.hr.hint = TALER_JSON_get_error_hint (json); 151 /* The hashed contract terms don't match with the order_id. */ 152 break; 153 case MHD_HTTP_INTERNAL_SERVER_ERROR: 154 opr.hr.ec = TALER_JSON_get_error_code (json); 155 opr.hr.hint = TALER_JSON_get_error_hint (json); 156 /* Server had an internal issue; we should retry, 157 but this API leaves this to the application */ 158 break; 159 case MHD_HTTP_SERVICE_UNAVAILABLE: 160 TALER_MERCHANT_parse_error_details_ (json, 161 response_code, 162 &opr.hr); 163 /* Exchange couldn't respond properly; the retry is 164 left to the application */ 165 break; 166 default: 167 TALER_MERCHANT_parse_error_details_ (json, 168 response_code, 169 &opr.hr); 170 /* unexpected response code */ 171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 172 "Unexpected response code %u/%d\n", 173 (unsigned int) response_code, 174 (int) opr.hr.ec); 175 GNUNET_break_op (0); 176 break; 177 } 178 oph->paid_cb (oph->paid_cb_cls, 179 &opr); 180 TALER_MERCHANT_order_paid_cancel (oph); 181 } 182 183 184 struct TALER_MERCHANT_OrderPaidHandle * 185 TALER_MERCHANT_order_paid ( 186 struct GNUNET_CURL_Context *ctx, 187 const char *merchant_url, 188 const char *order_id, 189 const char *session_id, 190 const struct TALER_PrivateContractHashP *h_contract_terms, 191 const struct GNUNET_HashCode *wallet_data_hash, 192 const struct TALER_MerchantSignatureP *merchant_sig, 193 TALER_MERCHANT_OrderPaidCallback paid_cb, 194 void *paid_cb_cls) 195 { 196 struct TALER_MERCHANT_OrderPaidHandle *oph; 197 json_t *req_obj; 198 199 (void) wallet_data_hash; 200 req_obj = GNUNET_JSON_PACK ( 201 GNUNET_JSON_pack_data_auto ("sig", 202 merchant_sig), 203 GNUNET_JSON_pack_data_auto ("h_contract", 204 h_contract_terms), 205 GNUNET_JSON_pack_string ("session_id", 206 session_id)); 207 oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle); 208 oph->ctx = ctx; 209 oph->paid_cb = paid_cb; 210 oph->paid_cb_cls = paid_cb_cls; 211 { 212 char *path; 213 214 GNUNET_asprintf (&path, 215 "orders/%s/paid", 216 order_id); 217 oph->url = TALER_url_join (merchant_url, 218 path, 219 NULL); 220 GNUNET_free (path); 221 } 222 if (NULL == oph->url) 223 { 224 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 225 "Could not construct request URL.\n"); 226 json_decref (req_obj); 227 GNUNET_free (oph); 228 return NULL; 229 } 230 { 231 CURL *eh; 232 233 eh = TALER_MERCHANT_curl_easy_get_ (oph->url); 234 if (GNUNET_OK != 235 TALER_curl_easy_post (&oph->post_ctx, 236 eh, 237 req_obj)) 238 { 239 GNUNET_break (0); 240 curl_easy_cleanup (eh); 241 json_decref (req_obj); 242 GNUNET_free (oph); 243 return NULL; 244 } 245 json_decref (req_obj); 246 oph->job = GNUNET_CURL_job_add2 (ctx, 247 eh, 248 oph->post_ctx.headers, 249 &handle_paid_finished, 250 oph); 251 } 252 return oph; 253 } 254 255 256 void 257 TALER_MERCHANT_order_paid_cancel ( 258 struct TALER_MERCHANT_OrderPaidHandle *oph) 259 { 260 if (NULL != oph->job) 261 { 262 GNUNET_CURL_job_cancel (oph->job); 263 oph->job = NULL; 264 } 265 TALER_curl_easy_post_finished (&oph->post_ctx); 266 GNUNET_free (oph->url); 267 GNUNET_free (oph); 268 } 269 270 271 /* end of merchant_api_post_order_paid.c */