merchant

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

testing_api_cmd_post_orders.c (31070B)


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