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