bank_api_registration.c (11598B)
1 /* 2 This file is part of TALER 3 Copyright (C) 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 General Public License as published by the Free Software 7 Foundation; either version 3, 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file bank-lib/bank_api_registration.c 19 * @brief Implementation of the POST /registration request of the bank's HTTP API 20 * @author Christian Grothoff 21 */ 22 #include "bank_api_common.h" 23 #include <microhttpd.h> /* just for HTTP status codes */ 24 #include "taler/taler_json_lib.h" 25 #include "taler/taler_signatures.h" 26 #include "taler/taler_curl_lib.h" 27 28 29 /** 30 * @brief A /registration Handle 31 */ 32 struct TALER_BANK_RegistrationHandle 33 { 34 35 /** 36 * The URL for this request. 37 */ 38 char *request_url; 39 40 /** 41 * POST context. 42 */ 43 struct TALER_CURL_PostContext post_ctx; 44 45 /** 46 * Handle for the request. 47 */ 48 struct GNUNET_CURL_Job *job; 49 50 /** 51 * Function to call with the result. 52 */ 53 TALER_BANK_RegistrationCallback cb; 54 55 /** 56 * Closure for @a cb. 57 */ 58 void *cb_cls; 59 60 }; 61 62 63 /** 64 * Parse the "subject" field of a successful /registration response. 65 * The field is a JSON object discriminated by "type". 66 * 67 * @param subject_json the JSON object to parse (the inner "subject" value) 68 * @param[out] ts set to the parsed transfer subject on success 69 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 70 */ 71 static enum GNUNET_GenericReturnValue 72 parse_transfer_subject (const json_t *subject_json, 73 struct TALER_BANK_TransferSubject *ts) 74 { 75 const char *type_str; 76 struct GNUNET_JSON_Specification type_spec[] = { 77 GNUNET_JSON_spec_string ("type", 78 &type_str), 79 GNUNET_JSON_spec_end () 80 }; 81 82 if (GNUNET_OK != 83 GNUNET_JSON_parse (subject_json, 84 type_spec, 85 NULL, 86 NULL)) 87 { 88 GNUNET_break_op (0); 89 return GNUNET_SYSERR; 90 } 91 92 if (0 == strcasecmp (type_str, 93 "SIMPLE")) 94 { 95 struct GNUNET_JSON_Specification spec[] = { 96 TALER_JSON_spec_amount_any ( 97 "credit_amount", 98 &ts->details.simple.credit_amount), 99 GNUNET_JSON_spec_string ( 100 "subject", 101 (const char **) &ts->details.simple.subject), 102 GNUNET_JSON_spec_end () 103 }; 104 105 if (GNUNET_OK != 106 GNUNET_JSON_parse (subject_json, 107 spec, 108 NULL, NULL)) 109 { 110 GNUNET_break_op (0); 111 return GNUNET_SYSERR; 112 } 113 ts->format = TALER_BANK_SUBJECT_FORMAT_SIMPLE; 114 return GNUNET_OK; 115 } 116 if (0 == strcasecmp (type_str, 117 "URI")) 118 { 119 struct GNUNET_JSON_Specification spec[] = { 120 TALER_JSON_spec_amount_any ( 121 "credit_amount", 122 &ts->details.uri.credit_amount), 123 GNUNET_JSON_spec_string ( 124 "uri", 125 (const char **) &ts->details.uri.uri), 126 GNUNET_JSON_spec_end () 127 }; 128 129 if (GNUNET_OK != 130 GNUNET_JSON_parse (subject_json, 131 spec, 132 NULL, NULL)) 133 { 134 GNUNET_break_op (0); 135 return GNUNET_SYSERR; 136 } 137 ts->format = TALER_BANK_SUBJECT_FORMAT_URI; 138 return GNUNET_OK; 139 } 140 if (0 == strcasecmp (type_str, 141 "CH_QR_BILL")) 142 { 143 struct GNUNET_JSON_Specification spec[] = { 144 TALER_JSON_spec_amount_any ( 145 "credit_amount", 146 &ts->details.ch_qr_bill.credit_amount), 147 GNUNET_JSON_spec_string ( 148 "qr_reference_number", 149 (const char **) &ts->details.ch_qr_bill.qr_reference_number), 150 GNUNET_JSON_spec_end () 151 }; 152 153 if (GNUNET_OK != 154 GNUNET_JSON_parse (subject_json, 155 spec, 156 NULL, NULL)) 157 { 158 GNUNET_break_op (0); 159 return GNUNET_SYSERR; 160 } 161 ts->format = TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL; 162 return GNUNET_OK; 163 } 164 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 165 "Unknown transfer subject type `%s'\n", 166 type_str); 167 GNUNET_break_op (0); 168 return GNUNET_SYSERR; 169 } 170 171 172 /** 173 * Function called when we're done processing the HTTP POST /registration 174 * request. 175 * 176 * @param cls the `struct TALER_BANK_RegistrationHandle` 177 * @param response_code HTTP response code, 0 on error 178 * @param response parsed JSON result, NULL on error 179 */ 180 static void 181 handle_registration_finished (void *cls, 182 long response_code, 183 const void *response) 184 { 185 struct TALER_BANK_RegistrationHandle *rh = cls; 186 const json_t *j = response; 187 struct TALER_BANK_RegistrationResponse rr = { 188 .http_status = response_code, 189 .response = response 190 }; 191 192 rh->job = NULL; 193 switch (response_code) 194 { 195 case 0: 196 rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 197 break; 198 case MHD_HTTP_OK: 199 { 200 const json_t *subjects; 201 struct GNUNET_JSON_Specification spec[] = { 202 GNUNET_JSON_spec_array_const ("subjects", 203 &subjects), 204 GNUNET_JSON_spec_timestamp ("expiration", 205 &rr.details.ok.expiration), 206 GNUNET_JSON_spec_end () 207 }; 208 209 if (GNUNET_OK != 210 GNUNET_JSON_parse (j, 211 spec, 212 NULL, NULL)) 213 { 214 GNUNET_break_op (0); 215 rr.http_status = 0; 216 rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 217 break; 218 } 219 220 { 221 size_t n = json_array_size (subjects); 222 struct TALER_BANK_TransferSubject *ts; 223 size_t i; 224 const json_t *subject; 225 226 ts = GNUNET_new_array (GNUNET_NZL (n), 227 struct TALER_BANK_TransferSubject); 228 json_array_foreach ((json_t *) subjects, i, subject) 229 { 230 if (GNUNET_OK != 231 parse_transfer_subject (subject, 232 &ts[i])) 233 { 234 GNUNET_break_op (0); 235 rr.http_status = 0; 236 rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 237 break; 238 } 239 } 240 if (MHD_HTTP_OK == rr.http_status) 241 { 242 rr.details.ok.num_subjects = n; 243 rr.details.ok.subjects = ts; 244 rh->cb (rh->cb_cls, 245 &rr); 246 GNUNET_free (ts); 247 TALER_BANK_registration_cancel (rh); 248 return; 249 } 250 GNUNET_free (ts); 251 } 252 } 253 break; 254 case MHD_HTTP_BAD_REQUEST: 255 /* Either we or the service is buggy, or there is an API version conflict. */ 256 GNUNET_break_op (0); 257 rr.ec = TALER_JSON_get_error_code (j); 258 break; 259 case MHD_HTTP_CONFLICT: 260 /* Covers TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT, 261 TALER_EC_BANK_UNSUPPORTED_SUBJECT_FORMAT, 262 TALER_EC_BANK_DERIVATION_REUSE, and 263 TALER_EC_BANK_BAD_SIGNATURE. */ 264 rr.ec = TALER_JSON_get_error_code (j); 265 break; 266 case MHD_HTTP_INTERNAL_SERVER_ERROR: 267 /* Server had an internal issue; we should retry, but this API 268 leaves that to the application. */ 269 rr.ec = TALER_JSON_get_error_code (j); 270 break; 271 default: 272 /* unexpected response code */ 273 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 274 "Unexpected response code %u\n", 275 (unsigned int) response_code); 276 GNUNET_break (0); 277 rr.ec = TALER_JSON_get_error_code (j); 278 break; 279 } 280 rh->cb (rh->cb_cls, 281 &rr); 282 TALER_BANK_registration_cancel (rh); 283 } 284 285 286 struct TALER_BANK_RegistrationHandle * 287 TALER_BANK_registration ( 288 struct GNUNET_CURL_Context *ctx, 289 const char *base_url, 290 const struct TALER_FullPayto *credit_account, 291 const struct TALER_Amount *credit_amount, 292 enum TALER_BankRegistrationType type, 293 const union TALER_AccountPublicKeyP *account_pub, 294 const struct TALER_PreparedTransferAuthorizationPrivateKeyP * 295 authorization_priv, 296 bool recurrent, 297 TALER_BANK_RegistrationCallback res_cb, 298 void *res_cb_cls) 299 { 300 struct TALER_PreparedTransferAuthorizationPublicKeyP authorization_pub; 301 struct TALER_PreparedTransferAuthorizationSignatureP authorization_sig; 302 struct TALER_BANK_RegistrationHandle *rh; 303 const char *type_str; 304 json_t *reg_obj; 305 CURL *eh; 306 307 TALER_wallet_prepared_transfer_registration_sign ( 308 *credit_account, 309 credit_amount, 310 type, 311 recurrent, 312 account_pub, 313 authorization_priv, 314 &authorization_sig); 315 GNUNET_CRYPTO_eddsa_key_get_public (&authorization_priv->eddsa_priv, 316 &authorization_pub.eddsa_pub); 317 318 switch (type) 319 { 320 case TALER_BANK_REGISTRATION_TYPE_RESERVE: 321 type_str = "reserve"; 322 break; 323 case TALER_BANK_REGISTRATION_TYPE_KYC: 324 type_str = "kyc"; 325 break; 326 default: 327 GNUNET_break (0); 328 return NULL; 329 } 330 331 reg_obj = GNUNET_JSON_PACK ( 332 TALER_JSON_pack_full_payto ("credit_account", 333 *credit_account), 334 TALER_JSON_pack_amount ("credit_amount", 335 credit_amount), 336 GNUNET_JSON_pack_string ("type", 337 type_str), 338 GNUNET_JSON_pack_string ("alg", 339 "EdDSA"), 340 GNUNET_JSON_pack_data_auto ("account_pub", 341 account_pub), 342 GNUNET_JSON_pack_data_auto ("authorization_pub", 343 &authorization_pub), 344 GNUNET_JSON_pack_data_auto ("authorization_sig", 345 &authorization_sig), 346 GNUNET_JSON_pack_bool ("recurrent", 347 recurrent)); 348 rh = GNUNET_new (struct TALER_BANK_RegistrationHandle); 349 rh->cb = res_cb; 350 rh->cb_cls = res_cb_cls; 351 rh->request_url = TALER_url_join (base_url, 352 "registration", 353 NULL); 354 if (NULL == rh->request_url) 355 { 356 GNUNET_free (rh); 357 json_decref (reg_obj); 358 return NULL; 359 } 360 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 361 "Requesting wire transfer subject registration at `%s'\n", 362 rh->request_url); 363 eh = curl_easy_init (); 364 if ( (NULL == eh) || 365 (CURLE_OK != 366 curl_easy_setopt (eh, 367 CURLOPT_URL, 368 rh->request_url)) || 369 (GNUNET_OK != 370 TALER_curl_easy_post (&rh->post_ctx, 371 eh, 372 reg_obj)) ) 373 { 374 GNUNET_break (0); 375 TALER_BANK_registration_cancel (rh); 376 if (NULL != eh) 377 curl_easy_cleanup (eh); 378 json_decref (reg_obj); 379 return NULL; 380 } 381 json_decref (reg_obj); 382 rh->job = GNUNET_CURL_job_add2 (ctx, 383 eh, 384 rh->post_ctx.headers, 385 &handle_registration_finished, 386 rh); 387 if (NULL == rh->job) 388 { 389 GNUNET_break (0); 390 curl_easy_cleanup (eh); 391 TALER_BANK_registration_cancel (rh); 392 return NULL; 393 } 394 return rh; 395 } 396 397 398 void 399 TALER_BANK_registration_cancel ( 400 struct TALER_BANK_RegistrationHandle *rh) 401 { 402 if (NULL != rh->job) 403 { 404 GNUNET_CURL_job_cancel (rh->job); 405 rh->job = NULL; 406 } 407 TALER_curl_easy_post_finished (&rh->post_ctx); 408 GNUNET_free (rh->request_url); 409 GNUNET_free (rh); 410 } 411 412 413 /* end of bank_api_registration.c */