bank_api_registration.c (11402B)
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 GNUNET_JSON_spec_string ( 121 "uri", 122 (const char **) &ts->details.uri.uri), 123 GNUNET_JSON_spec_end () 124 }; 125 126 if (GNUNET_OK != 127 GNUNET_JSON_parse (subject_json, 128 spec, 129 NULL, NULL)) 130 { 131 GNUNET_break_op (0); 132 return GNUNET_SYSERR; 133 } 134 ts->format = TALER_BANK_SUBJECT_FORMAT_URI; 135 return GNUNET_OK; 136 } 137 if (0 == strcasecmp (type_str, 138 "CH_QR_BILL")) 139 { 140 struct GNUNET_JSON_Specification spec[] = { 141 TALER_JSON_spec_amount_any ( 142 "credit_amount", 143 &ts->details.ch_qr_bill.credit_amount), 144 GNUNET_JSON_spec_string ( 145 "qr_reference_number", 146 (const char **) &ts->details.ch_qr_bill.qr_reference_number), 147 GNUNET_JSON_spec_end () 148 }; 149 150 if (GNUNET_OK != 151 GNUNET_JSON_parse (subject_json, 152 spec, 153 NULL, NULL)) 154 { 155 GNUNET_break_op (0); 156 return GNUNET_SYSERR; 157 } 158 ts->format = TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL; 159 return GNUNET_OK; 160 } 161 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 162 "Unknown transfer subject type `%s'\n", 163 type_str); 164 GNUNET_break_op (0); 165 return GNUNET_SYSERR; 166 } 167 168 169 /** 170 * Function called when we're done processing the HTTP POST /registration 171 * request. 172 * 173 * @param cls the `struct TALER_BANK_RegistrationHandle` 174 * @param response_code HTTP response code, 0 on error 175 * @param response parsed JSON result, NULL on error 176 */ 177 static void 178 handle_registration_finished (void *cls, 179 long response_code, 180 const void *response) 181 { 182 struct TALER_BANK_RegistrationHandle *rh = cls; 183 const json_t *j = response; 184 struct TALER_BANK_RegistrationResponse rr = { 185 .http_status = response_code, 186 .response = response 187 }; 188 189 rh->job = NULL; 190 switch (response_code) 191 { 192 case 0: 193 rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 194 break; 195 case MHD_HTTP_OK: 196 { 197 const json_t *subjects; 198 struct GNUNET_JSON_Specification spec[] = { 199 GNUNET_JSON_spec_array_const ("subjects", 200 &subjects), 201 GNUNET_JSON_spec_timestamp ("expiration", 202 &rr.details.ok.expiration), 203 GNUNET_JSON_spec_end () 204 }; 205 206 if (GNUNET_OK != 207 GNUNET_JSON_parse (j, 208 spec, 209 NULL, NULL)) 210 { 211 GNUNET_break_op (0); 212 rr.http_status = 0; 213 rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 214 break; 215 } 216 217 { 218 size_t n = json_array_size (subjects); 219 struct TALER_BANK_TransferSubject *ts; 220 size_t i; 221 const json_t *subject; 222 223 ts = GNUNET_new_array (GNUNET_NZL (n), 224 struct TALER_BANK_TransferSubject); 225 json_array_foreach ((json_t *) subjects, i, subject) 226 { 227 if (GNUNET_OK != 228 parse_transfer_subject (subject, 229 &ts[i])) 230 { 231 GNUNET_break_op (0); 232 rr.http_status = 0; 233 rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 234 break; 235 } 236 } 237 if (MHD_HTTP_OK == rr.http_status) 238 { 239 rr.details.ok.num_subjects = n; 240 rr.details.ok.subjects = ts; 241 rh->cb (rh->cb_cls, 242 &rr); 243 GNUNET_free (ts); 244 TALER_BANK_registration_cancel (rh); 245 return; 246 } 247 GNUNET_free (ts); 248 } 249 } 250 break; 251 case MHD_HTTP_BAD_REQUEST: 252 /* Either we or the service is buggy, or there is an API version conflict. */ 253 GNUNET_break_op (0); 254 rr.ec = TALER_JSON_get_error_code (j); 255 break; 256 case MHD_HTTP_CONFLICT: 257 /* Covers TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT, 258 TALER_EC_BANK_UNSUPPORTED_SUBJECT_FORMAT, 259 TALER_EC_BANK_DERIVATION_REUSE, and 260 TALER_EC_BANK_BAD_SIGNATURE. */ 261 rr.ec = TALER_JSON_get_error_code (j); 262 break; 263 case MHD_HTTP_INTERNAL_SERVER_ERROR: 264 /* Server had an internal issue; we should retry, but this API 265 leaves that to the application. */ 266 rr.ec = TALER_JSON_get_error_code (j); 267 break; 268 default: 269 /* unexpected response code */ 270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 271 "Unexpected response code %u\n", 272 (unsigned int) response_code); 273 GNUNET_break (0); 274 rr.ec = TALER_JSON_get_error_code (j); 275 break; 276 } 277 rh->cb (rh->cb_cls, 278 &rr); 279 TALER_BANK_registration_cancel (rh); 280 } 281 282 283 struct TALER_BANK_RegistrationHandle * 284 TALER_BANK_registration ( 285 struct GNUNET_CURL_Context *ctx, 286 const char *base_url, 287 const struct TALER_FullPayto *credit_account, 288 const struct TALER_Amount *credit_amount, 289 enum TALER_BankRegistrationType type, 290 const union TALER_AccountPublicKeyP *account_pub, 291 const struct TALER_PreparedTransferAuthorizationPrivateKeyP * 292 authorization_priv, 293 bool recurrent, 294 TALER_BANK_RegistrationCallback res_cb, 295 void *res_cb_cls) 296 { 297 struct TALER_PreparedTransferAuthorizationPublicKeyP authorization_pub; 298 struct TALER_PreparedTransferAuthorizationSignatureP authorization_sig; 299 struct TALER_BANK_RegistrationHandle *rh; 300 const char *type_str; 301 json_t *reg_obj; 302 CURL *eh; 303 304 TALER_wallet_prepared_transfer_registration_sign ( 305 *credit_account, 306 credit_amount, 307 type == 308 TALER_BANK_REGISTRATION_TYPE_RESERVE, 309 recurrent, 310 account_pub, 311 authorization_priv, 312 &authorization_sig); 313 GNUNET_CRYPTO_eddsa_key_get_public (&authorization_priv->eddsa_priv, 314 &authorization_pub.eddsa_pub); 315 316 switch (type) 317 { 318 case TALER_BANK_REGISTRATION_TYPE_RESERVE: 319 type_str = "reserve"; 320 break; 321 case TALER_BANK_REGISTRATION_TYPE_KYC: 322 type_str = "kyc"; 323 break; 324 default: 325 GNUNET_break (0); 326 return NULL; 327 } 328 329 reg_obj = GNUNET_JSON_PACK ( 330 TALER_JSON_pack_full_payto ("credit_account", 331 *credit_account), 332 TALER_JSON_pack_amount ("credit_amount", 333 credit_amount), 334 GNUNET_JSON_pack_string ("type", 335 type_str), 336 GNUNET_JSON_pack_string ("alg", 337 "EdDSA"), 338 GNUNET_JSON_pack_data_auto ("account_pub", 339 account_pub), 340 GNUNET_JSON_pack_data_auto ("authorization_pub", 341 &authorization_pub), 342 GNUNET_JSON_pack_data_auto ("authorization_sig", 343 &authorization_sig), 344 GNUNET_JSON_pack_bool ("recurrent", 345 recurrent)); 346 rh = GNUNET_new (struct TALER_BANK_RegistrationHandle); 347 rh->cb = res_cb; 348 rh->cb_cls = res_cb_cls; 349 rh->request_url = TALER_url_join (base_url, 350 "registration", 351 NULL); 352 if (NULL == rh->request_url) 353 { 354 GNUNET_free (rh); 355 json_decref (reg_obj); 356 return NULL; 357 } 358 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 359 "Requesting wire transfer subject registration at `%s'\n", 360 rh->request_url); 361 eh = curl_easy_init (); 362 if ( (NULL == eh) || 363 (CURLE_OK != 364 curl_easy_setopt (eh, 365 CURLOPT_URL, 366 rh->request_url)) || 367 (GNUNET_OK != 368 TALER_curl_easy_post (&rh->post_ctx, 369 eh, 370 reg_obj)) ) 371 { 372 GNUNET_break (0); 373 TALER_BANK_registration_cancel (rh); 374 if (NULL != eh) 375 curl_easy_cleanup (eh); 376 json_decref (reg_obj); 377 return NULL; 378 } 379 json_decref (reg_obj); 380 rh->job = GNUNET_CURL_job_add2 (ctx, 381 eh, 382 rh->post_ctx.headers, 383 &handle_registration_finished, 384 rh); 385 return rh; 386 } 387 388 389 void 390 TALER_BANK_registration_cancel ( 391 struct TALER_BANK_RegistrationHandle *rh) 392 { 393 if (NULL != rh->job) 394 { 395 GNUNET_CURL_job_cancel (rh->job); 396 rh->job = NULL; 397 } 398 TALER_curl_easy_post_finished (&rh->post_ctx); 399 GNUNET_free (rh->request_url); 400 GNUNET_free (rh); 401 } 402 403 404 /* end of bank_api_registration.c */