testing_api_cmd_post_transfers.c (12847B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020, 2023, 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing_api_cmd_post_transfers.c 21 * @brief command to test POST /transfers 22 * @author Christian Grothoff 23 */ 24 #include "platform.h" 25 #include <taler/taler_exchange_service.h> 26 #include <taler/taler_testing_lib.h> 27 #include "taler_merchant_service.h" 28 #include "taler_merchant_testing_lib.h" 29 30 31 /** 32 * State of a "POST /transfers" CMD. 33 */ 34 struct PostTransfersState 35 { 36 37 /** 38 * Handle for a "POST /transfers" request. 39 */ 40 struct TALER_MERCHANT_PostTransfersHandle *pth; 41 42 /** 43 * Handle for a "GET" bank account history request. 44 */ 45 struct TALER_BANK_DebitHistoryHandle *dhh; 46 47 /** 48 * The interpreter state. 49 */ 50 struct TALER_TESTING_Interpreter *is; 51 52 /** 53 * Base URL of the merchant serving the request. 54 */ 55 const char *merchant_url; 56 57 /** 58 * URL of the bank to run history on. 59 */ 60 char *exchange_url; 61 62 /** 63 * Credit account of the merchant. 64 */ 65 struct TALER_FullPayto credit_account; 66 67 /** 68 * Payto URI to filter on. 69 */ 70 struct TALER_FullPayto payto_uri; 71 72 /** 73 * Set to the hash of the @e payto_uri. 74 */ 75 struct TALER_FullPaytoHashP h_payto; 76 77 /** 78 * Set to the hash of the normalized @e payto_uri. 79 */ 80 struct TALER_NormalizedPaytoHashP h_normalized_payto; 81 82 /** 83 * Authentication details to authenticate to the bank. 84 */ 85 struct TALER_BANK_AuthenticationData auth; 86 87 /** 88 * Set once we discovered the WTID. 89 */ 90 struct TALER_WireTransferIdentifierRawP wtid; 91 92 /** 93 * the credit amount to look for at @e bank_url. 94 */ 95 struct TALER_Amount credit_amount; 96 97 /** 98 * Expected HTTP response code. 99 */ 100 unsigned int http_status; 101 102 /** 103 * Array of deposit command labels we expect to see aggregated. 104 */ 105 const char **deposits; 106 107 /** 108 * Serial number of the wire transfer in the merchant backend, 109 * set by #TALER_TESTING_cmd_merchant_get_transfers(). 0 if unknown. 110 */ 111 uint64_t serial; 112 113 /** 114 * Length of @e deposits. 115 */ 116 unsigned int deposits_length; 117 118 }; 119 120 121 /** 122 * Callback for a POST /transfers operation. 123 * 124 * @param cls closure for this function 125 * @param ptr response details 126 */ 127 static void 128 transfers_cb (void *cls, 129 const struct TALER_MERCHANT_PostTransfersResponse *ptr) 130 { 131 struct PostTransfersState *pts = cls; 132 133 pts->pth = NULL; 134 if (pts->http_status != ptr->hr.http_status) 135 { 136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 137 "Unexpected response code %u (%d) to command %s\n", 138 ptr->hr.http_status, 139 (int) ptr->hr.ec, 140 TALER_TESTING_interpreter_get_current_label (pts->is)); 141 GNUNET_break (0); 142 TALER_TESTING_interpreter_fail (pts->is); 143 return; 144 } 145 switch (ptr->hr.http_status) 146 { 147 case MHD_HTTP_NO_CONTENT: 148 break; 149 case MHD_HTTP_UNAUTHORIZED: 150 break; 151 case MHD_HTTP_NOT_FOUND: 152 break; 153 case MHD_HTTP_GATEWAY_TIMEOUT: 154 break; 155 default: 156 GNUNET_break (0); 157 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 158 "Unhandled HTTP status %u for POST /transfers.\n", 159 ptr->hr.http_status); 160 } 161 TALER_TESTING_interpreter_next (pts->is); 162 } 163 164 165 /** 166 * Offers information from the POST /transfers CMD state to other 167 * commands. 168 * 169 * @param cls closure 170 * @param[out] ret result (could be anything) 171 * @param trait name of the trait 172 * @param index index number of the object to extract. 173 * @return #GNUNET_OK on success 174 */ 175 static enum GNUNET_GenericReturnValue 176 post_transfers_traits (void *cls, 177 const void **ret, 178 const char *trait, 179 unsigned int index) 180 { 181 struct PostTransfersState *pts = cls; 182 struct TALER_TESTING_Trait traits[] = { 183 TALER_TESTING_make_trait_wtid (&pts->wtid), 184 TALER_TESTING_make_trait_credit_payto_uri (&pts->credit_account), 185 TALER_TESTING_make_trait_h_full_payto (&pts->h_payto), 186 TALER_TESTING_make_trait_h_normalized_payto (&pts->h_normalized_payto), 187 TALER_TESTING_make_trait_amount (&pts->credit_amount), 188 TALER_TESTING_make_trait_exchange_url (pts->exchange_url), 189 TALER_TESTING_make_trait_bank_row (&pts->serial), 190 TALER_TESTING_trait_end (), 191 }; 192 193 return TALER_TESTING_get_trait (traits, 194 ret, 195 trait, 196 index); 197 } 198 199 200 /** 201 * Run the "POST /transfers" CMD. First, get the bank history to find 202 * the wtid. 203 * 204 * @param cls closure. 205 * @param cmd command being run now. 206 * @param is interpreter state. 207 */ 208 static void 209 post_transfers_run2 (void *cls, 210 const struct TALER_TESTING_Command *cmd, 211 struct TALER_TESTING_Interpreter *is) 212 { 213 struct PostTransfersState *pts = cls; 214 215 pts->is = is; 216 pts->pth = TALER_MERCHANT_transfers_post ( 217 TALER_TESTING_interpreter_get_context (pts->is), 218 pts->merchant_url, 219 &pts->credit_amount, 220 &pts->wtid, 221 pts->credit_account, 222 pts->exchange_url, 223 &transfers_cb, 224 pts); 225 GNUNET_assert (NULL != pts->pth); 226 } 227 228 229 /** 230 * Callbacks of this type are used to serve the result of asking 231 * the bank for the debit transaction history. 232 * 233 * @param cls closure with a `struct PostTransfersState *` 234 * @param reply details from the HTTP response code 235 */ 236 static void 237 debit_cb ( 238 void *cls, 239 const struct TALER_BANK_DebitHistoryResponse *reply) 240 { 241 struct PostTransfersState *pts = cls; 242 243 pts->dhh = NULL; 244 switch (reply->http_status) 245 { 246 case MHD_HTTP_OK: 247 /* handled below */ 248 break; 249 case MHD_HTTP_NO_CONTENT: 250 GNUNET_break (0); 251 TALER_TESTING_interpreter_fail (pts->is); 252 return; 253 default: 254 GNUNET_break (0); 255 TALER_TESTING_interpreter_fail (pts->is); 256 return; 257 } 258 for (unsigned int i = 0; i<reply->details.ok.details_length; i++) 259 { 260 const struct TALER_BANK_DebitDetails *details 261 = &reply->details.ok.details[i]; 262 263 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 264 "Bank reports transfer of %s to %s\n", 265 TALER_amount2s (&details->amount), 266 details->credit_account_uri.full_payto); 267 if (0 != TALER_amount_cmp (&pts->credit_amount, 268 &details->amount)) 269 continue; 270 pts->wtid = details->wtid; 271 pts->credit_account.full_payto 272 = GNUNET_strdup (details->credit_account_uri.full_payto); 273 pts->exchange_url = GNUNET_strdup (details->exchange_base_url); 274 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 275 "Bank transfer found, checking with merchant backend at %s about %s from %s to %s with %s\n", 276 pts->merchant_url, 277 TALER_amount2s (&pts->credit_amount), 278 pts->payto_uri.full_payto, 279 pts->exchange_url, 280 TALER_B2S (&pts->wtid)); 281 pts->pth = TALER_MERCHANT_transfers_post ( 282 TALER_TESTING_interpreter_get_context (pts->is), 283 pts->merchant_url, 284 &pts->credit_amount, 285 &pts->wtid, 286 pts->credit_account, 287 pts->exchange_url, 288 &transfers_cb, 289 pts); 290 GNUNET_assert (NULL != pts->pth); 291 break; 292 } 293 if (NULL == pts->pth) 294 { 295 GNUNET_break (0); 296 TALER_TESTING_interpreter_fail (pts->is); 297 return; 298 } 299 } 300 301 302 /** 303 * Run the "POST /transfers" CMD. First, get the bank history to find 304 * the wtid. 305 * 306 * @param cls closure. 307 * @param cmd command being run now. 308 * @param is interpreter state. 309 */ 310 static void 311 post_transfers_run (void *cls, 312 const struct TALER_TESTING_Command *cmd, 313 struct TALER_TESTING_Interpreter *is) 314 { 315 struct PostTransfersState *pts = cls; 316 317 pts->is = is; 318 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 319 "Looking for transfer of %s from %s at bank\n", 320 TALER_amount2s (&pts->credit_amount), 321 pts->payto_uri.full_payto); 322 pts->dhh = TALER_BANK_debit_history (TALER_TESTING_interpreter_get_context ( 323 is), 324 &pts->auth, 325 UINT64_MAX, 326 -INT64_MAX, 327 GNUNET_TIME_UNIT_ZERO, 328 &debit_cb, 329 pts); 330 GNUNET_assert (NULL != pts->dhh); 331 } 332 333 334 /** 335 * Free the state of a "POST product" CMD, and possibly 336 * cancel a pending operation thereof. 337 * 338 * @param cls closure. 339 * @param cmd command being run. 340 */ 341 static void 342 post_transfers_cleanup (void *cls, 343 const struct TALER_TESTING_Command *cmd) 344 { 345 struct PostTransfersState *pts = cls; 346 347 if (NULL != pts->pth) 348 { 349 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 350 "POST /transfers operation did not complete\n"); 351 TALER_MERCHANT_transfers_post_cancel (pts->pth); 352 } 353 if (NULL != pts->dhh) 354 { 355 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 356 "GET debit history operation did not complete\n"); 357 TALER_BANK_debit_history_cancel (pts->dhh); 358 } 359 GNUNET_array_grow (pts->deposits, 360 pts->deposits_length, 361 0); 362 GNUNET_free (pts->exchange_url); 363 GNUNET_free (pts->credit_account.full_payto); 364 GNUNET_free (pts); 365 } 366 367 368 struct TALER_TESTING_Command 369 TALER_TESTING_cmd_merchant_post_transfer ( 370 const char *label, 371 const struct TALER_BANK_AuthenticationData *auth, 372 struct TALER_FullPayto payto_uri, 373 const char *merchant_url, 374 const char *credit_amount, 375 unsigned int http_code, 376 ...) 377 { 378 struct PostTransfersState *pts; 379 380 pts = GNUNET_new (struct PostTransfersState); 381 pts->merchant_url = merchant_url; 382 pts->auth = *auth; 383 pts->payto_uri = payto_uri; 384 TALER_full_payto_hash (payto_uri, 385 &pts->h_payto); 386 TALER_full_payto_normalize_and_hash (payto_uri, 387 &pts->h_normalized_payto); 388 GNUNET_assert (GNUNET_OK == 389 TALER_string_to_amount (credit_amount, 390 &pts->credit_amount)); 391 pts->http_status = http_code; 392 { 393 const char *clabel; 394 va_list ap; 395 396 va_start (ap, http_code); 397 while (NULL != (clabel = va_arg (ap, const char *))) 398 { 399 GNUNET_array_append (pts->deposits, 400 pts->deposits_length, 401 clabel); 402 } 403 va_end (ap); 404 } 405 { 406 struct TALER_TESTING_Command cmd = { 407 .cls = pts, 408 .label = label, 409 .run = &post_transfers_run, 410 .cleanup = &post_transfers_cleanup, 411 .traits = &post_transfers_traits 412 }; 413 414 return cmd; 415 } 416 } 417 418 419 struct TALER_TESTING_Command 420 TALER_TESTING_cmd_merchant_post_transfer2 ( 421 const char *label, 422 const char *merchant_url, 423 struct TALER_FullPayto payto_uri, 424 const char *credit_amount, 425 const char *wtid, 426 const char *exchange_url, 427 unsigned int http_code) 428 { 429 struct PostTransfersState *pts; 430 431 pts = GNUNET_new (struct PostTransfersState); 432 pts->merchant_url = merchant_url; 433 pts->credit_account.full_payto 434 = GNUNET_strdup (payto_uri.full_payto); 435 pts->exchange_url = GNUNET_strdup (exchange_url); 436 GNUNET_assert (GNUNET_OK == 437 TALER_string_to_amount (credit_amount, 438 &pts->credit_amount)); 439 if (NULL == wtid) 440 { 441 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 442 &pts->wtid, 443 sizeof (pts->wtid)); 444 } 445 else 446 { 447 GNUNET_assert (GNUNET_OK == 448 GNUNET_STRINGS_string_to_data (wtid, 449 strlen (wtid), 450 &pts->wtid, 451 sizeof (pts->wtid))); 452 } 453 pts->http_status = http_code; 454 { 455 struct TALER_TESTING_Command cmd = { 456 .cls = pts, 457 .label = label, 458 .run = &post_transfers_run2, 459 .cleanup = &post_transfers_cleanup, 460 .traits = &post_transfers_traits 461 }; 462 463 return cmd; 464 } 465 } 466 467 468 void 469 TALER_TESTING_cmd_merchant_post_transfer_set_serial ( 470 struct TALER_TESTING_Command *cmd, 471 uint64_t serial) 472 { 473 struct PostTransfersState *pts = cmd->cls; 474 475 GNUNET_assert (cmd->run = &post_transfers_run); 476 pts->serial = serial; 477 } 478 479 480 /* end of testing_api_cmd_post_transfers.c */