merchant

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

testing_api_cmd_post_using_templates.c (25808B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022-2023 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  * @file src/testing/testing_api_cmd_post_using_templates.c
     21  * @brief command to test POST /using-templates
     22  * @author Priscilla HUANG
     23  */
     24 #include "platform.h"
     25 struct PostUsingTemplatesState;
     26 #define TALER_MERCHANT_POST_TEMPLATES_RESULT_CLOSURE struct PostUsingTemplatesState
     27 #define TALER_MERCHANT_POST_ORDERS_CLAIM_RESULT_CLOSURE struct PostUsingTemplatesState
     28 #include <taler/taler_exchange_service.h>
     29 #include <taler/taler_testing_lib.h>
     30 #include "taler/taler_merchant_service.h"
     31 #include "taler/taler_merchant_testing_lib.h"
     32 #include <taler/merchant/post-templates-TEMPLATE_ID.h>
     33 #include <taler/merchant/post-orders-ORDER_ID-claim.h>
     34 
     35 
     36 /**
     37  * State of a "POST /templates" CMD.
     38  */
     39 struct PostUsingTemplatesState
     40 {
     41 
     42   /**
     43    * Handle for a "POST /templates/$TEMPLATE_ID" request.
     44    */
     45   struct TALER_MERCHANT_PostTemplatesHandle *iph;
     46 
     47   /**
     48    * The interpreter state.
     49    */
     50   struct TALER_TESTING_Interpreter *is;
     51 
     52   /**
     53    * The (initial) POST /orders/$ID/claim operation handle.
     54    * The logic is such that after an order creation,
     55    * we immediately claim the order.
     56    */
     57   struct TALER_MERCHANT_PostOrdersClaimHandle *och;
     58 
     59   /**
     60    * Base URL of the merchant serving the request.
     61    */
     62   const char *merchant_url;
     63 
     64   /**
     65    * ID of the using template to run.
     66    */
     67   const char *using_template_id;
     68 
     69   /**
     70    * Summary given by the customer.
     71    */
     72   const char *summary;
     73 
     74   /**
     75    * Amount given by the customer.
     76    */
     77   struct TALER_Amount amount;
     78 
     79   /**
     80    * Raw request body for inventory templates (if set).
     81    */
     82   json_t *details;
     83 
     84   /**
     85    * Label of a command that created the template we should use.
     86    */
     87   const char *template_ref;
     88 
     89   /**
     90    * Order id.
     91    */
     92   char *order_id;
     93 
     94   /**
     95    * The order id we expect the merchant to assign (if not NULL).
     96    */
     97   const char *expected_order_id;
     98 
     99   /**
    100    * Contract terms obtained from the backend.
    101    */
    102   json_t *contract_terms;
    103 
    104   /**
    105    * Order submitted to the backend.
    106    */
    107   json_t *order_terms;
    108 
    109   /**
    110    * Contract terms hash code.
    111    */
    112   struct TALER_PrivateContractHashP h_contract_terms;
    113 
    114   /**
    115    * Merchant signature over the orders.
    116    */
    117   struct TALER_MerchantSignatureP merchant_sig;
    118 
    119   /**
    120    * Merchant public key.
    121    */
    122   struct TALER_MerchantPublicKeyP merchant_pub;
    123 
    124   /**
    125    * The nonce.
    126    */
    127   struct GNUNET_CRYPTO_EddsaPublicKey nonce;
    128 
    129   /**
    130    * The claim token
    131    */
    132   struct TALER_ClaimTokenP claim_token;
    133 
    134   /**
    135    * Pay deadline returned when the template was instantiated.
    136    */
    137   struct GNUNET_TIME_Timestamp pay_deadline;
    138 
    139   /**
    140    * Time when the template instantiation request was started.
    141    */
    142   struct GNUNET_TIME_Absolute request_start;
    143 
    144   /**
    145    * Pay duration configured in the template.
    146    */
    147   struct GNUNET_TIME_Relative pay_duration;
    148 
    149   /**
    150    * Whether @e pay_duration is finite and non-zero and should be checked.
    151    */
    152   bool check_pay_duration;
    153 
    154   /**
    155    * Whether @e pay_duration was explicitly supplied as the expected duration.
    156    */
    157   bool have_expected_pay_duration;
    158 
    159   /**
    160    * Should the command also CLAIM the order?
    161    */
    162   bool with_claim;
    163 
    164   /**
    165    * If not NULL, the command should duplicate the request and verify the
    166    * response is the same as in this command.
    167    */
    168   const char *duplicate_of;
    169 
    170   /**
    171    * Label of command creating/updating OTP device, or NULL.
    172    */
    173   const char *otp_ref;
    174 
    175   /**
    176    * Encoded key for the payment verification.
    177    */
    178   const char *otp_key;
    179 
    180   /**
    181    * Option that add amount of the order
    182    */
    183   const enum TALER_MerchantConfirmationAlgorithm *otp_alg;
    184 
    185   /**
    186    * Public key of the OTP device, for a challenge-signature device.
    187    */
    188   const char *otp_device_pub;
    189 
    190   /**
    191    * Challenge we made up on behalf of an offline verifier, sent with
    192    * the request and expected back signed after payment.  NULL unless
    193    * the template's device signs a challenge.
    194    */
    195   struct TALER_PosChallengeP *pos_challenge;
    196 
    197   /**
    198    * Expected HTTP response code.
    199    */
    200   unsigned int http_status;
    201 
    202 };
    203 
    204 /**
    205  * Used to fill the "using_template" CMD state with backend-provided
    206  * values.  Also double-checks that the using_template was correctly
    207  * created.
    208  *
    209  * @param cls closure
    210  * @param ocr response we got
    211  */
    212 static void
    213 using_claim_cb (struct PostUsingTemplatesState *tis,
    214                 const struct TALER_MERCHANT_PostOrdersClaimResponse *ocr)
    215 {
    216   const char *error_name;
    217   unsigned int error_line;
    218   struct GNUNET_TIME_Timestamp pay_deadline;
    219   struct GNUNET_JSON_Specification spec[] = {
    220     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    221                                  &tis->merchant_pub),
    222     GNUNET_JSON_spec_timestamp ("pay_deadline",
    223                                 &pay_deadline),
    224     GNUNET_JSON_spec_end ()
    225   };
    226 
    227   tis->och = NULL;
    228   if (tis->http_status != ocr->hr.http_status)
    229   {
    230     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    231                 "Expected status %u, got %u\n",
    232                 tis->http_status,
    233                 ocr->hr.http_status);
    234     TALER_TESTING_FAIL (tis->is);
    235   }
    236   if (MHD_HTTP_OK != ocr->hr.http_status)
    237   {
    238     TALER_TESTING_interpreter_next (tis->is);
    239     return;
    240   }
    241   tis->contract_terms = json_deep_copy (
    242     (json_t *) ocr->details.ok.contract_terms);
    243   if (GNUNET_OK !=
    244       TALER_JSON_contract_hash (tis->contract_terms,
    245                                 &tis->h_contract_terms))
    246   {
    247     GNUNET_break (0);
    248     TALER_TESTING_FAIL (tis->is);
    249   }
    250   tis->merchant_sig = ocr->details.ok.merchant_sig;
    251   if (GNUNET_OK !=
    252       GNUNET_JSON_parse (tis->contract_terms,
    253                          spec,
    254                          &error_name,
    255                          &error_line))
    256   {
    257     char *log;
    258 
    259     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    260                 "Parser failed on %s:%u\n",
    261                 error_name,
    262                 error_line);
    263     log = json_dumps (tis->contract_terms,
    264                       JSON_INDENT (1));
    265     fprintf (stderr,
    266              "%s\n",
    267              log);
    268     free (log);
    269     TALER_TESTING_FAIL (tis->is);
    270   }
    271   if (GNUNET_TIME_timestamp_cmp (pay_deadline,
    272                                  !=,
    273                                  tis->pay_deadline))
    274   {
    275     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    276                 "Pay deadline in claimed contract does not match template instantiation response\n");
    277     TALER_TESTING_FAIL (tis->is);
    278   }
    279   TALER_TESTING_interpreter_next (tis->is);
    280 }
    281 
    282 
    283 /**
    284  * Callback for a POST /using-templates operation.
    285  *
    286  * @param cls closure for this function
    287  * @param por response being processed
    288  */
    289 static void
    290 post_using_templates_cb (struct PostUsingTemplatesState *tis,
    291                          const struct TALER_MERCHANT_PostTemplatesResponse *por)
    292 {
    293 
    294   tis->iph = NULL;
    295   if (tis->http_status != por->hr.http_status)
    296   {
    297     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    298                 "Unexpected response code %u (%d) to command %s\n",
    299                 por->hr.http_status,
    300                 (int) por->hr.ec,
    301                 TALER_TESTING_interpreter_get_current_label (tis->is));
    302     TALER_TESTING_interpreter_fail (tis->is);
    303     return;
    304   }
    305   if (0 == tis->http_status)
    306   {
    307     TALER_LOG_DEBUG ("/using_templates, expected 0 status code\n");
    308     TALER_TESTING_interpreter_next (tis->is);
    309     return;
    310   }
    311   // check for order
    312   switch (por->hr.http_status)
    313   {
    314   case MHD_HTTP_OK:
    315     tis->pay_deadline = por->details.ok.pay_deadline;
    316     if (tis->check_pay_duration)
    317     {
    318       struct GNUNET_TIME_Absolute earliest;
    319       struct GNUNET_TIME_Absolute latest;
    320 
    321       earliest = GNUNET_TIME_absolute_subtract (
    322         GNUNET_TIME_absolute_add (tis->request_start,
    323                                   tis->pay_duration),
    324         GNUNET_TIME_UNIT_SECONDS);
    325       latest = GNUNET_TIME_absolute_add (
    326         GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
    327                                   tis->pay_duration),
    328         GNUNET_TIME_UNIT_SECONDS);
    329       if ( (GNUNET_TIME_absolute_cmp (tis->pay_deadline.abs_time,
    330                                       <,
    331                                       earliest)) ||
    332            (GNUNET_TIME_absolute_cmp (tis->pay_deadline.abs_time,
    333                                       >,
    334                                       latest)) )
    335       {
    336         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    337                     "Template pay deadline does not match configured pay duration\n");
    338         TALER_TESTING_interpreter_fail (tis->is);
    339         return;
    340       }
    341     }
    342     if (NULL != por->details.ok.token)
    343       tis->claim_token = *por->details.ok.token;
    344     tis->order_id = GNUNET_strdup (por->details.ok.order_id);
    345     if ((NULL != tis->expected_order_id) &&
    346         (0 != strcmp (por->details.ok.order_id,
    347                       tis->expected_order_id)))
    348     {
    349       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    350                   "Order id assigned does not match\n");
    351       TALER_TESTING_interpreter_fail (tis->is);
    352       return;
    353     }
    354     if (NULL != tis->duplicate_of)
    355     {
    356       const struct TALER_TESTING_Command *order_cmd;
    357       const struct TALER_ClaimTokenP *prev_token;
    358       struct TALER_ClaimTokenP zero_token = {0};
    359       const struct TALER_ClaimTokenP *resp_token;
    360 
    361       order_cmd = TALER_TESTING_interpreter_lookup_command (
    362         tis->is,
    363         tis->duplicate_of);
    364       if (GNUNET_OK !=
    365           TALER_TESTING_get_trait_claim_token (order_cmd,
    366                                                &prev_token))
    367       {
    368         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    369                     "Could not fetch previous order claim token\n");
    370         TALER_TESTING_interpreter_fail (tis->is);
    371         return;
    372       }
    373 
    374       resp_token = (NULL != por->details.ok.token)
    375                    ? por->details.ok.token
    376                    : &zero_token;
    377       if (0 != GNUNET_memcmp (prev_token,
    378                               resp_token))
    379       {
    380         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    381                     "Claim tokens for identical requests do not match\n");
    382         TALER_TESTING_interpreter_fail (tis->is);
    383         return;
    384       }
    385     }
    386     break;
    387   case MHD_HTTP_BAD_REQUEST:
    388   case MHD_HTTP_NOT_FOUND:
    389     TALER_TESTING_interpreter_next (tis->is);
    390     return;
    391   case MHD_HTTP_GONE:
    392     TALER_TESTING_interpreter_next (tis->is);
    393     return;
    394   case MHD_HTTP_CONFLICT:
    395     TALER_TESTING_interpreter_next (tis->is);
    396     return;
    397   default:
    398     {
    399       char *s = json_dumps (por->hr.reply,
    400                             JSON_COMPACT);
    401       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    402                   "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n",
    403                   por->hr.http_status,
    404                   (int) por->hr.ec,
    405                   TALER_TESTING_interpreter_get_current_label (tis->is),
    406                   s);
    407       GNUNET_free (s);
    408       /**
    409        * Not failing, as test cases are _supposed_
    410        * to create non 200 OK situations.
    411        */
    412       TALER_TESTING_interpreter_next (tis->is);
    413     }
    414     return;
    415   }
    416 
    417   if (! tis->with_claim)
    418   {
    419     TALER_TESTING_interpreter_next (tis->is);
    420     return;
    421   }
    422   tis->och = TALER_MERCHANT_post_orders_claim_create (
    423     TALER_TESTING_interpreter_get_context (tis->is),
    424     tis->merchant_url,
    425     tis->order_id,
    426     &tis->nonce);
    427   if (NULL == tis->och)
    428     TALER_TESTING_FAIL (tis->is);
    429   TALER_MERCHANT_post_orders_claim_set_options (
    430     tis->och,
    431     TALER_MERCHANT_post_orders_claim_option_token (
    432       &tis->claim_token));
    433   {
    434     enum TALER_ErrorCode ec;
    435 
    436     ec = TALER_MERCHANT_post_orders_claim_start (tis->och,
    437                                                  &using_claim_cb,
    438                                                  tis);
    439     GNUNET_assert (TALER_EC_NONE == ec);
    440   }
    441 }
    442 
    443 
    444 /**
    445  * Run the "POST /using-templates" CMD.
    446  *
    447  *
    448  * @param cls closure.
    449  * @param cmd command being run now.
    450  * @param is interpreter state.
    451  */
    452 static void
    453 post_using_templates_run (void *cls,
    454                           const struct TALER_TESTING_Command *cmd,
    455                           struct TALER_TESTING_Interpreter *is)
    456 {
    457   struct PostUsingTemplatesState *tis = cls;
    458   const struct TALER_TESTING_Command *ref;
    459   const char *template_id;
    460   const json_t *template_contract;
    461 
    462   tis->is = is;
    463   ref = TALER_TESTING_interpreter_lookup_command (is,
    464                                                   tis->template_ref);
    465   if (GNUNET_OK !=
    466       TALER_TESTING_get_trait_template_id (ref,
    467                                            &template_id))
    468     TALER_TESTING_FAIL (is);
    469   if (GNUNET_OK !=
    470       TALER_TESTING_get_trait_template_contract (ref,
    471                                                  &template_contract))
    472     TALER_TESTING_FAIL (is);
    473   if (tis->have_expected_pay_duration)
    474   {
    475     tis->check_pay_duration
    476       = (! GNUNET_TIME_relative_is_zero (tis->pay_duration)) &&
    477         (! GNUNET_TIME_relative_is_forever (tis->pay_duration));
    478   }
    479   else if (NULL !=
    480            json_object_get (template_contract,
    481                             "pay_duration"))
    482   {
    483     struct GNUNET_JSON_Specification spec[] = {
    484       GNUNET_JSON_spec_relative_time ("pay_duration",
    485                                       &tis->pay_duration),
    486       GNUNET_JSON_spec_end ()
    487     };
    488 
    489     GNUNET_assert (GNUNET_OK ==
    490                    GNUNET_JSON_parse ((json_t *) template_contract,
    491                                       spec,
    492                                       NULL,
    493                                       NULL));
    494     tis->check_pay_duration
    495       = (! GNUNET_TIME_relative_is_zero (tis->pay_duration)) &&
    496         (! GNUNET_TIME_relative_is_forever (tis->pay_duration));
    497   }
    498   if (NULL != tis->otp_ref)
    499   {
    500     ref = TALER_TESTING_interpreter_lookup_command (is,
    501                                                     tis->otp_ref);
    502     if (GNUNET_OK !=
    503         TALER_TESTING_get_trait_otp_key (ref,
    504                                          &tis->otp_key))
    505       TALER_TESTING_FAIL (is);
    506     if (GNUNET_OK !=
    507         TALER_TESTING_get_trait_otp_alg (ref,
    508                                          &tis->otp_alg))
    509       TALER_TESTING_FAIL (is);
    510     /* only challenge-signature devices have one */
    511     if (GNUNET_OK !=
    512         TALER_TESTING_get_trait_otp_device_pub (ref,
    513                                                 &tis->otp_device_pub))
    514       tis->otp_device_pub = NULL;
    515   }
    516   GNUNET_CRYPTO_random_block (&tis->nonce,
    517                               sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
    518   if ( (NULL != tis->otp_alg) &&
    519        ( (TALER_MCA_ECDSA_CHALLENGE == *tis->otp_alg) ||
    520          (TALER_MCA_EDDSA_CHALLENGE == *tis->otp_alg) ) )
    521   {
    522     /* stand in for the offline verifier: pick an unpredictable
    523        challenge that the backend must sign after payment */
    524     tis->pos_challenge = GNUNET_new (struct TALER_PosChallengeP);
    525     GNUNET_CRYPTO_random_block (tis->pos_challenge,
    526                                 sizeof (*tis->pos_challenge));
    527   }
    528   tis->iph = TALER_MERCHANT_post_templates_create (
    529     TALER_TESTING_interpreter_get_context (is),
    530     tis->merchant_url,
    531     template_id);
    532   GNUNET_assert (NULL != tis->iph);
    533   if (NULL != tis->details)
    534   {
    535     TALER_MERCHANT_post_templates_set_options (
    536       tis->iph,
    537       TALER_MERCHANT_post_templates_option_details (
    538         tis->details));
    539   }
    540   else
    541   {
    542     if (NULL != tis->summary)
    543       TALER_MERCHANT_post_templates_set_options (
    544         tis->iph,
    545         TALER_MERCHANT_post_templates_option_summary (
    546           tis->summary));
    547     if (TALER_amount_is_valid (&tis->amount))
    548       TALER_MERCHANT_post_templates_set_options (
    549         tis->iph,
    550         TALER_MERCHANT_post_templates_option_amount (
    551           &tis->amount));
    552   }
    553   if (NULL != tis->pos_challenge)
    554     TALER_MERCHANT_post_templates_set_options (
    555       tis->iph,
    556       TALER_MERCHANT_post_templates_option_challenge (
    557         tis->pos_challenge));
    558   {
    559     enum TALER_ErrorCode ec;
    560 
    561     tis->request_start = GNUNET_TIME_absolute_get ();
    562     ec = TALER_MERCHANT_post_templates_start (tis->iph,
    563                                               &post_using_templates_cb,
    564                                               tis);
    565     GNUNET_assert (TALER_EC_NONE == ec);
    566   }
    567 }
    568 
    569 
    570 /**
    571  * Offers information from the POST /using-templates CMD state to other
    572  * commands.
    573  *
    574  * @param cls closure
    575  * @param[out] ret result (could be anything)
    576  * @param trait name of the trait
    577  * @param index index number of the object to extract.
    578  * @return #GNUNET_OK on success
    579  */
    580 static enum GNUNET_GenericReturnValue
    581 post_using_templates_traits (void *cls,
    582                              const void **ret,
    583                              const char *trait,
    584                              unsigned int index)
    585 {
    586   struct PostUsingTemplatesState *pts = cls;
    587   struct TALER_TESTING_Trait traits[] = {
    588     TALER_TESTING_make_trait_order_id (pts->order_id),
    589     TALER_TESTING_make_trait_contract_terms (pts->contract_terms),
    590     TALER_TESTING_make_trait_order_terms (pts->order_terms),
    591     TALER_TESTING_make_trait_h_contract_terms (&pts->h_contract_terms),
    592     TALER_TESTING_make_trait_merchant_sig (&pts->merchant_sig),
    593     TALER_TESTING_make_trait_merchant_pub (&pts->merchant_pub),
    594     TALER_TESTING_make_trait_claim_nonce (&pts->nonce),
    595     TALER_TESTING_make_trait_claim_token (&pts->claim_token),
    596     TALER_TESTING_make_trait_otp_key (pts->otp_key),
    597     TALER_TESTING_make_trait_otp_alg (pts->otp_alg),
    598     TALER_TESTING_make_trait_otp_device_pub (pts->otp_device_pub),
    599     TALER_TESTING_make_trait_pos_challenge (pts->pos_challenge),
    600     TALER_TESTING_trait_end (),
    601   };
    602 
    603   (void) pts;
    604   return TALER_TESTING_get_trait (traits,
    605                                   ret,
    606                                   trait,
    607                                   index);
    608 }
    609 
    610 
    611 /**
    612  * Free the state of a "POST using-template" CMD, and possibly
    613  * cancel a pending operation thereof.
    614  *
    615  * @param cls closure.
    616  * @param cmd command being run.
    617  */
    618 static void
    619 post_using_templates_cleanup (void *cls,
    620                               const struct TALER_TESTING_Command *cmd)
    621 {
    622   struct PostUsingTemplatesState *tis = cls;
    623 
    624   if (NULL != tis->iph)
    625   {
    626     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    627                 "POST /using-templates operation did not complete\n");
    628     TALER_MERCHANT_post_templates_cancel (tis->iph);
    629   }
    630   json_decref (tis->order_terms);
    631   json_decref (tis->contract_terms);
    632   json_decref (tis->details);
    633   GNUNET_free (tis->order_id);
    634   GNUNET_free (tis->pos_challenge);
    635   GNUNET_free (tis);
    636 }
    637 
    638 
    639 /**
    640  * Mark part of the contract terms as possible to forget.
    641  *
    642  * @param cls pointer to the result of the forget operation.
    643  * @param object_id name of the object to forget.
    644  * @param parent parent of the object at @e object_id.
    645  */
    646 static void
    647 mark_forgettable (void *cls,
    648                   const char *object_id,
    649                   json_t *parent)
    650 {
    651   GNUNET_assert (GNUNET_OK ==
    652                  TALER_JSON_contract_mark_forgettable (parent,
    653                                                        object_id));
    654 }
    655 
    656 
    657 /**
    658  * Constructs the json for a POST using template request.
    659  *
    660  * @param using_template_id the name of the using_template to add, can be NULL.
    661  * @param refund_deadline the deadline for refunds on this using template.
    662  * @param pay_deadline the deadline for payment on this using template.
    663  * @param amount the amount this using template is for.
    664  * @param[out] using_template where to write the json string.
    665  */
    666 static void
    667 make_order_json (const char *using_template_id,
    668                  struct GNUNET_TIME_Timestamp refund_deadline,
    669                  struct GNUNET_TIME_Timestamp pay_deadline,
    670                  const char *amount,
    671                  json_t **using_template)
    672 {
    673   struct GNUNET_TIME_Timestamp refund = refund_deadline;
    674   struct GNUNET_TIME_Timestamp pay = pay_deadline;
    675   json_t *contract_terms;
    676   struct TALER_Amount tamount;
    677   json_t *arr;
    678 
    679   if (NULL != amount)
    680     GNUNET_assert (GNUNET_OK ==
    681                    TALER_string_to_amount (amount,
    682                                            &tamount));
    683   /* Include required fields and some dummy objects to test forgetting. */
    684   arr = json_array ();
    685   GNUNET_assert (NULL != arr);
    686   GNUNET_assert (0 ==
    687                  json_array_append_new (
    688                    arr,
    689                    GNUNET_JSON_PACK (
    690                      GNUNET_JSON_pack_string (
    691                        "item", "speakers"))));
    692   GNUNET_assert (0 ==
    693                  json_array_append_new (
    694                    arr,
    695                    GNUNET_JSON_PACK (
    696                      GNUNET_JSON_pack_string (
    697                        "item", "headphones"))));
    698   GNUNET_assert (0 ==
    699                  json_array_append_new (
    700                    arr,
    701                    GNUNET_JSON_PACK (
    702                      GNUNET_JSON_pack_string (
    703                        "item", "earbuds"))));
    704   contract_terms = GNUNET_JSON_PACK (
    705     GNUNET_JSON_pack_string ("summary",
    706                              "merchant-lib testcase"),
    707     GNUNET_JSON_pack_allow_null (
    708       GNUNET_JSON_pack_string (
    709         "using_template_id", using_template_id)),
    710     GNUNET_JSON_pack_conditional (
    711       NULL != amount,
    712       TALER_JSON_pack_amount ("amount",
    713                               &tamount)),
    714     GNUNET_JSON_pack_string ("fulfillment_url",
    715                              "https://example.com"),
    716     GNUNET_JSON_pack_allow_null (
    717       GNUNET_JSON_pack_timestamp ("refund_deadline",
    718                                   refund)),
    719     GNUNET_JSON_pack_allow_null (
    720       GNUNET_JSON_pack_timestamp ("pay_deadline",
    721                                   pay)),
    722     GNUNET_JSON_pack_string ("dummy_obj",
    723                              "EUR:1.0"),
    724     GNUNET_JSON_pack_array_steal ("dummy_array",
    725                                   arr));
    726   GNUNET_assert (GNUNET_OK ==
    727                  TALER_JSON_expand_path (contract_terms,
    728                                          "$.dummy_obj",
    729                                          &mark_forgettable,
    730                                          NULL));
    731   GNUNET_assert (GNUNET_OK ==
    732                  TALER_JSON_expand_path (contract_terms,
    733                                          "$.dummy_array[*].item",
    734                                          &mark_forgettable,
    735                                          NULL));
    736   *using_template = contract_terms;
    737 }
    738 
    739 
    740 struct TALER_TESTING_Command
    741 TALER_TESTING_cmd_merchant_post_using_templates (
    742   const char *label,
    743   const char *template_ref,
    744   const char *otp_ref,
    745   const char *merchant_url,
    746   const char *using_template_id,
    747   const char *summary,
    748   const char *amount,
    749   struct GNUNET_TIME_Timestamp refund_deadline,
    750   struct GNUNET_TIME_Timestamp pay_deadline,
    751   unsigned int http_status)
    752 {
    753   struct PostUsingTemplatesState *tis;
    754 
    755   tis = GNUNET_new (struct PostUsingTemplatesState);
    756   tis->template_ref = template_ref;
    757   tis->otp_ref = otp_ref;
    758   tis->merchant_url = merchant_url;
    759   tis->using_template_id = using_template_id;
    760   tis->http_status = http_status;
    761   tis->summary = summary;
    762   tis->with_claim = true;
    763   make_order_json (using_template_id,
    764                    refund_deadline,
    765                    pay_deadline,
    766                    amount,
    767                    &tis->order_terms);
    768   if (NULL != amount)
    769     GNUNET_assert (GNUNET_OK ==
    770                    TALER_string_to_amount (amount,
    771                                            &tis->amount));
    772   {
    773     struct TALER_TESTING_Command cmd = {
    774       .cls = tis,
    775       .label = label,
    776       .run = &post_using_templates_run,
    777       .cleanup = &post_using_templates_cleanup,
    778       .traits = &post_using_templates_traits
    779     };
    780 
    781     return cmd;
    782   }
    783 }
    784 
    785 
    786 struct TALER_TESTING_Command
    787 TALER_TESTING_cmd_merchant_post_using_templates_with_expected_pay_duration (
    788   const char *label,
    789   const char *template_ref,
    790   const char *otp_ref,
    791   const char *merchant_url,
    792   const char *using_template_id,
    793   const char *summary,
    794   const char *amount,
    795   struct GNUNET_TIME_Timestamp refund_deadline,
    796   struct GNUNET_TIME_Timestamp pay_deadline,
    797   struct GNUNET_TIME_Relative expected_pay_duration,
    798   unsigned int http_status)
    799 {
    800   struct TALER_TESTING_Command cmd;
    801   struct PostUsingTemplatesState *tis;
    802 
    803   cmd = TALER_TESTING_cmd_merchant_post_using_templates (
    804     label,
    805     template_ref,
    806     otp_ref,
    807     merchant_url,
    808     using_template_id,
    809     summary,
    810     amount,
    811     refund_deadline,
    812     pay_deadline,
    813     http_status);
    814   tis = cmd.cls;
    815   tis->pay_duration = expected_pay_duration;
    816   tis->have_expected_pay_duration = true;
    817   return cmd;
    818 }
    819 
    820 
    821 struct TALER_TESTING_Command
    822 TALER_TESTING_cmd_merchant_post_using_templates2 (
    823   const char *label,
    824   const char *template_ref,
    825   const char *otp_ref,
    826   const char *merchant_url,
    827   const char *using_template_id,
    828   const json_t *details,
    829   unsigned int http_status)
    830 {
    831   struct PostUsingTemplatesState *tis;
    832 
    833   tis = GNUNET_new (struct PostUsingTemplatesState);
    834   tis->template_ref = template_ref;
    835   tis->otp_ref = otp_ref;
    836   tis->merchant_url = merchant_url;
    837   tis->using_template_id = using_template_id;
    838   tis->http_status = http_status;
    839   tis->with_claim = true;
    840   if (NULL != details)
    841     tis->details = json_incref ((json_t *) details);
    842   {
    843     struct TALER_TESTING_Command cmd = {
    844       .cls = tis,
    845       .label = label,
    846       .run = &post_using_templates_run,
    847       .cleanup = &post_using_templates_cleanup,
    848       .traits = &post_using_templates_traits
    849     };
    850 
    851     return cmd;
    852   }
    853 }
    854 
    855 
    856 /* end of testing_api_cmd_post_using_templates.c */