merchant

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

testing_api_cmd_wallet_get_order.c (25444B)


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