merchant

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

testing_api_cmd_wallet_get_order.c (24083B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020 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 testing_api_cmd_wallet_get_order.c
     21  * @brief command to test GET /order/$ORDER_ID
     22  * @author Jonathan Buchanan
     23  */
     24 #include "platform.h"
     25 #include <taler/taler_exchange_service.h>
     26 #include <taler/taler_testing_lib.h>
     27 #include "taler_merchant_service.h"
     28 #include "taler_merchant_testing_lib.h"
     29 
     30 
     31 /**
     32  * State for a GET /orders/$ORDER_ID CMD.
     33  */
     34 struct WalletGetOrderState
     35 {
     36   /**
     37    * The merchant base URL.
     38    */
     39   const char *merchant_url;
     40 
     41   /**
     42    * Expected HTTP response code for this CMD.
     43    */
     44   unsigned int http_status;
     45 
     46   /**
     47    * The handle to the current GET /orders/$ORDER_ID request.
     48    */
     49   struct TALER_MERCHANT_OrderWalletGetHandle *ogh;
     50 
     51   /**
     52    * The interpreter state.
     53    */
     54   struct TALER_TESTING_Interpreter *is;
     55 
     56   /**
     57    * Reference to a command that created an order.
     58    */
     59   const char *order_reference;
     60 
     61   /**
     62    * Reference to a command that created a paid
     63    * equivalent order that we expect to be referred
     64    * to during repurchase detection, or NULL.
     65    */
     66   const char *repurchase_order_ref;
     67 
     68   /**
     69    * Session Id the order needs to be bound to.
     70    */
     71   const char *session_id;
     72 
     73   /**
     74    * Whether the order was paid or not.
     75    */
     76   bool paid;
     77 
     78   /**
     79    * Whether the order was refunded or not.
     80    */
     81   bool refunded;
     82 
     83   /**
     84    * Whether the order has refunds pending.
     85    */
     86   bool refund_pending;
     87 };
     88 
     89 
     90 /**
     91  * Callback to process a GET /orders/$ID request
     92  *
     93  * @param cls closure
     94  * @param owgr response details
     95  */
     96 static void
     97 wallet_get_order_cb (
     98   void *cls,
     99   const struct TALER_MERCHANT_OrderWalletGetResponse *owgr)
    100 {
    101   struct WalletGetOrderState *gos = cls;
    102   const struct TALER_MERCHANT_HttpResponse *hr = &owgr->hr;
    103 
    104   gos->ogh = NULL;
    105   if (gos->http_status != hr->http_status)
    106   {
    107     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    108                 "Unexpected response code %u (%d) to command %s\n",
    109                 hr->http_status,
    110                 (int) hr->ec,
    111                 TALER_TESTING_interpreter_get_current_label (gos->is));
    112     TALER_TESTING_interpreter_fail (gos->is);
    113     return;
    114   }
    115   switch (hr->http_status)
    116   {
    117   case MHD_HTTP_OK:
    118     if (gos->refunded != owgr->details.ok.refunded)
    119     {
    120       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    121                   "Order refunded does not match\n");
    122       TALER_TESTING_interpreter_fail (gos->is);
    123       return;
    124     }
    125     if (gos->refund_pending != owgr->details.ok.refund_pending)
    126     {
    127       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    128                   "Order refund pending does not match\n");
    129       TALER_TESTING_interpreter_fail (gos->is);
    130       return;
    131     }
    132     break;
    133   case MHD_HTTP_PAYMENT_REQUIRED:
    134     {
    135       struct TALER_MERCHANT_PayUriData pud;
    136       const struct TALER_TESTING_Command *order_cmd;
    137       const char *order_id;
    138       const struct TALER_ClaimTokenP *claim_token;
    139 
    140       if (NULL != gos->repurchase_order_ref)
    141       {
    142         const struct TALER_TESTING_Command *rep_cmd;
    143         const char *rep_id;
    144         const char *ri;
    145 
    146         rep_cmd = TALER_TESTING_interpreter_lookup_command (
    147           gos->is,
    148           gos->repurchase_order_ref);
    149         if (GNUNET_OK !=
    150             TALER_TESTING_get_trait_order_id (rep_cmd,
    151                                               &rep_id))
    152         {
    153           TALER_TESTING_FAIL (gos->is);
    154         }
    155         ri = owgr->details.payment_required.already_paid_order_id;
    156         if ( (NULL == ri) ||
    157              (0 !=
    158               strcmp (ri,
    159                       rep_id)) )
    160         {
    161           TALER_TESTING_FAIL (gos->is);
    162         }
    163       }
    164 
    165       if (GNUNET_OK !=
    166           TALER_MERCHANT_parse_pay_uri (
    167             owgr->details.payment_required.taler_pay_uri,
    168             &pud))
    169       {
    170         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    171                     "Taler pay uri `%s' is malformed\n",
    172                     owgr->details.payment_required.taler_pay_uri);
    173         TALER_TESTING_interpreter_fail (gos->is);
    174         return;
    175       }
    176 
    177       order_cmd = TALER_TESTING_interpreter_lookup_command (
    178         gos->is,
    179         gos->order_reference);
    180 
    181       if (GNUNET_OK !=
    182           TALER_TESTING_get_trait_order_id (order_cmd,
    183                                             &order_id))
    184       {
    185         TALER_MERCHANT_parse_pay_uri_free (&pud);
    186         TALER_TESTING_FAIL (gos->is);
    187       }
    188 
    189       if (GNUNET_OK !=
    190           TALER_TESTING_get_trait_claim_token (order_cmd,
    191                                                &claim_token))
    192       {
    193         TALER_MERCHANT_parse_pay_uri_free (&pud);
    194         TALER_TESTING_FAIL (gos->is);
    195       }
    196 
    197       {
    198         char *host;
    199 
    200         host = TALER_MERCHANT_TESTING_extract_host (gos->merchant_url);
    201         if ((0 != strcmp (host,
    202                           pud.merchant_host)) ||
    203             (NULL != pud.merchant_prefix_path) ||
    204             (0 != strcmp (order_id,
    205                           pud.order_id)) ||
    206             (NULL != pud.ssid))
    207         {
    208           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    209                       "Order pay uri `%s' does not match `%s'\n",
    210                       owgr->details.payment_required.taler_pay_uri,
    211                       pud.order_id);
    212           TALER_TESTING_interpreter_fail (gos->is);
    213           TALER_MERCHANT_parse_pay_uri_free (&pud);
    214           GNUNET_free (host);
    215           return;
    216         }
    217         GNUNET_free (host);
    218       }
    219       /* The claim token is not given in the pay uri if the order
    220          has been claimed already. */
    221       if ((NULL != pud.claim_token) &&
    222           ((NULL == claim_token) ||
    223            (0 != GNUNET_memcmp (claim_token,
    224                                 pud.claim_token))))
    225       {
    226         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    227                     "Order pay uri claim token does not match (%d/%d)\n",
    228                     NULL == pud.claim_token,
    229                     NULL == claim_token);
    230         TALER_TESTING_interpreter_fail (gos->is);
    231         TALER_MERCHANT_parse_pay_uri_free (&pud);
    232         return;
    233       }
    234       TALER_MERCHANT_parse_pay_uri_free (&pud);
    235     }
    236     break;
    237   default:
    238     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    239                 "Unhandled HTTP status.\n");
    240   }
    241   TALER_TESTING_interpreter_next (gos->is);
    242 }
    243 
    244 
    245 /**
    246  * Run the "GET order" CMD.
    247  *
    248  * @param cls closure.
    249  * @param cmd command being run now.
    250  * @param is interpreter state.
    251  */
    252 static void
    253 wallet_get_order_run (void *cls,
    254                       const struct TALER_TESTING_Command *cmd,
    255                       struct TALER_TESTING_Interpreter *is)
    256 {
    257   struct WalletGetOrderState *gos = cls;
    258   const struct TALER_TESTING_Command *order_cmd;
    259   const char *order_id;
    260   const struct TALER_PrivateContractHashP *h_contract;
    261 
    262   order_cmd = TALER_TESTING_interpreter_lookup_command (
    263     is,
    264     gos->order_reference);
    265 
    266   if (GNUNET_OK !=
    267       TALER_TESTING_get_trait_order_id (order_cmd,
    268                                         &order_id))
    269     TALER_TESTING_FAIL (is);
    270 
    271   if (GNUNET_OK !=
    272       TALER_TESTING_get_trait_h_contract_terms (order_cmd,
    273                                                 &h_contract))
    274     TALER_TESTING_FAIL (is);
    275 
    276   gos->is = is;
    277   gos->ogh = TALER_MERCHANT_wallet_order_get (
    278     TALER_TESTING_interpreter_get_context (is),
    279     gos->merchant_url,
    280     order_id,
    281     h_contract,
    282     GNUNET_TIME_UNIT_ZERO,
    283     gos->session_id,
    284     NULL,
    285     false,
    286     &wallet_get_order_cb,
    287     gos);
    288 }
    289 
    290 
    291 /**
    292  * Free the state of a "GET order" CMD, and possibly
    293  * cancel a pending operation thereof.
    294  *
    295  * @param cls closure.
    296  * @param cmd command being run.
    297  */
    298 static void
    299 wallet_get_order_cleanup (void *cls,
    300                           const struct TALER_TESTING_Command *cmd)
    301 {
    302   struct WalletGetOrderState *gos = cls;
    303 
    304   if (NULL != gos->ogh)
    305   {
    306     TALER_LOG_WARNING ("Get order operation did not complete\n");
    307     TALER_MERCHANT_wallet_order_get_cancel (gos->ogh);
    308   }
    309   GNUNET_free (gos);
    310 }
    311 
    312 
    313 struct TALER_TESTING_Command
    314 TALER_TESTING_cmd_wallet_get_order (
    315   const char *label,
    316   const char *merchant_url,
    317   const char *order_reference,
    318   bool paid,
    319   bool refunded,
    320   bool refund_pending,
    321   unsigned int http_status)
    322 {
    323   struct WalletGetOrderState *gos;
    324 
    325   gos = GNUNET_new (struct WalletGetOrderState);
    326   gos->merchant_url = merchant_url;
    327   gos->order_reference = order_reference;
    328   gos->http_status = http_status;
    329   gos->paid = paid;
    330   gos->refunded = refunded;
    331   gos->refund_pending = refund_pending;
    332   {
    333     struct TALER_TESTING_Command cmd = {
    334       .cls = gos,
    335       .label = label,
    336       .run = &wallet_get_order_run,
    337       .cleanup = &wallet_get_order_cleanup
    338     };
    339 
    340     return cmd;
    341   }
    342 }
    343 
    344 
    345 struct TALER_TESTING_Command
    346 TALER_TESTING_cmd_wallet_get_order2 (
    347   const char *label,
    348   const char *merchant_url,
    349   const char *order_reference,
    350   const char *session_id,
    351   bool paid,
    352   bool refunded,
    353   bool refund_pending,
    354   const char *repurchase_order_ref,
    355   unsigned int http_status)
    356 {
    357   struct WalletGetOrderState *gos;
    358 
    359   gos = GNUNET_new (struct WalletGetOrderState);
    360   gos->merchant_url = merchant_url;
    361   gos->order_reference = order_reference;
    362   gos->http_status = http_status;
    363   gos->paid = paid;
    364   gos->session_id = session_id;
    365   gos->refunded = refunded;
    366   gos->refund_pending = refund_pending;
    367   gos->repurchase_order_ref = repurchase_order_ref;
    368   {
    369     struct TALER_TESTING_Command cmd = {
    370       .cls = gos,
    371       .label = label,
    372       .run = &wallet_get_order_run,
    373       .cleanup = &wallet_get_order_cleanup
    374     };
    375 
    376     return cmd;
    377   }
    378 }
    379 
    380 
    381 struct WalletPollOrderConcludeState
    382 {
    383   /**
    384    * The interpreter state.
    385    */
    386   struct TALER_TESTING_Interpreter *is;
    387 
    388   /**
    389    * Reference to a command that can provide a poll order start command.
    390    */
    391   const char *start_reference;
    392 
    393   /**
    394    * Already paid order ID expected, or NULL for none.
    395    */
    396   const char *already_paid_order_id;
    397 
    398   /**
    399    * Task to wait for the deadline.
    400    */
    401   struct GNUNET_SCHEDULER_Task *task;
    402 
    403   /**
    404    * Amount of a refund expected.
    405    */
    406   struct TALER_Amount expected_refund_amount;
    407 
    408   /**
    409    * Expected HTTP response status code.
    410    */
    411   unsigned int expected_http_status;
    412 
    413   /**
    414    * Are we expecting a refund?
    415    */
    416   bool expected_refund;
    417 };
    418 
    419 
    420 struct WalletPollOrderStartState
    421 {
    422   /**
    423    * The merchant base URL.
    424    */
    425   const char *merchant_url;
    426 
    427   /**
    428    * The handle to the current GET /orders/$ORDER_ID request.
    429    */
    430   struct TALER_MERCHANT_OrderWalletGetHandle *ogh;
    431 
    432   /**
    433    * The interpreter state.
    434    */
    435   struct TALER_TESTING_Interpreter *is;
    436 
    437   /**
    438    * Reference to a command that created an order.
    439    */
    440   const char *order_ref;
    441 
    442   /**
    443    * Which session ID to poll for.
    444    */
    445   const char *session_id;
    446 
    447   /**
    448    * How long to wait for server to return a response.
    449    */
    450   struct GNUNET_TIME_Relative timeout;
    451 
    452   /**
    453    * Conclude state waiting for completion (if any).
    454    */
    455   struct WalletPollOrderConcludeState *cs;
    456 
    457   /**
    458    * The HTTP status code returned by the backend.
    459    */
    460   unsigned int http_status;
    461 
    462   /**
    463    * When the request should be completed by.
    464    */
    465   struct GNUNET_TIME_Absolute deadline;
    466 
    467   /**
    468    * Minimum refund to wait for.
    469    */
    470   struct TALER_Amount refund_threshold;
    471 
    472   /**
    473    * Available refund as returned by the merchant.
    474    */
    475   struct TALER_Amount refund_available;
    476 
    477   /**
    478    * Already paid order ID returned, or NULL for none.
    479    */
    480   char *already_paid_order_id;
    481 
    482   /**
    483    * Should we poll for a refund?
    484    */
    485   bool wait_for_refund;
    486 
    487   /**
    488    * Did we receive a refund according to response from the merchant?
    489    */
    490   bool refunded;
    491 
    492   /**
    493    * Was the order paid according to response from the merchant?
    494    */
    495   bool paid;
    496 
    497   /**
    498    * Has the order a pending refund according to response from the merchant?
    499    */
    500   bool refund_pending;
    501 };
    502 
    503 
    504 /**
    505  * Task called when either the timeout for the GET /private/order/$ID command
    506  * expired or we got a response.  Checks if the result is what we expected.
    507  *
    508  * @param cls a `struct WalletPollOrderConcludeState`
    509  */
    510 static void
    511 conclude_task (void *cls)
    512 {
    513   struct WalletPollOrderConcludeState *ppc = cls;
    514   const struct TALER_TESTING_Command *poll_cmd;
    515   struct WalletPollOrderStartState *cps;
    516   struct GNUNET_TIME_Absolute now;
    517 
    518   ppc->task = NULL;
    519   poll_cmd =
    520     TALER_TESTING_interpreter_lookup_command (ppc->is,
    521                                               ppc->start_reference);
    522   if (NULL == poll_cmd)
    523     TALER_TESTING_FAIL (ppc->is);
    524   cps = poll_cmd->cls;
    525   if (NULL != cps->ogh)
    526   {
    527     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    528                 "Expected poll GET /orders/$ORDER_ID to have completed, but it did not!\n");
    529     TALER_TESTING_FAIL (ppc->is);
    530   }
    531   if (cps->http_status != ppc->expected_http_status)
    532   {
    533     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    534                 "Expected HTTP status %u, got %u\n",
    535                 ppc->expected_http_status,
    536                 cps->http_status);
    537     TALER_TESTING_FAIL (ppc->is);
    538   }
    539   if (ppc->expected_refund != cps->refunded)
    540   {
    541     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    542                 "Order was %srefunded, contrary to our expectations\n",
    543                 cps->refunded ? "" : "NOT ");
    544     TALER_TESTING_FAIL (ppc->is);
    545   }
    546   if ( (NULL == ppc->already_paid_order_id)
    547        ^ (NULL == cps->already_paid_order_id) )
    548   {
    549     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    550                 "Mismatch in already paid order IDs: %s vs %s\n",
    551                 ppc->already_paid_order_id,
    552                 cps->already_paid_order_id);
    553     TALER_TESTING_FAIL (ppc->is);
    554   }
    555   if ( (NULL != ppc->already_paid_order_id) &&
    556        (0 != strcmp (ppc->already_paid_order_id,
    557                      cps->already_paid_order_id) ) )
    558   {
    559     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    560                 "Mismatch in already paid order IDs: %s vs %s\n",
    561                 ppc->already_paid_order_id,
    562                 cps->already_paid_order_id);
    563     TALER_TESTING_FAIL (ppc->is);
    564   }
    565 
    566   if (cps->refunded)
    567   {
    568     if (0 != TALER_amount_cmp (&ppc->expected_refund_amount,
    569                                &cps->refund_available))
    570     {
    571       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    572                   "Refund amount %s does not match our expectation!\n",
    573                   TALER_amount2s (&cps->refund_available));
    574       TALER_TESTING_FAIL (ppc->is);
    575     }
    576   }
    577   // FIXME: add checks for cps->paid/refund_available status flags?
    578   now = GNUNET_TIME_absolute_get ();
    579   if ((GNUNET_TIME_absolute_add (cps->deadline,
    580                                  GNUNET_TIME_UNIT_SECONDS).abs_value_us <
    581        now.abs_value_us) )
    582   {
    583     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    584                 "Expected answer to be delayed until %llu, but got response at %llu\n",
    585                 (unsigned long long) cps->deadline.abs_value_us,
    586                 (unsigned long long) now.abs_value_us);
    587     TALER_TESTING_FAIL (ppc->is);
    588   }
    589   TALER_TESTING_interpreter_next (ppc->is);
    590 }
    591 
    592 
    593 /**
    594  * Process response from a GET /orders/$ID request
    595  *
    596  * @param cls a `struct WalletPollOrderStartState *`
    597  * @param owgr response details
    598  */
    599 static void
    600 wallet_poll_order_cb (
    601   void *cls,
    602   const struct TALER_MERCHANT_OrderWalletGetResponse *owgr)
    603 {
    604   struct WalletPollOrderStartState *pos = cls;
    605   const struct TALER_MERCHANT_HttpResponse *hr = &owgr->hr;
    606 
    607   pos->ogh = NULL;
    608   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    609               "GET /orders/$ID finished with status %u.\n",
    610               hr->http_status);
    611   pos->http_status = hr->http_status;
    612   switch (hr->http_status)
    613   {
    614   case MHD_HTTP_OK:
    615     pos->paid = true;
    616     pos->refunded = owgr->details.ok.refunded;
    617     pos->refund_pending = owgr->details.ok.refund_pending;
    618     if (owgr->details.ok.refunded)
    619       pos->refund_available = owgr->details.ok.refund_amount;
    620     break;
    621   case MHD_HTTP_PAYMENT_REQUIRED:
    622     if (NULL != owgr->details.payment_required.already_paid_order_id)
    623       pos->already_paid_order_id = GNUNET_strdup (
    624         owgr->details.payment_required.already_paid_order_id);
    625     break;
    626   default:
    627     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    628                 "Unhandled HTTP status.\n");
    629     break;
    630   }
    631   if ( (NULL != pos->cs) &&
    632        (NULL != pos->cs->task) )
    633   {
    634     GNUNET_SCHEDULER_cancel (pos->cs->task);
    635     pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
    636                                               pos->cs);
    637   }
    638 }
    639 
    640 
    641 /**
    642  * Run the "GET order" CMD.
    643  *
    644  * @param cls closure.
    645  * @param cmd command being run now.
    646  * @param is interpreter state.
    647  */
    648 static void
    649 wallet_poll_order_start_run (void *cls,
    650                              const struct TALER_TESTING_Command *cmd,
    651                              struct TALER_TESTING_Interpreter *is)
    652 {
    653   struct WalletPollOrderStartState *pos = cls;
    654   const struct TALER_TESTING_Command *order_cmd;
    655   const char *order_id;
    656   const struct TALER_PrivateContractHashP *h_contract;
    657 
    658   order_cmd = TALER_TESTING_interpreter_lookup_command (
    659     is,
    660     pos->order_ref);
    661 
    662   if (GNUNET_OK !=
    663       TALER_TESTING_get_trait_order_id (order_cmd,
    664                                         &order_id))
    665     TALER_TESTING_FAIL (is);
    666 
    667   if (GNUNET_OK !=
    668       TALER_TESTING_get_trait_h_contract_terms (order_cmd,
    669                                                 &h_contract))
    670     TALER_TESTING_FAIL (is);
    671 
    672   /* add 1s grace time to timeout */
    673   pos->deadline
    674     = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
    675                                 GNUNET_TIME_UNIT_SECONDS);
    676   pos->is = is;
    677   pos->ogh = TALER_MERCHANT_wallet_order_get (
    678     TALER_TESTING_interpreter_get_context (is),
    679     pos->merchant_url,
    680     order_id,
    681     h_contract,
    682     pos->timeout,
    683     pos->session_id,
    684     pos->wait_for_refund
    685     ? &pos->refund_threshold
    686     : NULL,
    687     false,                                           /* await_refund_obtained */
    688     &wallet_poll_order_cb,
    689     pos);
    690   GNUNET_assert (NULL != pos->ogh);
    691   /* We CONTINUE to run the interpreter while the long-polled command
    692      completes asynchronously! */
    693   TALER_TESTING_interpreter_next (pos->is);
    694 }
    695 
    696 
    697 /**
    698  * Free the state of a "GET order" CMD, and possibly
    699  * cancel a pending operation thereof.
    700  *
    701  * @param cls closure.
    702  * @param cmd command being run.
    703  */
    704 static void
    705 wallet_poll_order_start_cleanup (void *cls,
    706                                  const struct TALER_TESTING_Command *cmd)
    707 {
    708   struct WalletPollOrderStartState *pos = cls;
    709 
    710   if (NULL != pos->ogh)
    711   {
    712     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    713                 "Command `%s' was not terminated\n",
    714                 TALER_TESTING_interpreter_get_current_label (
    715                   pos->is));
    716     TALER_MERCHANT_wallet_order_get_cancel (pos->ogh);
    717   }
    718   GNUNET_free (pos->already_paid_order_id);
    719   GNUNET_free (pos);
    720 }
    721 
    722 
    723 struct TALER_TESTING_Command
    724 TALER_TESTING_cmd_wallet_poll_order_start (
    725   const char *label,
    726   const char *merchant_url,
    727   const char *order_ref,
    728   struct GNUNET_TIME_Relative timeout,
    729   const char *await_refund)
    730 {
    731   struct WalletPollOrderStartState *pos;
    732 
    733   pos = GNUNET_new (struct WalletPollOrderStartState);
    734   pos->order_ref = order_ref;
    735   pos->merchant_url = merchant_url;
    736   pos->timeout = timeout;
    737   if (NULL != await_refund)
    738   {
    739     pos->wait_for_refund = true;
    740     GNUNET_assert (GNUNET_OK ==
    741                    TALER_string_to_amount (await_refund,
    742                                            &pos->refund_threshold));
    743   }
    744   {
    745     struct TALER_TESTING_Command cmd = {
    746       .cls = pos,
    747       .label = label,
    748       .run = &wallet_poll_order_start_run,
    749       .cleanup = &wallet_poll_order_start_cleanup
    750     };
    751 
    752     return cmd;
    753   }
    754 }
    755 
    756 
    757 struct TALER_TESTING_Command
    758 TALER_TESTING_cmd_wallet_poll_order_start2 (
    759   const char *label,
    760   const char *merchant_url,
    761   const char *order_ref,
    762   struct GNUNET_TIME_Relative timeout,
    763   const char *await_refund,
    764   const char *session_id)
    765 {
    766   struct WalletPollOrderStartState *pos;
    767   struct TALER_TESTING_Command cmd;
    768 
    769   cmd = TALER_TESTING_cmd_wallet_poll_order_start (label,
    770                                                    merchant_url,
    771                                                    order_ref,
    772                                                    timeout,
    773                                                    await_refund);
    774   pos = cmd.cls;
    775   pos->session_id = session_id;
    776   return cmd;
    777 }
    778 
    779 
    780 /**
    781  * Run the "GET order conclude" CMD.
    782  *
    783  * @param cls closure.
    784  * @param cmd command being run now.
    785  * @param is interpreter state.
    786  */
    787 static void
    788 wallet_poll_order_conclude_run (void *cls,
    789                                 const struct TALER_TESTING_Command *cmd,
    790                                 struct TALER_TESTING_Interpreter *is)
    791 {
    792   struct WalletPollOrderConcludeState *poc = cls;
    793   const struct TALER_TESTING_Command *poll_cmd;
    794   struct WalletPollOrderStartState *pos;
    795 
    796   poc->is = is;
    797   poll_cmd =
    798     TALER_TESTING_interpreter_lookup_command (is,
    799                                               poc->start_reference);
    800   if (NULL == poll_cmd)
    801     TALER_TESTING_FAIL (poc->is);
    802   GNUNET_assert (poll_cmd->run == &wallet_poll_order_start_run);
    803   pos = poll_cmd->cls;
    804   pos->cs = poc;
    805   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    806               "Waiting on GET /orders/$ID of %s (%s)\n",
    807               poc->start_reference,
    808               (NULL == pos->ogh)
    809               ? "finished"
    810               : "active");
    811   if (NULL == pos->ogh)
    812     poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
    813                                           poc);
    814   else
    815     poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
    816                                          &conclude_task,
    817                                          poc);
    818 }
    819 
    820 
    821 /**
    822  * Free the state of a "GET order" CMD, and possibly
    823  * cancel a pending operation thereof.
    824  *
    825  * @param cls closure.
    826  * @param cmd command being run.
    827  */
    828 static void
    829 wallet_poll_order_conclude_cleanup (void *cls,
    830                                     const struct TALER_TESTING_Command *cmd)
    831 {
    832   struct WalletPollOrderConcludeState *poc = cls;
    833 
    834   if (NULL != poc->task)
    835   {
    836     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    837                 "Command `%s' was not terminated\n",
    838                 TALER_TESTING_interpreter_get_current_label (
    839                   poc->is));
    840     GNUNET_SCHEDULER_cancel (poc->task);
    841     poc->task = NULL;
    842   }
    843   GNUNET_free (poc);
    844 }
    845 
    846 
    847 struct TALER_TESTING_Command
    848 TALER_TESTING_cmd_wallet_poll_order_conclude (
    849   const char *label,
    850   unsigned int expected_http_status,
    851   const char *expected_refund_amount,
    852   const char *poll_start_reference)
    853 {
    854   struct WalletPollOrderConcludeState *cps;
    855 
    856   cps = GNUNET_new (struct WalletPollOrderConcludeState);
    857   cps->start_reference = poll_start_reference;
    858   cps->expected_http_status = expected_http_status;
    859   if (NULL != expected_refund_amount)
    860   {
    861     cps->expected_refund = true;
    862     GNUNET_assert (GNUNET_OK ==
    863                    TALER_string_to_amount (expected_refund_amount,
    864                                            &cps->expected_refund_amount));
    865   }
    866   {
    867     struct TALER_TESTING_Command cmd = {
    868       .cls = cps,
    869       .label = label,
    870       .run = &wallet_poll_order_conclude_run,
    871       .cleanup = &wallet_poll_order_conclude_cleanup
    872     };
    873 
    874     return cmd;
    875   }
    876 }
    877 
    878 
    879 struct TALER_TESTING_Command
    880 TALER_TESTING_cmd_wallet_poll_order_conclude2 (
    881   const char *label,
    882   unsigned int expected_http_status,
    883   const char *expected_refund_amount,
    884   const char *poll_start_reference,
    885   const char *already_paid_order_id)
    886 {
    887   struct WalletPollOrderConcludeState *cps;
    888   struct TALER_TESTING_Command cmd;
    889 
    890   cmd = TALER_TESTING_cmd_wallet_poll_order_conclude (
    891     label,
    892     expected_http_status,
    893     expected_refund_amount,
    894     poll_start_reference);
    895   cps = cmd.cls;
    896   cps->already_paid_order_id = already_paid_order_id;
    897   return cmd;
    898 }
    899 
    900 
    901 /* end of testing_api_cmd_wallet_get_order.c */