merchant

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

testing_api_cmd_post_orders.c (32570B)


      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     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       const struct TALER_ClaimTokenP *resp_token;
    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 
    320       resp_token = (NULL != por->details.ok.token)
    321         ? por->details.ok.token
    322         : &zero_token;
    323       if (0 != GNUNET_memcmp (prev_token,
    324                               resp_token))
    325       {
    326         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    327                     "Claim tokens for identical requests do not match\n");
    328         TALER_TESTING_interpreter_fail (ps->is);
    329         return;
    330       }
    331     }
    332     break;
    333   case MHD_HTTP_NOT_FOUND:
    334     TALER_TESTING_interpreter_next (ps->is);
    335     return;
    336   case MHD_HTTP_GONE:
    337     TALER_TESTING_interpreter_next (ps->is);
    338     return;
    339   case MHD_HTTP_CONFLICT:
    340     TALER_TESTING_interpreter_next (ps->is);
    341     return;
    342   default:
    343     {
    344       char *s = json_dumps (por->hr.reply,
    345                             JSON_COMPACT);
    346       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    347                   "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n",
    348                   por->hr.http_status,
    349                   (int) por->hr.ec,
    350                   TALER_TESTING_interpreter_get_current_label (ps->is),
    351                   s);
    352       free (s);
    353       /**
    354        * Not failing, as test cases are _supposed_
    355        * to create non 200 OK situations.
    356        */
    357       TALER_TESTING_interpreter_next (ps->is);
    358     }
    359     return;
    360   }
    361 
    362   if (! ps->with_claim)
    363   {
    364     TALER_TESTING_interpreter_next (ps->is);
    365     return;
    366   }
    367   ps->och = TALER_MERCHANT_post_orders_claim_create (
    368     TALER_TESTING_interpreter_get_context (ps->is),
    369     ps->merchant_url,
    370     ps->order_id,
    371     &ps->nonce);
    372   if (NULL == ps->och)
    373     TALER_TESTING_FAIL (ps->is);
    374   TALER_MERCHANT_post_orders_claim_set_options (
    375     ps->och,
    376     TALER_MERCHANT_post_orders_claim_option_token (
    377       &ps->claim_token));
    378   {
    379     enum TALER_ErrorCode ec;
    380 
    381     ec = TALER_MERCHANT_post_orders_claim_start (ps->och,
    382                                                  &orders_claim_cb,
    383                                                  ps);
    384     GNUNET_assert (TALER_EC_NONE == ec);
    385   }
    386 }
    387 
    388 
    389 /**
    390  * Run a "orders" CMD.
    391  *
    392  * @param cls closure.
    393  * @param cmd command currently being run.
    394  * @param is interpreter state.
    395  */
    396 static void
    397 orders_run (void *cls,
    398             const struct TALER_TESTING_Command *cmd,
    399             struct TALER_TESTING_Interpreter *is)
    400 {
    401   struct OrdersState *ps = cls;
    402 
    403   ps->is = is;
    404   if (NULL == json_object_get (ps->order_terms,
    405                                "order_id"))
    406   {
    407     struct GNUNET_TIME_Absolute now;
    408     char *order_id;
    409 
    410     now = GNUNET_TIME_absolute_get_monotonic (ps->cfg);
    411     order_id = GNUNET_STRINGS_data_to_string_alloc (
    412       &now,
    413       sizeof (now));
    414     GNUNET_assert (0 ==
    415                    json_object_set_new (ps->order_terms,
    416                                         "order_id",
    417                                         json_string (order_id)));
    418     GNUNET_free (order_id);
    419   }
    420   GNUNET_CRYPTO_random_block (&ps->nonce,
    421                               sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
    422   ps->po = TALER_MERCHANT_post_private_orders_create (
    423     TALER_TESTING_interpreter_get_context (is),
    424     ps->merchant_url,
    425     ps->order_terms);
    426   GNUNET_assert (NULL != ps->po);
    427   {
    428     enum TALER_ErrorCode ec;
    429 
    430     ec = TALER_MERCHANT_post_private_orders_start (ps->po,
    431                                                    &order_cb,
    432                                                    ps);
    433     GNUNET_assert (TALER_EC_NONE == ec);
    434   }
    435 }
    436 
    437 
    438 /**
    439  * Run a "orders" CMD.
    440  *
    441  * @param cls closure.
    442  * @param cmd command currently being run.
    443  * @param is interpreter state.
    444  */
    445 static void
    446 orders_run2 (void *cls,
    447              const struct TALER_TESTING_Command *cmd,
    448              struct TALER_TESTING_Interpreter *is)
    449 {
    450   struct OrdersState *ps = cls;
    451   const json_t *order;
    452   char *products_string = GNUNET_strdup (ps->products);
    453   char *locks_string = GNUNET_strdup (ps->locks);
    454   char *token;
    455   struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct *products = NULL;
    456   unsigned int products_length = 0;
    457   const char **locks = NULL;
    458   unsigned int locks_length = 0;
    459 
    460   ps->is = is;
    461   if (NULL != ps->duplicate_of)
    462   {
    463     const struct TALER_TESTING_Command *order_cmd;
    464     const json_t *ct;
    465 
    466     order_cmd = TALER_TESTING_interpreter_lookup_command (
    467       is,
    468       ps->duplicate_of);
    469     if (GNUNET_OK !=
    470         TALER_TESTING_get_trait_order_terms (order_cmd,
    471                                              &ct))
    472     {
    473       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    474                   "Could not fetch previous order string\n");
    475       TALER_TESTING_interpreter_fail (is);
    476       return;
    477     }
    478     order = (json_t *) ct;
    479   }
    480   else
    481   {
    482     if (NULL == json_object_get (ps->order_terms,
    483                                  "order_id"))
    484     {
    485       struct GNUNET_TIME_Absolute now;
    486       char *order_id;
    487 
    488       now = GNUNET_TIME_absolute_get_monotonic (ps->cfg);
    489       order_id = GNUNET_STRINGS_data_to_string_alloc (
    490         &now.abs_value_us,
    491         sizeof (now.abs_value_us));
    492       GNUNET_assert (0 ==
    493                      json_object_set_new (ps->order_terms,
    494                                           "order_id",
    495                                           json_string (order_id)));
    496       GNUNET_free (order_id);
    497     }
    498     order = ps->order_terms;
    499   }
    500   if (NULL == order)
    501   {
    502     GNUNET_break (0);
    503     TALER_TESTING_interpreter_fail (is);
    504     return;
    505   }
    506 
    507   GNUNET_CRYPTO_random_block (&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 (&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 }