merchant

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

testing_api_cmd_post_orders.c (32590B)


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