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