testing_api_cmd_post_orders.c (36955B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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 /** 21 * @file src/testing/testing_api_cmd_post_orders.c 22 * @brief command to run POST /orders 23 * @author Marcello Stanisci 24 */ 25 26 #include "platform.h" 27 struct OrdersState; 28 #define TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE struct OrdersState 29 #define TALER_MERCHANT_POST_ORDERS_CLAIM_RESULT_CLOSURE struct OrdersState 30 #include <gnunet/gnunet_common.h> 31 #include <gnunet/gnunet_time_lib.h> 32 #include <jansson.h> 33 #include <stdint.h> 34 #include "taler/taler_merchant_util.h" 35 #include <stdlib.h> 36 #include <math.h> 37 #include <taler/taler_exchange_service.h> 38 #include <taler/taler_testing_lib.h> 39 #include "taler/taler_merchant_service.h" 40 #include "taler/taler_merchant_testing_lib.h" 41 #include <taler/merchant/post-private-orders.h> 42 #include <taler/merchant/post-orders-ORDER_ID-claim.h> 43 44 /** 45 * State for a "POST /orders" CMD. 46 */ 47 struct OrdersState 48 { 49 50 /** 51 * Expected status code. 52 */ 53 unsigned int http_status; 54 55 /** 56 * Order id. 57 */ 58 const char *order_id; 59 60 /** 61 * Our configuration. 62 */ 63 const struct GNUNET_CONFIGURATION_Handle *cfg; 64 65 /** 66 * The order id we expect the merchant to assign (if not NULL). 67 */ 68 const char *expected_order_id; 69 70 /** 71 * Contract terms obtained from the backend. 72 */ 73 json_t *contract_terms; 74 75 /** 76 * Order submitted to the backend. 77 */ 78 json_t *order_terms; 79 80 /** 81 * Contract terms hash code. 82 */ 83 struct TALER_PrivateContractHashP h_contract_terms; 84 85 /** 86 * The /orders operation handle. 87 */ 88 struct TALER_MERCHANT_PostPrivateOrdersHandle *po; 89 90 /** 91 * The (initial) POST /orders/$ID/claim operation handle. 92 * The logic is such that after an order creation, 93 * we immediately claim the order. 94 */ 95 struct TALER_MERCHANT_PostOrdersClaimHandle *och; 96 97 /** 98 * The nonce. 99 */ 100 struct GNUNET_CRYPTO_EddsaPublicKey nonce; 101 102 /** 103 * Whether to generate a claim token. 104 */ 105 bool make_claim_token; 106 107 /** 108 * The claim token 109 */ 110 struct TALER_ClaimTokenP claim_token; 111 112 /** 113 * URL of the merchant backend. 114 */ 115 const char *merchant_url; 116 117 /** 118 * The interpreter state. 119 */ 120 struct TALER_TESTING_Interpreter *is; 121 122 /** 123 * Merchant signature over the orders. 124 */ 125 struct TALER_MerchantSignatureP merchant_sig; 126 127 /** 128 * Merchant public key. 129 */ 130 struct TALER_MerchantPublicKeyP merchant_pub; 131 132 /** 133 * The payment target for the order 134 */ 135 const char *payment_target; 136 137 /** 138 * The products the order is purchasing. 139 */ 140 const char *products; 141 142 /** 143 * The locks that the order should release. 144 */ 145 const char *locks; 146 147 /** 148 * Should the command also CLAIM the order? 149 */ 150 bool with_claim; 151 152 /** 153 * If not NULL, the command should duplicate the request and verify the 154 * response is the same as in this command. 155 */ 156 const char *duplicate_of; 157 158 /** 159 * Optional device association and verification material for direct orders. 160 */ 161 const char *otp_id; 162 const char *otp_ref; 163 const enum TALER_MerchantConfirmationAlgorithm *otp_alg; 164 const char *otp_device_pub; 165 struct TALER_PosChallengeP challenge; 166 bool with_challenge; 167 }; 168 169 170 /** 171 * Offer internal data to other commands. 172 * 173 * @param cls closure 174 * @param[out] ret result (could be anything) 175 * @param trait name of the trait 176 * @param index index number of the object to extract. 177 * @return #GNUNET_OK on success 178 */ 179 static enum GNUNET_GenericReturnValue 180 orders_traits (void *cls, 181 const void **ret, 182 const char *trait, 183 unsigned int index) 184 { 185 struct OrdersState *ps = cls; 186 struct TALER_TESTING_Trait traits[] = { 187 TALER_TESTING_make_trait_order_id (ps->order_id), 188 TALER_TESTING_make_trait_contract_terms (ps->contract_terms), 189 TALER_TESTING_make_trait_order_terms (ps->order_terms), 190 TALER_TESTING_make_trait_h_contract_terms (&ps->h_contract_terms), 191 TALER_TESTING_make_trait_merchant_sig (&ps->merchant_sig), 192 TALER_TESTING_make_trait_merchant_pub (&ps->merchant_pub), 193 TALER_TESTING_make_trait_claim_nonce (&ps->nonce), 194 TALER_TESTING_make_trait_claim_token (&ps->claim_token), 195 TALER_TESTING_make_trait_otp_alg (ps->otp_alg), 196 TALER_TESTING_make_trait_otp_device_pub (ps->otp_device_pub), 197 TALER_TESTING_make_trait_pos_challenge ( 198 ps->with_challenge ? &ps->challenge : NULL), 199 TALER_TESTING_trait_end () 200 }; 201 202 return TALER_TESTING_get_trait (traits, 203 ret, 204 trait, 205 index); 206 } 207 208 209 /** 210 * Used to fill the "orders" CMD state with backend-provided 211 * values. Also double-checks that the order was correctly 212 * created. 213 * 214 * @param cls closure 215 * @param ocr response we got 216 */ 217 static void 218 orders_claim_cb (struct OrdersState *ps, 219 const struct TALER_MERCHANT_PostOrdersClaimResponse *ocr) 220 { 221 const char *error_name; 222 unsigned int error_line; 223 struct GNUNET_JSON_Specification spec[] = { 224 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 225 &ps->merchant_pub), 226 GNUNET_JSON_spec_end () 227 }; 228 229 ps->och = NULL; 230 if (ps->http_status != ocr->hr.http_status) 231 { 232 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 233 "Expected status %u, got %u\n", 234 ps->http_status, 235 ocr->hr.http_status); 236 TALER_TESTING_FAIL (ps->is); 237 } 238 if (MHD_HTTP_OK != ocr->hr.http_status) 239 { 240 TALER_TESTING_interpreter_next (ps->is); 241 return; 242 } 243 ps->contract_terms = json_deep_copy ( 244 (json_t *) ocr->details.ok.contract_terms); 245 GNUNET_assert (GNUNET_OK == 246 TALER_JSON_contract_hash (ps->contract_terms, 247 &ps->h_contract_terms)); 248 ps->merchant_sig = ocr->details.ok.merchant_sig; 249 if (GNUNET_OK != 250 GNUNET_JSON_parse (ps->contract_terms, 251 spec, 252 &error_name, 253 &error_line)) 254 { 255 char *log; 256 257 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 258 "Parser failed on %s:%u\n", 259 error_name, 260 error_line); 261 log = json_dumps (ps->contract_terms, 262 JSON_INDENT (1)); 263 fprintf (stderr, 264 "%s\n", 265 log); 266 free (log); 267 TALER_TESTING_FAIL (ps->is); 268 } 269 TALER_TESTING_interpreter_next (ps->is); 270 } 271 272 273 /** 274 * Callback that processes the response following a POST /orders. NOTE: no 275 * contract terms are included here; they need to be taken via the "orders 276 * lookup" method. 277 * 278 * @param cls closure. 279 * @param por details about the response 280 */ 281 static void 282 order_cb (struct OrdersState *ps, 283 const struct TALER_MERCHANT_PostPrivateOrdersResponse *por) 284 { 285 286 ps->po = NULL; 287 if (ps->http_status != por->hr.http_status) 288 { 289 TALER_TESTING_unexpected_status_with_body (ps->is, 290 por->hr.http_status, 291 ps->http_status, 292 por->hr.reply); 293 return; 294 } 295 switch (por->hr.http_status) 296 { 297 case 0: 298 TALER_LOG_DEBUG ("/orders, expected 0 status code\n"); 299 TALER_TESTING_interpreter_next (ps->is); 300 return; 301 case MHD_HTTP_OK: 302 if (NULL != por->details.ok.token) 303 ps->claim_token = *por->details.ok.token; 304 ps->order_id = GNUNET_strdup (por->details.ok.order_id); 305 if ((NULL != ps->expected_order_id) && 306 (0 != strcmp (por->details.ok.order_id, 307 ps->expected_order_id))) 308 { 309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 310 "Order id assigned does not match\n"); 311 TALER_TESTING_interpreter_fail (ps->is); 312 return; 313 } 314 if (NULL != ps->duplicate_of) 315 { 316 const struct TALER_TESTING_Command *order_cmd; 317 const struct TALER_ClaimTokenP *prev_token; 318 struct TALER_ClaimTokenP zero_token = {0}; 319 const struct TALER_ClaimTokenP *resp_token; 320 321 order_cmd = TALER_TESTING_interpreter_lookup_command ( 322 ps->is, 323 ps->duplicate_of); 324 if (GNUNET_OK != 325 TALER_TESTING_get_trait_claim_token (order_cmd, 326 &prev_token)) 327 { 328 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 329 "Could not fetch previous order claim token\n"); 330 TALER_TESTING_interpreter_fail (ps->is); 331 return; 332 } 333 334 resp_token = (NULL != por->details.ok.token) 335 ? por->details.ok.token 336 : &zero_token; 337 if (0 != GNUNET_memcmp (prev_token, 338 resp_token)) 339 { 340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 341 "Claim tokens for identical requests do not match\n"); 342 TALER_TESTING_interpreter_fail (ps->is); 343 return; 344 } 345 } 346 break; 347 case MHD_HTTP_BAD_REQUEST: 348 case MHD_HTTP_NOT_FOUND: 349 TALER_TESTING_interpreter_next (ps->is); 350 return; 351 case MHD_HTTP_GONE: 352 TALER_TESTING_interpreter_next (ps->is); 353 return; 354 case MHD_HTTP_CONFLICT: 355 TALER_TESTING_interpreter_next (ps->is); 356 return; 357 default: 358 { 359 char *s = json_dumps (por->hr.reply, 360 JSON_COMPACT); 361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 362 "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n", 363 por->hr.http_status, 364 (int) por->hr.ec, 365 TALER_TESTING_interpreter_get_current_label (ps->is), 366 s); 367 free (s); 368 /** 369 * Not failing, as test cases are _supposed_ 370 * to create non 200 OK situations. 371 */ 372 TALER_TESTING_interpreter_next (ps->is); 373 } 374 return; 375 } 376 377 if (! ps->with_claim) 378 { 379 TALER_TESTING_interpreter_next (ps->is); 380 return; 381 } 382 ps->och = TALER_MERCHANT_post_orders_claim_create ( 383 TALER_TESTING_interpreter_get_context (ps->is), 384 ps->merchant_url, 385 ps->order_id, 386 &ps->nonce); 387 if (NULL == ps->och) 388 TALER_TESTING_FAIL (ps->is); 389 TALER_MERCHANT_post_orders_claim_set_options ( 390 ps->och, 391 TALER_MERCHANT_post_orders_claim_option_token ( 392 &ps->claim_token)); 393 { 394 enum TALER_ErrorCode ec; 395 396 ec = TALER_MERCHANT_post_orders_claim_start (ps->och, 397 &orders_claim_cb, 398 ps); 399 GNUNET_assert (TALER_EC_NONE == ec); 400 } 401 } 402 403 404 /** 405 * Run a "orders" CMD. 406 * 407 * @param cls closure. 408 * @param cmd command currently being run. 409 * @param is interpreter state. 410 */ 411 static void 412 orders_run (void *cls, 413 const struct TALER_TESTING_Command *cmd, 414 struct TALER_TESTING_Interpreter *is) 415 { 416 struct OrdersState *ps = cls; 417 418 ps->is = is; 419 if (NULL == json_object_get (ps->order_terms, 420 "order_id")) 421 { 422 struct GNUNET_TIME_Absolute now; 423 char *order_id; 424 425 now = GNUNET_TIME_absolute_get_monotonic (ps->cfg); 426 order_id = GNUNET_STRINGS_data_to_string_alloc ( 427 &now, 428 sizeof (now)); 429 GNUNET_assert (0 == 430 json_object_set_new (ps->order_terms, 431 "order_id", 432 json_string (order_id))); 433 GNUNET_free (order_id); 434 } 435 GNUNET_CRYPTO_random_block (&ps->nonce, 436 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); 437 ps->po = TALER_MERCHANT_post_private_orders_create ( 438 TALER_TESTING_interpreter_get_context (is), 439 ps->merchant_url, 440 ps->order_terms); 441 GNUNET_assert (NULL != ps->po); 442 if (NULL != ps->otp_ref) 443 { 444 const struct TALER_TESTING_Command *ref; 445 446 ref = TALER_TESTING_interpreter_lookup_command (is, ps->otp_ref); 447 if ( (GNUNET_OK != TALER_TESTING_get_trait_otp_alg (ref, &ps->otp_alg)) || 448 (GNUNET_OK != TALER_TESTING_get_trait_otp_device_pub ( 449 ref, &ps->otp_device_pub)) ) 450 TALER_TESTING_FAIL (is); 451 } 452 if (NULL != ps->otp_id) 453 TALER_MERCHANT_post_private_orders_set_options ( 454 ps->po, 455 TALER_MERCHANT_post_private_orders_option_otp_id (ps->otp_id)); 456 if (ps->with_challenge) 457 { 458 struct TALER_PosChallengeP temporary; 459 460 GNUNET_CRYPTO_random_block (&ps->challenge, sizeof (ps->challenge)); 461 temporary = ps->challenge; 462 TALER_MERCHANT_post_private_orders_set_options ( 463 ps->po, 464 TALER_MERCHANT_post_private_orders_option_challenge (&temporary)); 465 /* The option promises to copy the challenge. */ 466 memset (&temporary, 0, sizeof (temporary)); 467 } 468 { 469 enum TALER_ErrorCode ec; 470 471 ec = TALER_MERCHANT_post_private_orders_start (ps->po, 472 &order_cb, 473 ps); 474 GNUNET_assert (TALER_EC_NONE == ec); 475 } 476 } 477 478 479 /** 480 * Run a "orders" CMD. 481 * 482 * @param cls closure. 483 * @param cmd command currently being run. 484 * @param is interpreter state. 485 */ 486 static void 487 orders_run2 (void *cls, 488 const struct TALER_TESTING_Command *cmd, 489 struct TALER_TESTING_Interpreter *is) 490 { 491 struct OrdersState *ps = cls; 492 const json_t *order; 493 char *products_string = GNUNET_strdup (ps->products); 494 char *locks_string = GNUNET_strdup (ps->locks); 495 char *token; 496 struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct *products = NULL; 497 unsigned int products_length = 0; 498 const char **locks = NULL; 499 unsigned int locks_length = 0; 500 501 ps->is = is; 502 if (NULL != ps->duplicate_of) 503 { 504 const struct TALER_TESTING_Command *order_cmd; 505 const json_t *ct; 506 507 order_cmd = TALER_TESTING_interpreter_lookup_command ( 508 is, 509 ps->duplicate_of); 510 if (GNUNET_OK != 511 TALER_TESTING_get_trait_order_terms (order_cmd, 512 &ct)) 513 { 514 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 515 "Could not fetch previous order string\n"); 516 TALER_TESTING_interpreter_fail (is); 517 return; 518 } 519 order = (json_t *) ct; 520 } 521 else 522 { 523 if (NULL == json_object_get (ps->order_terms, 524 "order_id")) 525 { 526 struct GNUNET_TIME_Absolute now; 527 char *order_id; 528 529 now = GNUNET_TIME_absolute_get_monotonic (ps->cfg); 530 order_id = GNUNET_STRINGS_data_to_string_alloc ( 531 &now.abs_value_us, 532 sizeof (now.abs_value_us)); 533 GNUNET_assert (0 == 534 json_object_set_new (ps->order_terms, 535 "order_id", 536 json_string (order_id))); 537 GNUNET_free (order_id); 538 } 539 order = ps->order_terms; 540 } 541 if (NULL == order) 542 { 543 GNUNET_break (0); 544 TALER_TESTING_interpreter_fail (is); 545 return; 546 } 547 548 GNUNET_CRYPTO_random_block (&ps->nonce, 549 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); 550 for (token = strtok (products_string, ";"); 551 NULL != token; 552 token = strtok (NULL, ";")) 553 { 554 char *ctok; 555 struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct pd; 556 double quantity_double = 0.0; 557 558 /* Token syntax is "[product_id]/[quantity]" */ 559 ctok = strchr (token, '/'); 560 if (NULL != ctok) 561 { 562 *ctok = '\0'; 563 ctok++; 564 { 565 char *endptr; 566 567 quantity_double = strtod (ctok, 568 &endptr); 569 if ( (endptr == ctok) || ('\0' != *endptr) || 570 (! isfinite (quantity_double)) || (quantity_double < 0.0)) 571 { 572 GNUNET_break (0); 573 break; 574 } 575 } 576 } 577 else 578 { 579 quantity_double = 1.0; 580 } 581 if (quantity_double <= 0.0) 582 { 583 GNUNET_break (0); 584 break; 585 } 586 587 { 588 double quantity_floor; 589 double frac; 590 uint64_t quantity_int; 591 uint32_t quantity_frac_local = 0; 592 long long scaled; 593 594 quantity_floor = floor (quantity_double); 595 frac = quantity_double - quantity_floor; 596 quantity_int = (uint64_t) quantity_floor; 597 if (frac < 0.0) 598 { 599 GNUNET_break (0); 600 break; 601 } 602 scaled = llround (frac * (double) TALER_MERCHANT_UNIT_FRAC_BASE); 603 if (scaled < 0) 604 { 605 GNUNET_break (0); 606 break; 607 } 608 if (scaled >= (long long) TALER_MERCHANT_UNIT_FRAC_BASE) 609 { 610 quantity_int++; 611 scaled -= TALER_MERCHANT_UNIT_FRAC_BASE; 612 } 613 quantity_frac_local = (uint32_t) scaled; 614 pd.quantity = quantity_int; 615 pd.quantity_frac = quantity_frac_local; 616 pd.use_fractional_quantity = (0 != quantity_frac_local); 617 } 618 pd.product_id = token; 619 620 GNUNET_array_append (products, 621 products_length, 622 pd); 623 } 624 for (token = strtok (locks_string, ";"); 625 NULL != token; 626 token = strtok (NULL, ";")) 627 { 628 const struct TALER_TESTING_Command *lock_cmd; 629 const char *uuid; 630 631 lock_cmd = TALER_TESTING_interpreter_lookup_command ( 632 is, 633 token); 634 635 if (GNUNET_OK != 636 TALER_TESTING_get_trait_lock_uuid (lock_cmd, 637 &uuid)) 638 { 639 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 640 "Could not fetch lock uuid\n"); 641 TALER_TESTING_interpreter_fail (is); 642 return; 643 } 644 645 GNUNET_array_append (locks, 646 locks_length, 647 uuid); 648 } 649 ps->po = TALER_MERCHANT_post_private_orders_create ( 650 TALER_TESTING_interpreter_get_context (is), 651 ps->merchant_url, 652 order); 653 GNUNET_assert (NULL != ps->po); 654 if (NULL != ps->payment_target) 655 TALER_MERCHANT_post_private_orders_set_options ( 656 ps->po, 657 TALER_MERCHANT_post_private_orders_option_payment_target ( 658 ps->payment_target)); 659 if (0 < products_length) 660 TALER_MERCHANT_post_private_orders_set_options ( 661 ps->po, 662 TALER_MERCHANT_post_private_orders_option_inventory_products ( 663 products_length, products)); 664 if (0 < locks_length) 665 TALER_MERCHANT_post_private_orders_set_options ( 666 ps->po, 667 TALER_MERCHANT_post_private_orders_option_lock_uuids ( 668 locks_length, locks)); 669 TALER_MERCHANT_post_private_orders_set_options ( 670 ps->po, 671 TALER_MERCHANT_post_private_orders_option_create_token ( 672 ps->make_claim_token)); 673 { 674 enum TALER_ErrorCode ec; 675 676 ec = TALER_MERCHANT_post_private_orders_start (ps->po, 677 &order_cb, 678 ps); 679 GNUNET_assert (TALER_EC_NONE == ec); 680 } 681 GNUNET_free (products_string); 682 GNUNET_free (locks_string); 683 GNUNET_array_grow (products, 684 products_length, 685 0); 686 GNUNET_array_grow (locks, 687 locks_length, 688 0); 689 } 690 691 692 /** 693 * Run a "orders" CMD. 694 * 695 * @param cls closure. 696 * @param cmd command currently being run. 697 * @param is interpreter state. 698 */ 699 static void 700 orders_run3 (void *cls, 701 const struct TALER_TESTING_Command *cmd, 702 struct TALER_TESTING_Interpreter *is) 703 { 704 struct OrdersState *ps = cls; 705 struct GNUNET_TIME_Absolute now; 706 707 ps->is = is; 708 now = GNUNET_TIME_absolute_get_monotonic (ps->cfg); 709 if (NULL == json_object_get (ps->order_terms, 710 "order_id")) 711 { 712 char *order_id; 713 714 order_id = GNUNET_STRINGS_data_to_string_alloc ( 715 &now, 716 sizeof (now)); 717 GNUNET_assert (0 == 718 json_object_set_new (ps->order_terms, 719 "order_id", 720 json_string (order_id))); 721 GNUNET_free (order_id); 722 } 723 724 GNUNET_CRYPTO_random_block (&ps->nonce, 725 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); 726 ps->po = TALER_MERCHANT_post_private_orders_create ( 727 TALER_TESTING_interpreter_get_context (is), 728 ps->merchant_url, 729 ps->order_terms); 730 GNUNET_assert (NULL != ps->po); 731 { 732 enum TALER_ErrorCode ec; 733 734 ec = TALER_MERCHANT_post_private_orders_start (ps->po, 735 &order_cb, 736 ps); 737 GNUNET_assert (TALER_EC_NONE == ec); 738 } 739 } 740 741 742 /** 743 * Free the state of a "orders" CMD, and possibly 744 * cancel it if it did not complete. 745 * 746 * @param cls closure. 747 * @param cmd command being freed. 748 */ 749 static void 750 orders_cleanup (void *cls, 751 const struct TALER_TESTING_Command *cmd) 752 { 753 struct OrdersState *ps = cls; 754 755 if (NULL != ps->po) 756 { 757 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 758 "Command '%s' did not complete (orders put)\n", 759 cmd->label); 760 TALER_MERCHANT_post_private_orders_cancel (ps->po); 761 ps->po = NULL; 762 } 763 764 if (NULL != ps->och) 765 { 766 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 767 "Command '%s' did not complete (orders lookup)\n", 768 cmd->label); 769 TALER_MERCHANT_post_orders_claim_cancel (ps->och); 770 ps->och = NULL; 771 } 772 773 json_decref (ps->contract_terms); 774 json_decref (ps->order_terms); 775 GNUNET_free_nz ((void *) ps->order_id); 776 GNUNET_free (ps); 777 } 778 779 780 /** 781 * Mark part of the contract terms as possible to forget. 782 * 783 * @param cls pointer to the result of the forget operation. 784 * @param object_id name of the object to forget. 785 * @param parent parent of the object at @e object_id. 786 */ 787 static void 788 mark_forgettable (void *cls, 789 const char *object_id, 790 json_t *parent) 791 { 792 GNUNET_assert (GNUNET_OK == 793 TALER_JSON_contract_mark_forgettable (parent, 794 object_id)); 795 } 796 797 798 /** 799 * Constructs the json for a POST order request. 800 * 801 * @param order_id the name of the order to add, can be NULL. 802 * @param refund_deadline the deadline for refunds on this order. 803 * @param pay_deadline the deadline for payment on this order. 804 * @param amount the amount this order is for, NULL for v1 orders 805 * @param[out] order where to write the json string. 806 */ 807 static void 808 make_order_json (const char *order_id, 809 struct GNUNET_TIME_Timestamp refund_deadline, 810 struct GNUNET_TIME_Timestamp pay_deadline, 811 const char *amount, 812 json_t **order) 813 { 814 struct GNUNET_TIME_Timestamp refund = refund_deadline; 815 struct GNUNET_TIME_Timestamp pay = pay_deadline; 816 json_t *contract_terms; 817 818 /* Include required fields and some dummy objects to test forgetting. */ 819 contract_terms = json_pack ( 820 "{s:s, s:s?, s:s?, s:s, s:o, s:o, s:s, s:[{s:s}, {s:s}, {s:s}]}", 821 "summary", "merchant-lib testcase", 822 "order_id", order_id, 823 "amount", amount, 824 "fulfillment_url", "https://example.com", 825 "refund_deadline", GNUNET_JSON_from_timestamp (refund), 826 "pay_deadline", GNUNET_JSON_from_timestamp (pay), 827 "dummy_obj", "EUR:1.0", 828 "dummy_array", /* For testing forgetting parts of arrays */ 829 "item", "speakers", 830 "item", "headphones", 831 "item", "earbuds"); 832 GNUNET_assert (GNUNET_OK == 833 TALER_JSON_expand_path (contract_terms, 834 "$.dummy_obj", 835 &mark_forgettable, 836 NULL)); 837 GNUNET_assert (GNUNET_OK == 838 TALER_JSON_expand_path (contract_terms, 839 "$.dummy_array[*].item", 840 &mark_forgettable, 841 NULL)); 842 *order = contract_terms; 843 } 844 845 846 struct TALER_TESTING_Command 847 TALER_TESTING_cmd_merchant_post_orders_no_claim ( 848 const char *label, 849 const char *merchant_url, 850 unsigned int http_status, 851 const char *order_id, 852 struct GNUNET_TIME_Timestamp refund_deadline, 853 struct GNUNET_TIME_Timestamp pay_deadline, 854 const char *amount) 855 { 856 struct OrdersState *ps; 857 858 ps = GNUNET_new (struct OrdersState); 859 make_order_json (order_id, 860 refund_deadline, 861 pay_deadline, 862 amount, 863 &ps->order_terms); 864 ps->http_status = http_status; 865 ps->expected_order_id = order_id; 866 ps->merchant_url = merchant_url; 867 { 868 struct TALER_TESTING_Command cmd = { 869 .cls = ps, 870 .label = label, 871 .run = &orders_run, 872 .cleanup = &orders_cleanup, 873 .traits = &orders_traits 874 }; 875 876 return cmd; 877 } 878 } 879 880 881 struct TALER_TESTING_Command 882 TALER_TESTING_cmd_merchant_post_orders_external ( 883 const char *label, 884 const char *merchant_url, 885 unsigned int http_status, 886 const char *order_id, 887 struct GNUNET_TIME_Timestamp refund_deadline, 888 struct GNUNET_TIME_Timestamp pay_deadline, 889 const char *amount, 890 const char *amount_external) 891 { 892 struct OrdersState *ps; 893 894 ps = GNUNET_new (struct OrdersState); 895 make_order_json (order_id, 896 refund_deadline, 897 pay_deadline, 898 amount, 899 &ps->order_terms); 900 { 901 json_t *ae; 902 903 ae = json_loads (amount_external, 904 0, 905 NULL); 906 GNUNET_assert (NULL != ae); 907 GNUNET_assert (0 == 908 json_object_set_new (ps->order_terms, 909 "amount_external", 910 ae)); 911 } 912 ps->http_status = http_status; 913 ps->expected_order_id = order_id; 914 ps->merchant_url = merchant_url; 915 { 916 struct TALER_TESTING_Command cmd = { 917 .cls = ps, 918 .label = label, 919 .run = &orders_run, 920 .cleanup = &orders_cleanup, 921 .traits = &orders_traits 922 }; 923 924 return cmd; 925 } 926 } 927 928 929 struct TALER_TESTING_Command 930 TALER_TESTING_cmd_merchant_post_orders_v1 ( 931 const char *label, 932 const char *merchant_url, 933 unsigned int http_status, 934 const char *order_id, 935 struct GNUNET_TIME_Timestamp refund_deadline, 936 struct GNUNET_TIME_Timestamp pay_deadline, 937 const char *choices) 938 { 939 struct OrdersState *ps; 940 941 ps = GNUNET_new (struct OrdersState); 942 make_order_json (order_id, 943 refund_deadline, 944 pay_deadline, 945 NULL, 946 &ps->order_terms); 947 { 948 json_t *cs; 949 950 cs = json_loads (choices, 951 0, 952 NULL); 953 GNUNET_assert (NULL != cs); 954 GNUNET_assert (0 == 955 json_object_set_new (ps->order_terms, 956 "choices", 957 cs)); 958 } 959 GNUNET_assert (0 == 960 json_object_set_new (ps->order_terms, 961 "version", 962 json_integer (1))); 963 ps->http_status = http_status; 964 ps->expected_order_id = order_id; 965 ps->merchant_url = merchant_url; 966 { 967 struct TALER_TESTING_Command cmd = { 968 .cls = ps, 969 .label = label, 970 .run = &orders_run, 971 .cleanup = &orders_cleanup, 972 .traits = &orders_traits 973 }; 974 975 return cmd; 976 } 977 } 978 979 980 struct TALER_TESTING_Command 981 TALER_TESTING_cmd_merchant_post_orders ( 982 const char *label, 983 const struct GNUNET_CONFIGURATION_Handle *cfg, 984 const char *merchant_url, 985 unsigned int http_status, 986 const char *order_id, 987 struct GNUNET_TIME_Timestamp refund_deadline, 988 struct GNUNET_TIME_Timestamp pay_deadline, 989 const char *amount) 990 { 991 struct OrdersState *ps; 992 993 ps = GNUNET_new (struct OrdersState); 994 ps->cfg = cfg; 995 make_order_json (order_id, 996 refund_deadline, 997 pay_deadline, 998 amount, 999 &ps->order_terms); 1000 ps->http_status = http_status; 1001 ps->expected_order_id = order_id; 1002 ps->merchant_url = merchant_url; 1003 ps->with_claim = true; 1004 { 1005 struct TALER_TESTING_Command cmd = { 1006 .cls = ps, 1007 .label = label, 1008 .run = &orders_run, 1009 .cleanup = &orders_cleanup, 1010 .traits = &orders_traits 1011 }; 1012 1013 return cmd; 1014 } 1015 } 1016 1017 1018 struct TALER_TESTING_Command 1019 TALER_TESTING_cmd_merchant_post_orders2 ( 1020 const char *label, 1021 const struct GNUNET_CONFIGURATION_Handle *cfg, 1022 const char *merchant_url, 1023 unsigned int http_status, 1024 const char *order_id, 1025 struct GNUNET_TIME_Timestamp refund_deadline, 1026 struct GNUNET_TIME_Timestamp pay_deadline, 1027 bool claim_token, 1028 const char *amount, 1029 const char *payment_target, 1030 const char *products, 1031 const char *locks, 1032 const char *duplicate_of) 1033 { 1034 struct OrdersState *ps; 1035 1036 ps = GNUNET_new (struct OrdersState); 1037 ps->cfg = cfg; 1038 make_order_json (order_id, 1039 refund_deadline, 1040 pay_deadline, 1041 amount, 1042 &ps->order_terms); 1043 ps->http_status = http_status; 1044 ps->expected_order_id = order_id; 1045 ps->merchant_url = merchant_url; 1046 ps->payment_target = payment_target; 1047 ps->products = products; 1048 ps->locks = locks; 1049 ps->with_claim = (NULL == duplicate_of); 1050 ps->make_claim_token = claim_token; 1051 ps->duplicate_of = duplicate_of; 1052 { 1053 struct TALER_TESTING_Command cmd = { 1054 .cls = ps, 1055 .label = label, 1056 .run = &orders_run2, 1057 .cleanup = &orders_cleanup, 1058 .traits = &orders_traits 1059 }; 1060 1061 return cmd; 1062 } 1063 } 1064 1065 1066 struct TALER_TESTING_Command 1067 TALER_TESTING_cmd_merchant_post_orders3 ( 1068 const char *label, 1069 const struct GNUNET_CONFIGURATION_Handle *cfg, 1070 const char *merchant_url, 1071 unsigned int expected_http_status, 1072 const char *order_id, 1073 struct GNUNET_TIME_Timestamp refund_deadline, 1074 struct GNUNET_TIME_Timestamp pay_deadline, 1075 const char *fulfillment_url, 1076 const char *amount) 1077 { 1078 struct OrdersState *ps; 1079 1080 ps = GNUNET_new (struct OrdersState); 1081 ps->cfg = cfg; 1082 make_order_json (order_id, 1083 refund_deadline, 1084 pay_deadline, 1085 amount, 1086 &ps->order_terms); 1087 GNUNET_assert (0 == 1088 json_object_set_new (ps->order_terms, 1089 "fulfillment_url", 1090 json_string (fulfillment_url))); 1091 ps->http_status = expected_http_status; 1092 ps->merchant_url = merchant_url; 1093 ps->with_claim = true; 1094 { 1095 struct TALER_TESTING_Command cmd = { 1096 .cls = ps, 1097 .label = label, 1098 .run = &orders_run, 1099 .cleanup = &orders_cleanup, 1100 .traits = &orders_traits 1101 }; 1102 1103 return cmd; 1104 } 1105 } 1106 1107 1108 struct TALER_TESTING_Command 1109 TALER_TESTING_cmd_merchant_post_orders_choices ( 1110 const char *label, 1111 const struct GNUNET_CONFIGURATION_Handle *cfg, 1112 const char *merchant_url, 1113 unsigned int http_status, 1114 const char *token_family_slug, 1115 const char *choice_description, 1116 json_t *choice_description_i18n, 1117 unsigned int num_inputs, 1118 unsigned int num_outputs, 1119 const char *order_id, 1120 struct GNUNET_TIME_Timestamp refund_deadline, 1121 struct GNUNET_TIME_Timestamp pay_deadline, 1122 const char *amount) 1123 { 1124 struct OrdersState *ps; 1125 struct TALER_Amount brutto; 1126 json_t *choice; 1127 json_t *choices; 1128 json_t *inputs; 1129 json_t *outputs; 1130 1131 ps = GNUNET_new (struct OrdersState); 1132 ps->cfg = cfg; 1133 make_order_json (order_id, 1134 refund_deadline, 1135 pay_deadline, 1136 NULL, 1137 &ps->order_terms); 1138 GNUNET_assert (GNUNET_OK == 1139 TALER_string_to_amount (amount, 1140 &brutto)); 1141 inputs = json_array (); 1142 GNUNET_assert (NULL != inputs); 1143 GNUNET_assert (0 == 1144 json_array_append_new ( 1145 inputs, 1146 GNUNET_JSON_PACK ( 1147 GNUNET_JSON_pack_string ("type", 1148 "token"), 1149 GNUNET_JSON_pack_uint64 ("count", 1150 num_inputs), 1151 GNUNET_JSON_pack_string ("token_family_slug", 1152 token_family_slug) 1153 ))); 1154 outputs = json_array (); 1155 GNUNET_assert (NULL != outputs); 1156 GNUNET_assert (0 == 1157 json_array_append_new ( 1158 outputs, 1159 GNUNET_JSON_PACK ( 1160 GNUNET_JSON_pack_string ("type", 1161 "token"), 1162 GNUNET_JSON_pack_uint64 ("count", 1163 num_outputs), 1164 GNUNET_JSON_pack_string ("token_family_slug", 1165 token_family_slug) 1166 ))); 1167 choice 1168 = GNUNET_JSON_PACK ( 1169 TALER_JSON_pack_amount ("amount", 1170 &brutto), 1171 GNUNET_JSON_pack_allow_null ( 1172 GNUNET_JSON_pack_string ("description", 1173 choice_description)), 1174 GNUNET_JSON_pack_allow_null ( 1175 GNUNET_JSON_pack_object_steal ("description_i18n", 1176 choice_description_i18n)), 1177 GNUNET_JSON_pack_array_steal ("inputs", 1178 inputs), 1179 GNUNET_JSON_pack_array_steal ("outputs", 1180 outputs)); 1181 choices = json_array (); 1182 GNUNET_assert (NULL != choices); 1183 GNUNET_assert (0 == 1184 json_array_append_new ( 1185 choices, 1186 choice)); 1187 GNUNET_assert (0 == 1188 json_object_set_new (ps->order_terms, 1189 "choices", 1190 choices) 1191 ); 1192 GNUNET_assert (0 == 1193 json_object_set_new (ps->order_terms, 1194 "version", 1195 json_integer (1)) 1196 ); 1197 1198 1199 ps->http_status = http_status; 1200 ps->expected_order_id = order_id; 1201 ps->merchant_url = merchant_url; 1202 ps->with_claim = true; 1203 { 1204 struct TALER_TESTING_Command cmd = { 1205 .cls = ps, 1206 .label = label, 1207 .run = &orders_run3, 1208 .cleanup = &orders_cleanup, 1209 .traits = &orders_traits 1210 }; 1211 1212 return cmd; 1213 } 1214 } 1215 1216 1217 struct TALER_TESTING_Command 1218 TALER_TESTING_cmd_merchant_post_orders_donau ( 1219 const char *label, 1220 const struct GNUNET_CONFIGURATION_Handle *cfg, 1221 const char *merchant_url, 1222 unsigned int http_status, 1223 const char *order_id, 1224 struct GNUNET_TIME_Timestamp refund_deadline, 1225 struct GNUNET_TIME_Timestamp pay_deadline, 1226 const char *amount) 1227 { 1228 struct OrdersState *ps; 1229 struct TALER_Amount brutto; 1230 json_t *choice; 1231 json_t *choices; 1232 json_t *outputs; 1233 1234 ps = GNUNET_new (struct OrdersState); 1235 ps->cfg = cfg; 1236 make_order_json (order_id, 1237 refund_deadline, 1238 pay_deadline, 1239 NULL, 1240 &ps->order_terms); 1241 GNUNET_assert (GNUNET_OK == 1242 TALER_string_to_amount (amount, 1243 &brutto)); 1244 1245 outputs = json_array (); 1246 GNUNET_assert (NULL != outputs); 1247 GNUNET_assert (0 == 1248 json_array_append_new ( 1249 outputs, 1250 GNUNET_JSON_PACK ( 1251 GNUNET_JSON_pack_string ("type", 1252 "tax-receipt") 1253 ))); 1254 choice 1255 = GNUNET_JSON_PACK ( 1256 TALER_JSON_pack_amount ("amount", 1257 &brutto), 1258 GNUNET_JSON_pack_array_steal ("outputs", 1259 outputs)); 1260 choices = json_array (); 1261 GNUNET_assert (NULL != choices); 1262 GNUNET_assert (0 == 1263 json_array_append_new ( 1264 choices, 1265 choice)); 1266 GNUNET_assert (0 == 1267 json_object_set_new (ps->order_terms, 1268 "choices", 1269 choices) 1270 ); 1271 GNUNET_assert (0 == 1272 json_object_set_new (ps->order_terms, 1273 "version", 1274 json_integer (1)) 1275 ); 1276 1277 1278 ps->http_status = http_status; 1279 ps->expected_order_id = order_id; 1280 ps->merchant_url = merchant_url; 1281 ps->with_claim = true; 1282 { 1283 struct TALER_TESTING_Command cmd = { 1284 .cls = ps, 1285 .label = label, 1286 .run = &orders_run3, 1287 .cleanup = &orders_cleanup, 1288 .traits = &orders_traits 1289 }; 1290 1291 return cmd; 1292 } 1293 } 1294 1295 1296 struct TALER_TESTING_Command 1297 TALER_TESTING_cmd_merchant_post_orders_with_otp ( 1298 struct TALER_TESTING_Command cmd, 1299 const char *otp_id, 1300 const char *otp_ref, 1301 bool with_challenge) 1302 { 1303 struct OrdersState *ps = cmd.cls; 1304 1305 GNUNET_assert (&orders_run == cmd.run); 1306 ps->otp_id = otp_id; 1307 ps->otp_ref = otp_ref; 1308 ps->with_challenge = with_challenge; 1309 return cmd; 1310 }