merchant

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

testing_api_cmd_post_orders.c (36955B)


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