merchant

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

testing_api_cmd_merchant_get_order.c (32757B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU General Public License as
      7   published by the Free Software Foundation; either version 3, or
      8   (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file testing_api_cmd_merchant_get_order.c
     21  * @brief command to test GET /private/orders/$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 /private/orders/$ORDER_ID CMD.
     33  */
     34 struct MerchantGetOrderState
     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 /private/orders/$ORDER_ID request.
     48    */
     49   struct TALER_MERCHANT_OrderMerchantGetHandle *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    * Expected order status.
     63    */
     64   enum TALER_MERCHANT_OrderStatusCode osc;
     65 
     66   /**
     67    * A NULL-terminated list of refunds associated with this order.
     68    */
     69   const char **refunds;
     70 
     71   /**
     72    * The length of @e refunds.
     73    */
     74   unsigned int refunds_length;
     75 
     76   /**
     77    * A NULL-terminated list of transfers associated with this order.
     78    */
     79   const char **transfers;
     80 
     81   /**
     82    * The length of @e transfers.
     83    */
     84   unsigned int transfers_length;
     85 
     86   /**
     87    * A list of forget commands that apply to this order's contract terms.
     88    */
     89   const char **forgets;
     90 
     91   /**
     92    * The length of @e forgets.
     93    */
     94   unsigned int forgets_length;
     95 
     96   /**
     97    * Set to a session ID, if we should pass one as part
     98    * of the request.
     99    */
    100   const char *session_id;
    101 
    102   /**
    103    * Set if we expect to be referred to another equivalent order which was
    104    * already paid by the wallet under this @e session_id.
    105    */
    106   const char *repurchase_order_ref;
    107 
    108   /**
    109    * Expected minimum age.
    110    */
    111   unsigned int expected_min_age;
    112 
    113   /**
    114    * True if we should pass the 'allow_refunded_for_repurchase' flag.
    115    */
    116   bool allow_refunded_for_repurchase;
    117 
    118   /**
    119    * Whether the order was refunded or not.
    120    */
    121   bool refunded;
    122 
    123   /**
    124    * Whether the order was wired or not.
    125    */
    126   bool wired;
    127 };
    128 
    129 
    130 /**
    131  * Forget part of the contract terms.
    132  *
    133  * @param cls pointer to the result of the forget operation.
    134  * @param object_id name of the object to forget.
    135  * @param parent parent of the object at @e object_id.
    136  */
    137 static void
    138 apply_forget (void *cls,
    139               const char *object_id,
    140               json_t *parent)
    141 {
    142   int *res = cls;
    143 
    144   if (GNUNET_SYSERR ==
    145       TALER_JSON_contract_part_forget (parent,
    146                                        object_id))
    147     *res = GNUNET_SYSERR;
    148 }
    149 
    150 
    151 /**
    152  * Callback to process a GET /orders/$ID request
    153  *
    154  * @param cls closure
    155  * @param osr order status response details
    156  */
    157 static void
    158 merchant_get_order_cb (
    159   void *cls,
    160   const struct TALER_MERCHANT_OrderStatusResponse *osr)
    161 {
    162   struct MerchantGetOrderState *gos = cls;
    163 
    164   gos->ogh = NULL;
    165   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    166               "GET /private/orders/$ID completed with status %u\n",
    167               osr->hr.http_status);
    168   if (gos->http_status != osr->hr.http_status)
    169   {
    170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    171                 "Unexpected response code %u (%d) to command %s\n",
    172                 osr->hr.http_status,
    173                 (int) osr->hr.ec,
    174                 TALER_TESTING_interpreter_get_current_label (gos->is));
    175     TALER_TESTING_interpreter_fail (gos->is);
    176     return;
    177   }
    178   switch (osr->hr.http_status)
    179   {
    180   case MHD_HTTP_OK:
    181     if (gos->osc != osr->details.ok.status)
    182     {
    183       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    184                   "Order paid does not match: %d vs %d\n",
    185                   gos->osc,
    186                   osr->details.ok.status);
    187       TALER_TESTING_interpreter_fail (gos->is);
    188       return;
    189     }
    190     switch (osr->details.ok.status)
    191     {
    192     case TALER_MERCHANT_OSC_PAID:
    193       {
    194         const struct TALER_TESTING_Command *order_cmd;
    195         struct TALER_Amount refunded_total;
    196 
    197         if ( (0 != gos->expected_min_age) &&
    198              (gos->expected_min_age !=
    199               json_integer_value (
    200                 json_object_get (
    201                   osr->details.ok.details.paid.contract_terms,
    202                   "minimum_age"))) )
    203         {
    204           GNUNET_break (0);
    205           TALER_TESTING_interpreter_fail (gos->is);
    206           return;
    207         }
    208         order_cmd = TALER_TESTING_interpreter_lookup_command (
    209           gos->is,
    210           gos->order_reference);
    211 
    212         {
    213           const json_t *expected_contract_terms;
    214           json_t *ct;
    215 
    216           if (GNUNET_OK !=
    217               TALER_TESTING_get_trait_contract_terms (order_cmd,
    218                                                       &expected_contract_terms))
    219           {
    220             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    221                         "Could not fetch order contract terms\n");
    222             TALER_TESTING_interpreter_fail (gos->is);
    223             return;
    224           }
    225 
    226           ct = json_deep_copy (expected_contract_terms);
    227 
    228           /* Apply all forgets, then compare */
    229           for (unsigned int i = 0; i < gos->forgets_length; ++i)
    230           {
    231             const struct TALER_TESTING_Command *forget_cmd;
    232             const uint32_t *paths_length;
    233 
    234             forget_cmd = TALER_TESTING_interpreter_lookup_command (
    235               gos->is,
    236               gos->forgets[i]);
    237 
    238             if (GNUNET_OK !=
    239                 TALER_TESTING_get_trait_paths_length (forget_cmd,
    240                                                       &paths_length))
    241             {
    242               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    243                           "Couldn't fetch forget paths length\n");
    244               TALER_TESTING_interpreter_fail (gos->is);
    245               return;
    246             }
    247 
    248             for (unsigned int j = 0; j < *paths_length; ++j)
    249             {
    250               const char *path;
    251               int res = GNUNET_OK;
    252 
    253               if (GNUNET_OK !=
    254                   TALER_TESTING_get_trait_paths (forget_cmd,
    255                                                  j,
    256                                                  &path))
    257               {
    258                 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    259                             "Couldn't fetch forget path\n");
    260                 TALER_TESTING_interpreter_fail (gos->is);
    261                 return;
    262               }
    263 
    264               GNUNET_assert (GNUNET_OK ==
    265                              TALER_JSON_expand_path (ct,
    266                                                      path,
    267                                                      &apply_forget,
    268                                                      &res));
    269               GNUNET_assert (GNUNET_OK == res);
    270             }
    271           }
    272 
    273           if (1 != json_equal (ct,
    274                                osr->details.ok.details.paid.contract_terms))
    275           {
    276             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    277                         "Order contract terms do not match\n");
    278             TALER_TESTING_interpreter_fail (gos->is);
    279             return;
    280           }
    281 
    282           json_decref (ct);
    283         }
    284         if (gos->wired != osr->details.ok.details.paid.wired)
    285         {
    286           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    287                       "Order wired does not match\n");
    288           TALER_TESTING_interpreter_fail (gos->is);
    289           return;
    290         }
    291         if (gos->transfers_length != osr->details.ok.details.paid.wts_len)
    292         {
    293           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    294                       "Number of transfers found does not match\n");
    295           TALER_TESTING_interpreter_fail (gos->is);
    296           return;
    297         }
    298         for (unsigned int i = 0; i < gos->transfers_length; ++i)
    299         {
    300           const struct TALER_TESTING_Command *transfer_cmd;
    301 
    302           transfer_cmd = TALER_TESTING_interpreter_lookup_command (
    303             gos->is,
    304             gos->transfers[i]);
    305           {
    306             const struct TALER_WireTransferIdentifierRawP *wtid;
    307 
    308             if (GNUNET_OK !=
    309                 TALER_TESTING_get_trait_wtid (transfer_cmd,
    310                                               &wtid))
    311             {
    312               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    313                           "Could not fetch wire transfer id\n");
    314               TALER_TESTING_interpreter_fail (gos->is);
    315               return;
    316             }
    317             if (0 != GNUNET_memcmp (wtid,
    318                                     &osr->details.ok.details.paid.wts[i].
    319                                     wtid))
    320             {
    321               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    322                           "Wire transfer id does not match\n");
    323               TALER_TESTING_interpreter_fail (gos->is);
    324               return;
    325             }
    326           }
    327           {
    328             const char *exchange_url;
    329 
    330             if (GNUNET_OK !=
    331                 TALER_TESTING_get_trait_exchange_url (transfer_cmd,
    332                                                       &exchange_url))
    333             {
    334               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    335                           "Could not fetch wire transfer exchange url\n");
    336               TALER_TESTING_interpreter_fail (gos->is);
    337               return;
    338             }
    339             if (0 != strcmp (
    340                   exchange_url,
    341                   osr->details.ok.details.paid.wts[i].exchange_url))
    342             {
    343               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    344                           "Wire transfer exchange url does not match\n");
    345               TALER_TESTING_interpreter_fail (gos->is);
    346               return;
    347             }
    348           }
    349         }
    350         if (gos->refunded != osr->details.ok.details.paid.refunded)
    351         {
    352           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    353                       "Order refunded does not match\n");
    354           TALER_TESTING_interpreter_fail (gos->is);
    355           return;
    356         }
    357         if (gos->refunds_length !=
    358             osr->details.ok.details.paid.refunds_len)
    359         {
    360           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    361                       "Number of refunds found does not match\n");
    362           TALER_TESTING_interpreter_fail (gos->is);
    363           return;
    364         }
    365         if (0 < gos->refunds_length)
    366           GNUNET_assert (
    367             GNUNET_OK ==
    368             TALER_amount_set_zero (
    369               osr->details.ok.details.paid.refund_amount.currency,
    370               &refunded_total));
    371         for (unsigned int i = 0; i < gos->refunds_length; ++i)
    372         {
    373           const struct TALER_TESTING_Command *refund_cmd;
    374 
    375           refund_cmd = TALER_TESTING_interpreter_lookup_command (
    376             gos->is,
    377             gos->refunds[i]);
    378           {
    379             const struct TALER_Amount *expected_amount;
    380             struct TALER_Amount *amount_found =
    381               &osr->details.ok.details.paid.refunds[i].refund_amount;
    382 
    383             if (GNUNET_OK !=
    384                 TALER_TESTING_get_trait_amount (refund_cmd,
    385                                                 &expected_amount))
    386             {
    387               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    388                           "Could not fetch refund amount\n");
    389               TALER_TESTING_interpreter_fail (gos->is);
    390               return;
    391             }
    392             GNUNET_assert (0 <= TALER_amount_add (&refunded_total,
    393                                                   &refunded_total,
    394                                                   amount_found));
    395             if ((GNUNET_OK !=
    396                  TALER_amount_cmp_currency (expected_amount,
    397                                             &refunded_total)) ||
    398                 (0 != TALER_amount_cmp (expected_amount,
    399                                         &refunded_total)))
    400             {
    401               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    402                           "Refund amounts do not match\n");
    403               TALER_TESTING_interpreter_fail (gos->is);
    404               return;
    405             }
    406           }
    407           {
    408             const char *expected_reason;
    409 
    410             if (GNUNET_OK !=
    411                 TALER_TESTING_get_trait_reason (refund_cmd,
    412                                                 &expected_reason))
    413             {
    414               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    415                           "Could not fetch reason\n");
    416               TALER_TESTING_interpreter_fail (gos->is);
    417               return;
    418             }
    419             if (0 !=
    420                 strcmp (
    421                   expected_reason,
    422                   osr->details.ok.details.paid.refunds[i].reason))
    423             {
    424               GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    425                           "Refund reason does not match\n");
    426               TALER_TESTING_interpreter_fail (gos->is);
    427               return;
    428             }
    429           }
    430         }
    431 
    432         if (gos->wired != osr->details.ok.details.paid.wired)
    433         {
    434           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    435                       "Order wired does not match\n");
    436           TALER_TESTING_interpreter_fail (gos->is);
    437           return;
    438         }
    439       }
    440       break;
    441     case TALER_MERCHANT_OSC_CLAIMED:
    442       /* FIXME: Check contract terms... */
    443       if ( (0 != gos->expected_min_age) &&
    444            (gos->expected_min_age !=
    445             json_integer_value (
    446               json_object_get (
    447                 osr->details.ok.details.claimed.contract_terms,
    448                 "minimum_age"))) )
    449       {
    450         GNUNET_break (0);
    451         TALER_TESTING_interpreter_fail (gos->is);
    452         return;
    453       }
    454       break;
    455     case TALER_MERCHANT_OSC_UNPAID:
    456       {
    457         struct TALER_MERCHANT_PayUriData pud;
    458         const struct TALER_TESTING_Command *order_cmd;
    459         const char *order_id;
    460         const struct TALER_ClaimTokenP *claim_token;
    461 
    462         if (NULL != gos->repurchase_order_ref)
    463         {
    464           const struct TALER_TESTING_Command *rep_cmd;
    465           const char *rep_id;
    466           const char *ri;
    467 
    468           rep_cmd = TALER_TESTING_interpreter_lookup_command (
    469             gos->is,
    470             gos->repurchase_order_ref);
    471           if (GNUNET_OK !=
    472               TALER_TESTING_get_trait_order_id (rep_cmd,
    473                                                 &rep_id))
    474           {
    475             TALER_TESTING_FAIL (gos->is);
    476           }
    477           ri = osr->details.ok.details.unpaid.already_paid_order_id;
    478           if ( (NULL == ri) ||
    479                (0 !=
    480                 strcmp (ri,
    481                         rep_id)) )
    482           {
    483             TALER_TESTING_FAIL (gos->is);
    484           }
    485         }
    486 
    487         if (GNUNET_OK !=
    488             TALER_MERCHANT_parse_pay_uri (
    489               osr->details.ok.details.unpaid.taler_pay_uri,
    490               &pud))
    491         {
    492           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    493                       "Taler pay uri `%s' is malformed\n",
    494                       osr->details.ok.details.unpaid.taler_pay_uri);
    495           TALER_TESTING_interpreter_fail (gos->is);
    496           return;
    497         }
    498 
    499         order_cmd = TALER_TESTING_interpreter_lookup_command (
    500           gos->is,
    501           gos->order_reference);
    502 
    503         if (GNUNET_OK !=
    504             TALER_TESTING_get_trait_order_id (order_cmd,
    505                                               &order_id))
    506         {
    507           TALER_MERCHANT_parse_pay_uri_free (&pud);
    508           TALER_TESTING_FAIL (gos->is);
    509         }
    510 
    511         if (GNUNET_OK !=
    512             TALER_TESTING_get_trait_claim_token (order_cmd,
    513                                                  &claim_token))
    514         {
    515           TALER_MERCHANT_parse_pay_uri_free (&pud);
    516           TALER_TESTING_FAIL (gos->is);
    517         }
    518         {
    519           char *host;
    520 
    521           host = TALER_MERCHANT_TESTING_extract_host (gos->merchant_url);
    522           if ((0 != strcmp (host,
    523                             pud.merchant_host)) ||
    524               (NULL != pud.merchant_prefix_path) ||
    525               (0 != strcmp (order_id,
    526                             pud.order_id)) ||
    527               (NULL != pud.ssid))
    528           {
    529             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    530                         "Order pay uri `%s' does not match, wanted %s/%s\n",
    531                         osr->details.ok.details.unpaid.taler_pay_uri,
    532                         host,
    533                         order_id);
    534             TALER_TESTING_interpreter_fail (gos->is);
    535             TALER_MERCHANT_parse_pay_uri_free (&pud);
    536             GNUNET_free (host);
    537             return;
    538           }
    539           GNUNET_free (host);
    540         }
    541         /* The claim token is not given in the pay uri if the order
    542            has been claimed already. */
    543         if ( (NULL != pud.claim_token) &&
    544              ( (NULL == claim_token) ||
    545                (0 != GNUNET_memcmp (claim_token,
    546                                     pud.claim_token)) ) )
    547         {
    548           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    549                       "Order pay uri claim token does not match (%d/%d/%d/%d)\n",
    550                       NULL == pud.claim_token,
    551                       NULL == claim_token,
    552                       (NULL != pud.claim_token) &&
    553                       GNUNET_is_zero (pud.claim_token),
    554                       (NULL != claim_token) &&
    555                       GNUNET_is_zero (claim_token));
    556           TALER_TESTING_interpreter_fail (gos->is);
    557           TALER_MERCHANT_parse_pay_uri_free (&pud);
    558           return;
    559         }
    560         TALER_MERCHANT_parse_pay_uri_free (&pud);
    561         break;
    562       }
    563     }
    564     break;
    565   default:
    566     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    567                 "Unhandled HTTP status.\n");
    568   }
    569   TALER_TESTING_interpreter_next (gos->is);
    570 }
    571 
    572 
    573 /**
    574  * Run the "GET order" CMD.
    575  *
    576  * @param cls closure.
    577  * @param cmd command being run now.
    578  * @param is interpreter state.
    579  */
    580 static void
    581 merchant_get_order_run (void *cls,
    582                         const struct TALER_TESTING_Command *cmd,
    583                         struct TALER_TESTING_Interpreter *is)
    584 {
    585   struct MerchantGetOrderState *gos = cls;
    586   const struct TALER_TESTING_Command *order_cmd;
    587   const char *order_id;
    588   const struct TALER_PrivateContractHashP *h_contract;
    589 
    590   order_cmd = TALER_TESTING_interpreter_lookup_command (
    591     is,
    592     gos->order_reference);
    593 
    594   if (GNUNET_OK !=
    595       TALER_TESTING_get_trait_order_id (order_cmd,
    596                                         &order_id))
    597     TALER_TESTING_FAIL (is);
    598 
    599   if (GNUNET_OK !=
    600       TALER_TESTING_get_trait_h_contract_terms (order_cmd,
    601                                                 &h_contract))
    602     TALER_TESTING_FAIL (is);
    603 
    604   gos->is = is;
    605   gos->ogh = TALER_MERCHANT_merchant_order_get (
    606     TALER_TESTING_interpreter_get_context (is),
    607     gos->merchant_url,
    608     order_id,
    609     gos->session_id,
    610     GNUNET_TIME_UNIT_ZERO,
    611     &merchant_get_order_cb,
    612     gos);
    613 }
    614 
    615 
    616 /**
    617  * Free the state of a "GET order" CMD, and possibly
    618  * cancel a pending operation thereof.
    619  *
    620  * @param cls closure.
    621  * @param cmd command being run.
    622  */
    623 static void
    624 merchant_get_order_cleanup (void *cls,
    625                             const struct TALER_TESTING_Command *cmd)
    626 {
    627   struct MerchantGetOrderState *gos = cls;
    628 
    629   if (NULL != gos->ogh)
    630   {
    631     TALER_LOG_WARNING ("Get order operation did not complete\n");
    632     TALER_MERCHANT_merchant_order_get_cancel (gos->ogh);
    633   }
    634   GNUNET_array_grow (gos->transfers,
    635                      gos->transfers_length,
    636                      0);
    637   GNUNET_array_grow (gos->refunds,
    638                      gos->refunds_length,
    639                      0);
    640   GNUNET_array_grow (gos->forgets,
    641                      gos->forgets_length,
    642                      0);
    643   GNUNET_free (gos);
    644 }
    645 
    646 
    647 struct TALER_TESTING_Command
    648 TALER_TESTING_cmd_merchant_get_order (
    649   const char *label,
    650   const char *merchant_url,
    651   const char *order_reference,
    652   enum TALER_MERCHANT_OrderStatusCode osc,
    653   bool refunded,
    654   unsigned int http_status,
    655   ...)
    656 {
    657   struct MerchantGetOrderState *gos;
    658 
    659   gos = GNUNET_new (struct MerchantGetOrderState);
    660   gos->merchant_url = merchant_url;
    661   gos->order_reference = order_reference;
    662   gos->osc = osc;
    663   gos->refunded = refunded;
    664   gos->http_status = http_status;
    665   if (refunded)
    666   {
    667     const char *clabel;
    668     va_list ap;
    669 
    670     va_start (ap, http_status);
    671     while (NULL != (clabel = va_arg (ap, const char *)))
    672     {
    673       GNUNET_array_append (gos->refunds,
    674                            gos->refunds_length,
    675                            clabel);
    676     }
    677     va_end (ap);
    678   }
    679   {
    680     struct TALER_TESTING_Command cmd = {
    681       .cls = gos,
    682       .label = label,
    683       .run = &merchant_get_order_run,
    684       .cleanup = &merchant_get_order_cleanup
    685     };
    686 
    687     return cmd;
    688   }
    689 }
    690 
    691 
    692 struct TALER_TESTING_Command
    693 TALER_TESTING_cmd_merchant_get_order2 (
    694   const char *label,
    695   const char *merchant_url,
    696   const char *order_reference,
    697   enum TALER_MERCHANT_OrderStatusCode osc,
    698   bool wired,
    699   const char **transfers,
    700   bool refunded,
    701   const char **refunds,
    702   const char **forgets,
    703   unsigned int http_status)
    704 {
    705   struct MerchantGetOrderState *gos;
    706 
    707   gos = GNUNET_new (struct MerchantGetOrderState);
    708   gos->merchant_url = merchant_url;
    709   gos->order_reference = order_reference;
    710   gos->osc = osc;
    711   gos->wired = wired;
    712   gos->refunded = refunded;
    713   gos->http_status = http_status;
    714   if (wired)
    715   {
    716     for (const char **clabel = transfers; *clabel != NULL; ++clabel)
    717     {
    718       GNUNET_array_append (gos->transfers,
    719                            gos->transfers_length,
    720                            *clabel);
    721     }
    722   }
    723   if (refunded)
    724   {
    725     for (const char **clabel = refunds; *clabel != NULL; ++clabel)
    726     {
    727       GNUNET_array_append (gos->refunds,
    728                            gos->refunds_length,
    729                            *clabel);
    730     }
    731   }
    732   if (NULL != forgets)
    733   {
    734     for (const char **clabel = forgets; *clabel != NULL; ++clabel)
    735     {
    736       GNUNET_array_append (gos->forgets,
    737                            gos->forgets_length,
    738                            *clabel);
    739     }
    740   }
    741   {
    742     struct TALER_TESTING_Command cmd = {
    743       .cls = gos,
    744       .label = label,
    745       .run = &merchant_get_order_run,
    746       .cleanup = &merchant_get_order_cleanup
    747     };
    748 
    749     return cmd;
    750   }
    751 }
    752 
    753 
    754 struct TALER_TESTING_Command
    755 TALER_TESTING_cmd_merchant_get_order3 (
    756   const char *label,
    757   const char *merchant_url,
    758   const char *order_reference,
    759   enum TALER_MERCHANT_OrderStatusCode osc,
    760   const char *session_id,
    761   const char *repurchase_order_ref,
    762   unsigned int expected_http_status)
    763 {
    764   struct MerchantGetOrderState *gos;
    765 
    766   gos = GNUNET_new (struct MerchantGetOrderState);
    767   gos->merchant_url = merchant_url;
    768   gos->order_reference = order_reference;
    769   gos->osc = osc;
    770   gos->session_id = session_id;
    771   gos->repurchase_order_ref = repurchase_order_ref;
    772   gos->http_status = expected_http_status;
    773   {
    774     struct TALER_TESTING_Command cmd = {
    775       .cls = gos,
    776       .label = label,
    777       .run = &merchant_get_order_run,
    778       .cleanup = &merchant_get_order_cleanup
    779     };
    780 
    781     return cmd;
    782   }
    783 }
    784 
    785 
    786 struct TALER_TESTING_Command
    787 TALER_TESTING_cmd_merchant_get_order4 (
    788   const char *label,
    789   const char *merchant_url,
    790   const char *order_reference,
    791   enum TALER_MERCHANT_OrderStatusCode osc,
    792   uint32_t expected_min_age,
    793   unsigned int expected_http_status)
    794 {
    795   struct MerchantGetOrderState *gos;
    796 
    797   gos = GNUNET_new (struct MerchantGetOrderState);
    798   gos->merchant_url = merchant_url;
    799   gos->order_reference = order_reference;
    800   gos->osc = osc;
    801   gos->expected_min_age = expected_min_age;
    802   gos->http_status = expected_http_status;
    803   {
    804     struct TALER_TESTING_Command cmd = {
    805       .cls = gos,
    806       .label = label,
    807       .run = &merchant_get_order_run,
    808       .cleanup = &merchant_get_order_cleanup
    809     };
    810 
    811     return cmd;
    812   }
    813 }
    814 
    815 
    816 struct MerchantPollOrderConcludeState
    817 {
    818   /**
    819    * The interpreter state.
    820    */
    821   struct TALER_TESTING_Interpreter *is;
    822 
    823   /**
    824    * Reference to a command that can provide a poll order start command.
    825    */
    826   const char *start_reference;
    827 
    828   /**
    829    * Task to wait for the deadline.
    830    */
    831   struct GNUNET_SCHEDULER_Task *task;
    832 
    833   /**
    834    * Expected HTTP response status code.
    835    */
    836   unsigned int expected_http_status;
    837 };
    838 
    839 
    840 struct MerchantPollOrderStartState
    841 {
    842   /**
    843    * The merchant base URL.
    844    */
    845   const char *merchant_url;
    846 
    847   /**
    848    * The handle to the current GET /private/orders/$ORDER_ID request.
    849    */
    850   struct TALER_MERCHANT_OrderMerchantGetHandle *ogh;
    851 
    852   /**
    853    * The interpreter state.
    854    */
    855   struct TALER_TESTING_Interpreter *is;
    856 
    857   /**
    858    * Reference to a command that created an order.
    859    */
    860   const char *order_id;
    861 
    862   /**
    863    * How long to wait for server to return a response.
    864    */
    865   struct GNUNET_TIME_Relative timeout;
    866 
    867   /**
    868    * Conclude state waiting for completion (if any).
    869    */
    870   struct MerchantPollOrderConcludeState *cs;
    871 
    872   /**
    873    * The HTTP status code returned by the backend.
    874    */
    875   unsigned int http_status;
    876 
    877   /**
    878    * When the request should be completed by.
    879    */
    880   struct GNUNET_TIME_Absolute deadline;
    881 };
    882 
    883 
    884 /**
    885  * Task called when either the timeout for the GET /private/order/$ID command
    886  * expired or we got a response.  Checks if the result is what we expected.
    887  *
    888  * @param cls a `struct MerchantPollOrderConcludeState`
    889  */
    890 static void
    891 conclude_task (void *cls)
    892 {
    893   struct MerchantPollOrderConcludeState *ppc = cls;
    894   const struct TALER_TESTING_Command *poll_cmd;
    895   struct MerchantPollOrderStartState *cps;
    896   struct GNUNET_TIME_Absolute now;
    897 
    898   ppc->task = NULL;
    899   poll_cmd =
    900     TALER_TESTING_interpreter_lookup_command (ppc->is,
    901                                               ppc->start_reference);
    902   if (NULL == poll_cmd)
    903     TALER_TESTING_FAIL (ppc->is);
    904   cps = poll_cmd->cls;
    905   if (NULL != cps->ogh)
    906   {
    907     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    908                 "Expected poll GET /private/orders/$ORDER_ID to have completed, but it did not!\n");
    909     TALER_TESTING_FAIL (ppc->is);
    910   }
    911   if (cps->http_status != ppc->expected_http_status)
    912   {
    913     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    914                 "Expected HTTP status %u, got %u\n",
    915                 ppc->expected_http_status,
    916                 cps->http_status);
    917     TALER_TESTING_FAIL (ppc->is);
    918   }
    919   now = GNUNET_TIME_absolute_get ();
    920   if ((GNUNET_TIME_absolute_add (cps->deadline,
    921                                  GNUNET_TIME_UNIT_SECONDS).abs_value_us <
    922        now.abs_value_us) )
    923   {
    924     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    925                 "Expected answer to be delayed until %llu, but got response at %llu\n",
    926                 (unsigned long long) cps->deadline.abs_value_us,
    927                 (unsigned long long) now.abs_value_us);
    928     TALER_TESTING_FAIL (ppc->is);
    929   }
    930   TALER_TESTING_interpreter_next (ppc->is);
    931 }
    932 
    933 
    934 /**
    935  * Callback to process a GET /private/orders/$ID request
    936  *
    937  * @param cls closure
    938  * @param osr order status response details
    939  */
    940 static void
    941 merchant_poll_order_cb (
    942   void *cls,
    943   const struct TALER_MERCHANT_OrderStatusResponse *osr)
    944 {
    945   struct MerchantPollOrderStartState *pos = cls;
    946   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    947 
    948   pos->ogh = NULL;
    949   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    950               "GET /private/orders/$ID finished with status %u.\n",
    951               hr->http_status);
    952   pos->http_status = hr->http_status;
    953   switch (hr->http_status)
    954   {
    955   case MHD_HTTP_OK:
    956     // FIXME: keep data from 'osr' here for checking?
    957     break;
    958   default:
    959     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    960                 "Unhandled HTTP status.\n");
    961   }
    962   if (NULL != pos->cs)
    963   {
    964     GNUNET_SCHEDULER_cancel (pos->cs->task);
    965     pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
    966                                               pos->cs);
    967   }
    968 }
    969 
    970 
    971 /**
    972  * Run the "GET order" CMD.
    973  *
    974  * @param cls closure.
    975  * @param cmd command being run now.
    976  * @param is interpreter state.
    977  */
    978 static void
    979 merchant_poll_order_start_run (void *cls,
    980                                const struct TALER_TESTING_Command *cmd,
    981                                struct TALER_TESTING_Interpreter *is)
    982 {
    983   struct MerchantPollOrderStartState *pos = cls;
    984 
    985   /* add 1s grace time to timeout */
    986   pos->deadline
    987     = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
    988                                 GNUNET_TIME_UNIT_SECONDS);
    989   pos->is = is;
    990   pos->ogh = TALER_MERCHANT_merchant_order_get (
    991     TALER_TESTING_interpreter_get_context (is),
    992     pos->merchant_url,
    993     pos->order_id,
    994     NULL,
    995     pos->timeout,
    996     &merchant_poll_order_cb,
    997     pos);
    998   GNUNET_assert (NULL != pos->ogh);
    999   /* We CONTINUE to run the interpreter while the long-polled command
   1000      completes asynchronously! */
   1001   TALER_TESTING_interpreter_next (pos->is);
   1002 }
   1003 
   1004 
   1005 /**
   1006  * Free the state of a "GET order" CMD, and possibly
   1007  * cancel a pending operation thereof.
   1008  *
   1009  * @param cls closure.
   1010  * @param cmd command being run.
   1011  */
   1012 static void
   1013 merchant_poll_order_start_cleanup (void *cls,
   1014                                    const struct TALER_TESTING_Command *cmd)
   1015 {
   1016   struct MerchantPollOrderStartState *pos = cls;
   1017 
   1018   if (NULL != pos->ogh)
   1019   {
   1020     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1021                 "Command `%s' was not terminated\n",
   1022                 TALER_TESTING_interpreter_get_current_label (
   1023                   pos->is));
   1024     TALER_MERCHANT_merchant_order_get_cancel (pos->ogh);
   1025   }
   1026   GNUNET_free (pos);
   1027 }
   1028 
   1029 
   1030 struct TALER_TESTING_Command
   1031 TALER_TESTING_cmd_poll_order_start (
   1032   const char *label,
   1033   const char *merchant_url,
   1034   const char *order_id,
   1035   struct GNUNET_TIME_Relative timeout)
   1036 {
   1037   struct MerchantPollOrderStartState *pos;
   1038 
   1039   pos = GNUNET_new (struct MerchantPollOrderStartState);
   1040   pos->order_id = order_id;
   1041   pos->merchant_url = merchant_url;
   1042   pos->timeout = timeout;
   1043   {
   1044     struct TALER_TESTING_Command cmd = {
   1045       .cls = pos,
   1046       .label = label,
   1047       .run = &merchant_poll_order_start_run,
   1048       .cleanup = &merchant_poll_order_start_cleanup
   1049     };
   1050 
   1051     return cmd;
   1052   }
   1053 }
   1054 
   1055 
   1056 /**
   1057  * Run the "GET order conclude" CMD.
   1058  *
   1059  * @param cls closure.
   1060  * @param cmd command being run now.
   1061  * @param is interpreter state.
   1062  */
   1063 static void
   1064 merchant_poll_order_conclude_run (void *cls,
   1065                                   const struct TALER_TESTING_Command *cmd,
   1066                                   struct TALER_TESTING_Interpreter *is)
   1067 {
   1068   struct MerchantPollOrderConcludeState *poc = cls;
   1069   const struct TALER_TESTING_Command *poll_cmd;
   1070   struct MerchantPollOrderStartState *pos;
   1071 
   1072   poc->is = is;
   1073   poll_cmd =
   1074     TALER_TESTING_interpreter_lookup_command (is,
   1075                                               poc->start_reference);
   1076   if (NULL == poll_cmd)
   1077     TALER_TESTING_FAIL (poc->is);
   1078   GNUNET_assert (poll_cmd->run == &merchant_poll_order_start_run);
   1079   pos = poll_cmd->cls;
   1080   pos->cs = poc;
   1081   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1082               "Waiting on GET /private/orders/$ID of %s (%s)\n",
   1083               poc->start_reference,
   1084               (NULL == pos->ogh)
   1085               ? "finished"
   1086               : "active");
   1087   if (NULL == pos->ogh)
   1088     poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
   1089                                           poc);
   1090   else
   1091     poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
   1092                                          &conclude_task,
   1093                                          poc);
   1094 }
   1095 
   1096 
   1097 /**
   1098  * Free the state of a "GET order" CMD, and possibly
   1099  * cancel a pending operation thereof.
   1100  *
   1101  * @param cls closure.
   1102  * @param cmd command being run.
   1103  */
   1104 static void
   1105 merchant_poll_order_conclude_cleanup (void *cls,
   1106                                       const struct TALER_TESTING_Command *cmd)
   1107 {
   1108   struct MerchantPollOrderConcludeState *poc = cls;
   1109 
   1110   if (NULL != poc->task)
   1111   {
   1112     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1113                 "Command `%s' was not terminated\n",
   1114                 TALER_TESTING_interpreter_get_current_label (
   1115                   poc->is));
   1116     GNUNET_SCHEDULER_cancel (poc->task);
   1117     poc->task = NULL;
   1118   }
   1119   GNUNET_free (poc);
   1120 }
   1121 
   1122 
   1123 struct TALER_TESTING_Command
   1124 TALER_TESTING_cmd_poll_order_conclude (const char *label,
   1125                                        unsigned int http_status,
   1126                                        const char *poll_start_reference)
   1127 {
   1128   struct MerchantPollOrderConcludeState *cps;
   1129 
   1130   cps = GNUNET_new (struct MerchantPollOrderConcludeState);
   1131   cps->start_reference = poll_start_reference;
   1132   cps->expected_http_status = http_status;
   1133   {
   1134     struct TALER_TESTING_Command cmd = {
   1135       .cls = cps,
   1136       .label = label,
   1137       .run = &merchant_poll_order_conclude_run,
   1138       .cleanup = &merchant_poll_order_conclude_cleanup
   1139     };
   1140 
   1141     return cmd;
   1142   }
   1143 }
   1144 
   1145 
   1146 /* end of testing_api_cmd_merchant_get_order.c */