merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

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 }