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 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 470 : orders_traits (void *cls,
166 : const void **ret,
167 : const char *trait,
168 : unsigned int index)
169 : {
170 470 : struct OrdersState *ps = cls;
171 : struct TALER_TESTING_Trait traits[] = {
172 470 : TALER_TESTING_make_trait_order_id (ps->order_id),
173 470 : TALER_TESTING_make_trait_contract_terms (ps->contract_terms),
174 470 : TALER_TESTING_make_trait_order_terms (ps->order_terms),
175 470 : TALER_TESTING_make_trait_h_contract_terms (&ps->h_contract_terms),
176 470 : TALER_TESTING_make_trait_merchant_sig (&ps->merchant_sig),
177 470 : TALER_TESTING_make_trait_merchant_pub (&ps->merchant_pub),
178 470 : TALER_TESTING_make_trait_claim_nonce (&ps->nonce),
179 470 : TALER_TESTING_make_trait_claim_token (&ps->claim_token),
180 470 : TALER_TESTING_trait_end ()
181 : };
182 :
183 470 : 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 39 : orders_claim_cb (void *cls,
200 : const struct TALER_MERCHANT_OrderClaimResponse *ocr)
201 : {
202 39 : struct OrdersState *ps = cls;
203 : const char *error_name;
204 : unsigned int error_line;
205 : struct GNUNET_JSON_Specification spec[] = {
206 39 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
207 : &ps->merchant_pub),
208 39 : GNUNET_JSON_spec_end ()
209 : };
210 :
211 39 : ps->och = NULL;
212 39 : if (ps->http_status != ocr->hr.http_status)
213 : {
214 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
215 : "Expected status %u, got %u\n",
216 : ps->http_status,
217 : ocr->hr.http_status);
218 0 : TALER_TESTING_FAIL (ps->is);
219 : }
220 39 : if (MHD_HTTP_OK != ocr->hr.http_status)
221 : {
222 0 : TALER_TESTING_interpreter_next (ps->is);
223 0 : return;
224 : }
225 78 : ps->contract_terms = json_deep_copy (
226 39 : (json_t *) ocr->details.ok.contract_terms);
227 39 : ps->h_contract_terms = ocr->details.ok.h_contract_terms;
228 39 : ps->merchant_sig = ocr->details.ok.sig;
229 39 : if (GNUNET_OK !=
230 39 : GNUNET_JSON_parse (ps->contract_terms,
231 : spec,
232 : &error_name,
233 : &error_line))
234 : {
235 : char *log;
236 :
237 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
238 : "Parser failed on %s:%u\n",
239 : error_name,
240 : error_line);
241 0 : log = json_dumps (ps->contract_terms,
242 : JSON_INDENT (1));
243 0 : fprintf (stderr,
244 : "%s\n",
245 : log);
246 0 : free (log);
247 0 : TALER_TESTING_FAIL (ps->is);
248 : }
249 39 : 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 55 : order_cb (void *cls,
263 : const struct TALER_MERCHANT_PostOrdersReply *por)
264 : {
265 55 : struct OrdersState *ps = cls;
266 :
267 55 : ps->po = NULL;
268 55 : if (ps->http_status != por->hr.http_status)
269 : {
270 0 : TALER_TESTING_unexpected_status_with_body (ps->is,
271 : por->hr.http_status,
272 : ps->http_status,
273 : por->hr.reply);
274 0 : TALER_TESTING_interpreter_fail (ps->is);
275 0 : return;
276 : }
277 55 : switch (por->hr.http_status)
278 : {
279 0 : case 0:
280 0 : TALER_LOG_DEBUG ("/orders, expected 0 status code\n");
281 0 : TALER_TESTING_interpreter_next (ps->is);
282 0 : return;
283 43 : case MHD_HTTP_OK:
284 43 : if (NULL != por->details.ok.token)
285 39 : ps->claim_token = *por->details.ok.token;
286 43 : ps->order_id = GNUNET_strdup (por->details.ok.order_id);
287 43 : if ((NULL != ps->expected_order_id) &&
288 37 : (0 != strcmp (por->details.ok.order_id,
289 : ps->expected_order_id)))
290 : {
291 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
292 : "Order id assigned does not match\n");
293 0 : TALER_TESTING_interpreter_fail (ps->is);
294 0 : return;
295 : }
296 43 : if (NULL != ps->duplicate_of)
297 : {
298 : const struct TALER_TESTING_Command *order_cmd;
299 : const struct TALER_ClaimTokenP *prev_token;
300 2 : struct TALER_ClaimTokenP zero_token = {0};
301 :
302 2 : order_cmd = TALER_TESTING_interpreter_lookup_command (
303 : ps->is,
304 : ps->duplicate_of);
305 2 : if (GNUNET_OK !=
306 2 : TALER_TESTING_get_trait_claim_token (order_cmd,
307 : &prev_token))
308 : {
309 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
310 : "Could not fetch previous order claim token\n");
311 0 : TALER_TESTING_interpreter_fail (ps->is);
312 0 : return;
313 : }
314 2 : if (NULL == por->details.ok.token)
315 0 : prev_token = &zero_token;
316 2 : if (0 != GNUNET_memcmp (prev_token,
317 : por->details.ok.token))
318 : {
319 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
320 : "Claim tokens for identical requests do not match\n");
321 0 : TALER_TESTING_interpreter_fail (ps->is);
322 0 : return;
323 : }
324 : }
325 43 : break;
326 4 : case MHD_HTTP_NOT_FOUND:
327 4 : TALER_TESTING_interpreter_next (ps->is);
328 4 : return;
329 2 : case MHD_HTTP_GONE:
330 2 : TALER_TESTING_interpreter_next (ps->is);
331 2 : return;
332 4 : case MHD_HTTP_CONFLICT:
333 4 : TALER_TESTING_interpreter_next (ps->is);
334 4 : return;
335 2 : default:
336 : {
337 2 : char *s = json_dumps (por->hr.reply,
338 : JSON_COMPACT);
339 2 : 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 2 : free (s);
346 : /**
347 : * Not failing, as test cases are _supposed_
348 : * to create non 200 OK situations.
349 : */
350 2 : TALER_TESTING_interpreter_next (ps->is);
351 : }
352 2 : return;
353 : }
354 :
355 43 : if (! ps->with_claim)
356 : {
357 4 : TALER_TESTING_interpreter_next (ps->is);
358 4 : return;
359 : }
360 39 : if (NULL ==
361 39 : (ps->och = TALER_MERCHANT_order_claim (
362 : TALER_TESTING_interpreter_get_context (ps->is),
363 : ps->merchant_url,
364 : ps->order_id,
365 39 : &ps->nonce,
366 39 : &ps->claim_token,
367 : &orders_claim_cb,
368 : ps)))
369 0 : 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 24 : orders_run (void *cls,
382 : const struct TALER_TESTING_Command *cmd,
383 : struct TALER_TESTING_Interpreter *is)
384 : {
385 24 : struct OrdersState *ps = cls;
386 :
387 24 : ps->is = is;
388 24 : if (NULL == json_object_get (ps->order_terms,
389 : "order_id"))
390 : {
391 : struct GNUNET_TIME_Absolute now;
392 : char *order_id;
393 :
394 0 : now = GNUNET_TIME_absolute_get_monotonic (ps->cfg);
395 0 : order_id = GNUNET_STRINGS_data_to_string_alloc (
396 : &now,
397 : sizeof (now));
398 0 : GNUNET_assert (0 ==
399 : json_object_set_new (ps->order_terms,
400 : "order_id",
401 : json_string (order_id)));
402 0 : GNUNET_free (order_id);
403 : }
404 24 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
405 24 : &ps->nonce,
406 : sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
407 24 : ps->po = TALER_MERCHANT_orders_post (TALER_TESTING_interpreter_get_context (
408 : is),
409 : ps->merchant_url,
410 24 : ps->order_terms,
411 24 : GNUNET_TIME_UNIT_ZERO,
412 : &order_cb,
413 : ps);
414 24 : GNUNET_assert (NULL != ps->po);
415 24 : }
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 23 : orders_run2 (void *cls,
427 : const struct TALER_TESTING_Command *cmd,
428 : struct TALER_TESTING_Interpreter *is)
429 : {
430 23 : struct OrdersState *ps = cls;
431 : const json_t *order;
432 23 : char *products_string = GNUNET_strdup (ps->products);
433 23 : char *locks_string = GNUNET_strdup (ps->locks);
434 : char *token;
435 23 : struct TALER_MERCHANT_InventoryProduct *products = NULL;
436 23 : unsigned int products_length = 0;
437 23 : const char **locks = NULL;
438 23 : unsigned int locks_length = 0;
439 :
440 23 : ps->is = is;
441 23 : if (NULL != ps->duplicate_of)
442 : {
443 : const struct TALER_TESTING_Command *order_cmd;
444 : const json_t *ct;
445 :
446 2 : order_cmd = TALER_TESTING_interpreter_lookup_command (
447 : is,
448 : ps->duplicate_of);
449 2 : if (GNUNET_OK !=
450 2 : TALER_TESTING_get_trait_order_terms (order_cmd,
451 : &ct))
452 : {
453 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454 : "Could not fetch previous order string\n");
455 0 : TALER_TESTING_interpreter_fail (is);
456 0 : return;
457 : }
458 2 : order = (json_t *) ct;
459 : }
460 : else
461 : {
462 21 : if (NULL == json_object_get (ps->order_terms,
463 : "order_id"))
464 : {
465 : struct GNUNET_TIME_Absolute now;
466 : char *order_id;
467 :
468 0 : now = GNUNET_TIME_absolute_get_monotonic (ps->cfg);
469 0 : order_id = GNUNET_STRINGS_data_to_string_alloc (
470 : &now.abs_value_us,
471 : sizeof (now.abs_value_us));
472 0 : GNUNET_assert (0 ==
473 : json_object_set_new (ps->order_terms,
474 : "order_id",
475 : json_string (order_id)));
476 0 : GNUNET_free (order_id);
477 : }
478 21 : order = ps->order_terms;
479 : }
480 23 : if (NULL == order)
481 : {
482 0 : GNUNET_break (0);
483 0 : TALER_TESTING_interpreter_fail (is);
484 0 : return;
485 : }
486 :
487 23 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
488 23 : &ps->nonce,
489 : sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
490 23 : for (token = strtok (products_string, ";");
491 37 : NULL != token;
492 14 : token = strtok (NULL, ";"))
493 : {
494 : char *ctok;
495 : struct TALER_MERCHANT_InventoryProduct pd;
496 14 : double quantity_double = 0.0;
497 :
498 : /* Token syntax is "[product_id]/[quantity]" */
499 14 : ctok = strchr (token, '/');
500 14 : if (NULL != ctok)
501 : {
502 12 : *ctok = '\0';
503 12 : ctok++;
504 : {
505 : char *endptr;
506 :
507 12 : quantity_double = strtod (ctok,
508 : &endptr);
509 12 : if ( (endptr == ctok) || ('\0' != *endptr) ||
510 12 : (! isfinite (quantity_double)) || (quantity_double < 0.0))
511 : {
512 0 : GNUNET_break (0);
513 0 : break;
514 : }
515 : }
516 : }
517 : else
518 : {
519 2 : quantity_double = 1.0;
520 : }
521 14 : if (quantity_double <= 0.0)
522 : {
523 0 : GNUNET_break (0);
524 0 : break;
525 : }
526 :
527 : {
528 : double quantity_floor;
529 : double frac;
530 : uint64_t quantity_int;
531 14 : uint32_t quantity_frac_local = 0;
532 : long long scaled;
533 :
534 14 : quantity_floor = floor (quantity_double);
535 14 : frac = quantity_double - quantity_floor;
536 14 : quantity_int = (uint64_t) quantity_floor;
537 14 : if (frac < 0.0)
538 : {
539 0 : GNUNET_break (0);
540 0 : break;
541 : }
542 14 : scaled = llround (frac * (double) MERCHANT_UNIT_FRAC_BASE);
543 14 : if (scaled < 0)
544 : {
545 0 : GNUNET_break (0);
546 0 : break;
547 : }
548 14 : if (scaled >= (long long) MERCHANT_UNIT_FRAC_BASE)
549 : {
550 0 : quantity_int++;
551 0 : scaled -= MERCHANT_UNIT_FRAC_BASE;
552 : }
553 14 : quantity_frac_local = (uint32_t) scaled;
554 14 : pd.quantity = quantity_int;
555 14 : pd.quantity_frac = quantity_frac_local;
556 14 : pd.use_fractional_quantity = (0 != quantity_frac_local);
557 : }
558 14 : pd.product_id = token;
559 :
560 14 : GNUNET_array_append (products,
561 : products_length,
562 : pd);
563 : }
564 23 : for (token = strtok (locks_string, ";");
565 25 : NULL != token;
566 2 : token = strtok (NULL, ";"))
567 : {
568 : const struct TALER_TESTING_Command *lock_cmd;
569 : const char *uuid;
570 :
571 2 : lock_cmd = TALER_TESTING_interpreter_lookup_command (
572 : is,
573 : token);
574 :
575 2 : if (GNUNET_OK !=
576 2 : TALER_TESTING_get_trait_lock_uuid (lock_cmd,
577 : &uuid))
578 : {
579 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
580 : "Could not fetch lock uuid\n");
581 0 : TALER_TESTING_interpreter_fail (is);
582 0 : return;
583 : }
584 :
585 2 : GNUNET_array_append (locks,
586 : locks_length,
587 : uuid);
588 : }
589 23 : ps->po = TALER_MERCHANT_orders_post2 (
590 : TALER_TESTING_interpreter_get_context (
591 : is),
592 : ps->merchant_url,
593 : order,
594 23 : GNUNET_TIME_UNIT_ZERO,
595 : ps->payment_target,
596 : products_length,
597 : products,
598 : locks_length,
599 : locks,
600 23 : ps->make_claim_token,
601 : &order_cb,
602 : ps);
603 23 : GNUNET_free (products_string);
604 23 : GNUNET_free (locks_string);
605 23 : GNUNET_array_grow (products,
606 : products_length,
607 : 0);
608 23 : GNUNET_array_grow (locks,
609 : locks_length,
610 : 0);
611 23 : 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 8 : orders_run3 (void *cls,
624 : const struct TALER_TESTING_Command *cmd,
625 : struct TALER_TESTING_Interpreter *is)
626 : {
627 8 : struct OrdersState *ps = cls;
628 : struct GNUNET_TIME_Absolute now;
629 :
630 8 : ps->is = is;
631 8 : now = GNUNET_TIME_absolute_get_monotonic (ps->cfg);
632 8 : if (NULL == json_object_get (ps->order_terms,
633 : "order_id"))
634 : {
635 : char *order_id;
636 :
637 0 : order_id = GNUNET_STRINGS_data_to_string_alloc (
638 : &now,
639 : sizeof (now));
640 0 : GNUNET_assert (0 ==
641 : json_object_set_new (ps->order_terms,
642 : "order_id",
643 : json_string (order_id)));
644 0 : GNUNET_free (order_id);
645 : }
646 :
647 8 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
648 8 : &ps->nonce,
649 : sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
650 8 : ps->po = TALER_MERCHANT_orders_post (TALER_TESTING_interpreter_get_context (
651 : is),
652 : ps->merchant_url,
653 8 : ps->order_terms,
654 8 : GNUNET_TIME_UNIT_ZERO,
655 : &order_cb,
656 : ps);
657 8 : GNUNET_assert (NULL != ps->po);
658 8 : }
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 55 : orders_cleanup (void *cls,
670 : const struct TALER_TESTING_Command *cmd)
671 : {
672 55 : struct OrdersState *ps = cls;
673 :
674 55 : if (NULL != ps->po)
675 : {
676 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
677 : "Command '%s' did not complete (orders put)\n",
678 : cmd->label);
679 0 : TALER_MERCHANT_orders_post_cancel (ps->po);
680 0 : ps->po = NULL;
681 : }
682 :
683 55 : if (NULL != ps->och)
684 : {
685 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
686 : "Command '%s' did not complete (orders lookup)\n",
687 : cmd->label);
688 0 : TALER_MERCHANT_order_claim_cancel (ps->och);
689 0 : ps->och = NULL;
690 : }
691 :
692 55 : json_decref (ps->contract_terms);
693 55 : json_decref (ps->order_terms);
694 55 : GNUNET_free_nz ((void *) ps->order_id);
695 55 : GNUNET_free (ps);
696 55 : }
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 220 : mark_forgettable (void *cls,
708 : const char *object_id,
709 : json_t *parent)
710 : {
711 220 : GNUNET_assert (GNUNET_OK ==
712 : TALER_JSON_contract_mark_forgettable (parent,
713 : object_id));
714 220 : }
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 55 : 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 55 : struct GNUNET_TIME_Timestamp refund = refund_deadline;
734 55 : 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 55 : 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 55 : GNUNET_assert (GNUNET_OK ==
752 : TALER_JSON_expand_path (contract_terms,
753 : "$.dummy_obj",
754 : &mark_forgettable,
755 : NULL));
756 55 : GNUNET_assert (GNUNET_OK ==
757 : TALER_JSON_expand_path (contract_terms,
758 : "$.dummy_array[*].item",
759 : &mark_forgettable,
760 : NULL));
761 55 : *order = contract_terms;
762 55 : }
763 :
764 :
765 : struct TALER_TESTING_Command
766 4 : 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 4 : ps = GNUNET_new (struct OrdersState);
778 4 : make_order_json (order_id,
779 : refund_deadline,
780 : pay_deadline,
781 : amount,
782 : &ps->order_terms);
783 4 : ps->http_status = http_status;
784 4 : ps->expected_order_id = order_id;
785 4 : ps->merchant_url = merchant_url;
786 : {
787 4 : 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 4 : return cmd;
796 : }
797 : }
798 :
799 :
800 : struct TALER_TESTING_Command
801 16 : 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 16 : ps = GNUNET_new (struct OrdersState);
814 16 : ps->cfg = cfg;
815 16 : make_order_json (order_id,
816 : refund_deadline,
817 : pay_deadline,
818 : amount,
819 : &ps->order_terms);
820 16 : ps->http_status = http_status;
821 16 : ps->expected_order_id = order_id;
822 16 : ps->merchant_url = merchant_url;
823 16 : ps->with_claim = true;
824 : {
825 16 : 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 16 : return cmd;
834 : }
835 : }
836 :
837 :
838 : struct TALER_TESTING_Command
839 23 : 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 23 : ps = GNUNET_new (struct OrdersState);
857 23 : ps->cfg = cfg;
858 23 : make_order_json (order_id,
859 : refund_deadline,
860 : pay_deadline,
861 : amount,
862 : &ps->order_terms);
863 23 : ps->http_status = http_status;
864 23 : ps->expected_order_id = order_id;
865 23 : ps->merchant_url = merchant_url;
866 23 : ps->payment_target = payment_target;
867 23 : ps->products = products;
868 23 : ps->locks = locks;
869 23 : ps->with_claim = (NULL == duplicate_of);
870 23 : ps->make_claim_token = claim_token;
871 23 : ps->duplicate_of = duplicate_of;
872 : {
873 23 : 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 23 : return cmd;
882 : }
883 : }
884 :
885 :
886 : struct TALER_TESTING_Command
887 4 : 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 4 : ps = GNUNET_new (struct OrdersState);
901 4 : ps->cfg = cfg;
902 4 : make_order_json (order_id,
903 : refund_deadline,
904 : pay_deadline,
905 : amount,
906 : &ps->order_terms);
907 4 : GNUNET_assert (0 ==
908 : json_object_set_new (ps->order_terms,
909 : "fulfillment_url",
910 : json_string (fulfillment_url)));
911 4 : ps->http_status = expected_http_status;
912 4 : ps->merchant_url = merchant_url;
913 4 : ps->with_claim = true;
914 : {
915 4 : 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 4 : return cmd;
924 : }
925 : }
926 :
927 :
928 : struct TALER_TESTING_Command
929 8 : 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 8 : ps = GNUNET_new (struct OrdersState);
952 8 : ps->cfg = cfg;
953 8 : make_order_json (order_id,
954 : refund_deadline,
955 : pay_deadline,
956 : NULL,
957 : &ps->order_terms);
958 8 : GNUNET_assert (GNUNET_OK ==
959 : TALER_string_to_amount (amount,
960 : &brutto));
961 8 : inputs = json_array ();
962 8 : GNUNET_assert (NULL != inputs);
963 8 : 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 8 : outputs = json_array ();
975 8 : GNUNET_assert (NULL != outputs);
976 8 : 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 8 : = 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 8 : choices = json_array ();
1002 8 : GNUNET_assert (NULL != choices);
1003 8 : GNUNET_assert (0 ==
1004 : json_array_append_new (
1005 : choices,
1006 : choice));
1007 8 : GNUNET_assert (0 ==
1008 : json_object_set_new (ps->order_terms,
1009 : "choices",
1010 : choices)
1011 : );
1012 8 : GNUNET_assert (0 ==
1013 : json_object_set_new (ps->order_terms,
1014 : "version",
1015 : json_integer (1))
1016 : );
1017 :
1018 :
1019 8 : ps->http_status = http_status;
1020 8 : ps->expected_order_id = order_id;
1021 8 : ps->merchant_url = merchant_url;
1022 8 : ps->with_claim = true;
1023 : {
1024 8 : 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 8 : return cmd;
1033 : }
1034 : }
1035 :
1036 :
1037 : struct TALER_TESTING_Command
1038 0 : 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 0 : ps = GNUNET_new (struct OrdersState);
1055 0 : ps->cfg = cfg;
1056 0 : make_order_json (order_id,
1057 : refund_deadline,
1058 : pay_deadline,
1059 : NULL,
1060 : &ps->order_terms);
1061 0 : GNUNET_assert (GNUNET_OK ==
1062 : TALER_string_to_amount (amount,
1063 : &brutto));
1064 :
1065 0 : outputs = json_array ();
1066 0 : GNUNET_assert (NULL != outputs);
1067 0 : 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 0 : = GNUNET_JSON_PACK (
1076 : TALER_JSON_pack_amount ("amount",
1077 : &brutto),
1078 : GNUNET_JSON_pack_array_steal ("outputs",
1079 : outputs));
1080 0 : choices = json_array ();
1081 0 : GNUNET_assert (NULL != choices);
1082 0 : GNUNET_assert (0 ==
1083 : json_array_append_new (
1084 : choices,
1085 : choice));
1086 0 : GNUNET_assert (0 ==
1087 : json_object_set_new (ps->order_terms,
1088 : "choices",
1089 : choices)
1090 : );
1091 0 : GNUNET_assert (0 ==
1092 : json_object_set_new (ps->order_terms,
1093 : "version",
1094 : json_integer (1))
1095 : );
1096 :
1097 :
1098 0 : ps->http_status = http_status;
1099 0 : ps->expected_order_id = order_id;
1100 0 : ps->merchant_url = merchant_url;
1101 0 : ps->with_claim = true;
1102 : {
1103 0 : 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 0 : return cmd;
1112 : }
1113 : }
|