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