merchant_api_post_transfers.c (7673B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 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_post_transfers.c 19 * @brief Implementation of the POST /transfers request of the merchant's HTTP API 20 * @author Marcello Stanisci 21 * @author Christian Grothoff 22 */ 23 #include "platform.h" 24 #include <curl/curl.h> 25 #include <jansson.h> 26 #include <microhttpd.h> /* just for HTTP status codes */ 27 #include <gnunet/gnunet_util_lib.h> 28 #include "taler_merchant_service.h" 29 #include "merchant_api_curl_defaults.h" 30 #include "merchant_api_common.h" 31 #include <taler/taler_curl_lib.h> 32 #include <taler/taler_json_lib.h> 33 34 35 /** 36 * @brief A handle for POSTing transfer data. 37 */ 38 struct TALER_MERCHANT_PostTransfersHandle 39 { 40 41 /** 42 * The url for this request. 43 */ 44 char *url; 45 46 /** 47 * Handle for the request. 48 */ 49 struct GNUNET_CURL_Job *job; 50 51 /** 52 * Function to call with the result. 53 */ 54 TALER_MERCHANT_PostTransfersCallback cb; 55 56 /** 57 * Closure for @a cb. 58 */ 59 void *cb_cls; 60 61 /** 62 * Reference to the execution context. 63 */ 64 struct GNUNET_CURL_Context *ctx; 65 66 /** 67 * Minor context that holds body and headers. 68 */ 69 struct TALER_CURL_PostContext post_ctx; 70 71 }; 72 73 74 /** 75 * Function called when we're done processing the 76 * HTTP POST /transfers request. 77 * 78 * @param cls the `struct TALER_MERCHANT_PostTransfersHandle` 79 * @param response_code HTTP response code, 0 on error 80 * @param response response body, NULL if not in JSON 81 */ 82 static void 83 handle_post_transfers_finished (void *cls, 84 long response_code, 85 const void *response) 86 { 87 struct TALER_MERCHANT_PostTransfersHandle *pth = cls; 88 struct TALER_MERCHANT_PostTransfersResponse ptr = { 89 .hr.reply = response, 90 .hr.http_status = (unsigned int) response_code 91 }; 92 93 pth->job = NULL; 94 switch (response_code) 95 { 96 case 0: 97 ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 98 break; 99 case MHD_HTTP_NO_CONTENT: 100 break; 101 case MHD_HTTP_UNAUTHORIZED: 102 ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); 103 ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); 104 /* Nothing really to verify, merchant says we need to authenticate. */ 105 break; 106 case MHD_HTTP_NOT_FOUND: 107 /* Nothing really to verify, this should never 108 happen, we should pass the JSON reply to the application */ 109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 110 "Did not find any data\n"); 111 ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); 112 ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); 113 break; 114 case MHD_HTTP_INTERNAL_SERVER_ERROR: 115 /* Server had an internal issue; we should retry, but this API 116 leaves this to the application */ 117 ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); 118 ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); 119 break; 120 case MHD_HTTP_BAD_GATEWAY: 121 /* Exchange had an issue; we should retry, but this API 122 leaves this to the application */ 123 ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); 124 ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); 125 { 126 uint32_t ehc; 127 struct GNUNET_JSON_Specification ispec[] = { 128 TALER_JSON_spec_ec ("exchange_code", 129 &ptr.details.bad_gateway.exchange_ec), 130 GNUNET_JSON_spec_uint32 ("exchange_http_status", 131 &ehc), 132 GNUNET_JSON_spec_end () 133 }; 134 135 if (GNUNET_OK != 136 GNUNET_JSON_parse (ptr.hr.reply, 137 ispec, 138 NULL, NULL)) 139 { 140 GNUNET_break_op (0); 141 ptr.details.bad_gateway.exchange_http_status = 0; 142 ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE; 143 break; 144 } 145 else 146 { 147 ptr.details.bad_gateway.exchange_http_status 148 = (unsigned int) ehc; 149 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 150 "Exchange returned %u/%u\n", 151 (unsigned int) ptr.details.bad_gateway.exchange_ec, 152 (unsigned int) ehc); 153 } 154 } 155 break; 156 case MHD_HTTP_GATEWAY_TIMEOUT: 157 /* Server had an internal issue; we should retry, but this API 158 leaves this to the application */ 159 ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply); 160 ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply); 161 break; 162 default: 163 /* unexpected response code */ 164 GNUNET_break_op (0); 165 TALER_MERCHANT_parse_error_details_ (ptr.hr.reply, 166 response_code, 167 &ptr.hr); 168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 169 "Unexpected response code %u/%d\n", 170 (unsigned int) ptr.hr.http_status, 171 (int) ptr.hr.ec); 172 break; 173 } 174 pth->cb (pth->cb_cls, 175 &ptr); 176 TALER_MERCHANT_transfers_post_cancel (pth); 177 } 178 179 180 struct TALER_MERCHANT_PostTransfersHandle * 181 TALER_MERCHANT_transfers_post ( 182 struct GNUNET_CURL_Context *ctx, 183 const char *backend_url, 184 const struct TALER_Amount *credit_amount, 185 const struct TALER_WireTransferIdentifierRawP *wtid, 186 struct TALER_FullPayto payto_uri, 187 const char *exchange_url, 188 TALER_MERCHANT_PostTransfersCallback cb, 189 void *cb_cls) 190 { 191 struct TALER_MERCHANT_PostTransfersHandle *pth; 192 CURL *eh; 193 json_t *req; 194 195 pth = GNUNET_new (struct TALER_MERCHANT_PostTransfersHandle); 196 pth->ctx = ctx; 197 pth->cb = cb; 198 pth->cb_cls = cb_cls; 199 pth->url = TALER_url_join (backend_url, 200 "private/transfers", 201 NULL); 202 if (NULL == pth->url) 203 { 204 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 205 "Could not construct request URL.\n"); 206 GNUNET_free (pth); 207 return NULL; 208 } 209 req = GNUNET_JSON_PACK ( 210 TALER_JSON_pack_amount ("credit_amount", 211 credit_amount), 212 GNUNET_JSON_pack_data_auto ("wtid", 213 wtid), 214 TALER_JSON_pack_full_payto ("payto_uri", 215 payto_uri), 216 GNUNET_JSON_pack_string ("exchange_url", 217 exchange_url)); 218 eh = TALER_MERCHANT_curl_easy_get_ (pth->url); 219 if (GNUNET_OK != 220 TALER_curl_easy_post (&pth->post_ctx, 221 eh, 222 req)) 223 { 224 GNUNET_break (0); 225 curl_easy_cleanup (eh); 226 json_decref (req); 227 GNUNET_free (pth->url); 228 GNUNET_free (pth); 229 return NULL; 230 } 231 json_decref (req); 232 pth->job = GNUNET_CURL_job_add2 (ctx, 233 eh, 234 pth->post_ctx.headers, 235 &handle_post_transfers_finished, 236 pth); 237 return pth; 238 } 239 240 241 void 242 TALER_MERCHANT_transfers_post_cancel ( 243 struct TALER_MERCHANT_PostTransfersHandle *pth) 244 { 245 if (NULL != pth->job) 246 { 247 GNUNET_CURL_job_cancel (pth->job); 248 pth->job = NULL; 249 } 250 GNUNET_free (pth->url); 251 TALER_curl_easy_post_finished (&pth->post_ctx); 252 GNUNET_free (pth); 253 } 254 255 256 /* end of merchant_api_post_transfers.c */