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