merchant

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

testing_api_cmd_merchant_get_order.c (33455B)


      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 "taler/platform.h"
     25 #include <taler/taler_exchange_service.h>
     26 #include <taler/taler_testing_lib.h>
     27 #include "taler/taler_merchant_testing_lib.h"
     28 #include <taler/taler-merchant/get-private-orders-ORDER_ID.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_GetPrivateOrderHandle *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_GetPrivateOrderResponse *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_get_private_order_create (
    606     TALER_TESTING_interpreter_get_context (is),
    607     gos->merchant_url,
    608     order_id);
    609   GNUNET_assert (NULL != gos->ogh);
    610   if (NULL != gos->session_id)
    611   {
    612     TALER_MERCHANT_get_private_order_set_options (
    613       gos->ogh,
    614       TALER_MERCHANT_get_private_order_option_session_id (
    615         gos->session_id));
    616   }
    617   {
    618     enum TALER_ErrorCode ec;
    619 
    620     ec = TALER_MERCHANT_get_private_order_start (
    621       gos->ogh,
    622       &merchant_get_order_cb,
    623       gos);
    624     GNUNET_assert (TALER_EC_NONE == ec);
    625   }
    626 }
    627 
    628 
    629 /**
    630  * Free the state of a "GET order" CMD, and possibly
    631  * cancel a pending operation thereof.
    632  *
    633  * @param cls closure.
    634  * @param cmd command being run.
    635  */
    636 static void
    637 merchant_get_order_cleanup (void *cls,
    638                             const struct TALER_TESTING_Command *cmd)
    639 {
    640   struct MerchantGetOrderState *gos = cls;
    641 
    642   if (NULL != gos->ogh)
    643   {
    644     TALER_LOG_WARNING ("Get order operation did not complete\n");
    645     TALER_MERCHANT_get_private_order_cancel (gos->ogh);
    646   }
    647   GNUNET_array_grow (gos->transfers,
    648                      gos->transfers_length,
    649                      0);
    650   GNUNET_array_grow (gos->refunds,
    651                      gos->refunds_length,
    652                      0);
    653   GNUNET_array_grow (gos->forgets,
    654                      gos->forgets_length,
    655                      0);
    656   GNUNET_free (gos);
    657 }
    658 
    659 
    660 struct TALER_TESTING_Command
    661 TALER_TESTING_cmd_merchant_get_order (
    662   const char *label,
    663   const char *merchant_url,
    664   const char *order_reference,
    665   enum TALER_MERCHANT_OrderStatusCode osc,
    666   bool refunded,
    667   unsigned int http_status,
    668   ...)
    669 {
    670   struct MerchantGetOrderState *gos;
    671 
    672   gos = GNUNET_new (struct MerchantGetOrderState);
    673   gos->merchant_url = merchant_url;
    674   gos->order_reference = order_reference;
    675   gos->osc = osc;
    676   gos->refunded = refunded;
    677   gos->http_status = http_status;
    678   if (refunded)
    679   {
    680     const char *clabel;
    681     va_list ap;
    682 
    683     va_start (ap, http_status);
    684     while (NULL != (clabel = va_arg (ap, const char *)))
    685     {
    686       GNUNET_array_append (gos->refunds,
    687                            gos->refunds_length,
    688                            clabel);
    689     }
    690     va_end (ap);
    691   }
    692   {
    693     struct TALER_TESTING_Command cmd = {
    694       .cls = gos,
    695       .label = label,
    696       .run = &merchant_get_order_run,
    697       .cleanup = &merchant_get_order_cleanup
    698     };
    699 
    700     return cmd;
    701   }
    702 }
    703 
    704 
    705 struct TALER_TESTING_Command
    706 TALER_TESTING_cmd_merchant_get_order2 (
    707   const char *label,
    708   const char *merchant_url,
    709   const char *order_reference,
    710   enum TALER_MERCHANT_OrderStatusCode osc,
    711   bool wired,
    712   const char **transfers,
    713   bool refunded,
    714   const char **refunds,
    715   const char **forgets,
    716   unsigned int http_status)
    717 {
    718   struct MerchantGetOrderState *gos;
    719 
    720   gos = GNUNET_new (struct MerchantGetOrderState);
    721   gos->merchant_url = merchant_url;
    722   gos->order_reference = order_reference;
    723   gos->osc = osc;
    724   gos->wired = wired;
    725   gos->refunded = refunded;
    726   gos->http_status = http_status;
    727   if (wired)
    728   {
    729     for (const char **clabel = transfers; *clabel != NULL; ++clabel)
    730     {
    731       GNUNET_array_append (gos->transfers,
    732                            gos->transfers_length,
    733                            *clabel);
    734     }
    735   }
    736   if (refunded)
    737   {
    738     for (const char **clabel = refunds; *clabel != NULL; ++clabel)
    739     {
    740       GNUNET_array_append (gos->refunds,
    741                            gos->refunds_length,
    742                            *clabel);
    743     }
    744   }
    745   if (NULL != forgets)
    746   {
    747     for (const char **clabel = forgets; *clabel != NULL; ++clabel)
    748     {
    749       GNUNET_array_append (gos->forgets,
    750                            gos->forgets_length,
    751                            *clabel);
    752     }
    753   }
    754   {
    755     struct TALER_TESTING_Command cmd = {
    756       .cls = gos,
    757       .label = label,
    758       .run = &merchant_get_order_run,
    759       .cleanup = &merchant_get_order_cleanup
    760     };
    761 
    762     return cmd;
    763   }
    764 }
    765 
    766 
    767 struct TALER_TESTING_Command
    768 TALER_TESTING_cmd_merchant_get_order3 (
    769   const char *label,
    770   const char *merchant_url,
    771   const char *order_reference,
    772   enum TALER_MERCHANT_OrderStatusCode osc,
    773   const char *session_id,
    774   const char *repurchase_order_ref,
    775   unsigned int expected_http_status)
    776 {
    777   struct MerchantGetOrderState *gos;
    778 
    779   gos = GNUNET_new (struct MerchantGetOrderState);
    780   gos->merchant_url = merchant_url;
    781   gos->order_reference = order_reference;
    782   gos->osc = osc;
    783   gos->session_id = session_id;
    784   gos->repurchase_order_ref = repurchase_order_ref;
    785   gos->http_status = expected_http_status;
    786   {
    787     struct TALER_TESTING_Command cmd = {
    788       .cls = gos,
    789       .label = label,
    790       .run = &merchant_get_order_run,
    791       .cleanup = &merchant_get_order_cleanup
    792     };
    793 
    794     return cmd;
    795   }
    796 }
    797 
    798 
    799 struct TALER_TESTING_Command
    800 TALER_TESTING_cmd_merchant_get_order4 (
    801   const char *label,
    802   const char *merchant_url,
    803   const char *order_reference,
    804   enum TALER_MERCHANT_OrderStatusCode osc,
    805   uint32_t expected_min_age,
    806   unsigned int expected_http_status)
    807 {
    808   struct MerchantGetOrderState *gos;
    809 
    810   gos = GNUNET_new (struct MerchantGetOrderState);
    811   gos->merchant_url = merchant_url;
    812   gos->order_reference = order_reference;
    813   gos->osc = osc;
    814   gos->expected_min_age = expected_min_age;
    815   gos->http_status = expected_http_status;
    816   {
    817     struct TALER_TESTING_Command cmd = {
    818       .cls = gos,
    819       .label = label,
    820       .run = &merchant_get_order_run,
    821       .cleanup = &merchant_get_order_cleanup
    822     };
    823 
    824     return cmd;
    825   }
    826 }
    827 
    828 
    829 struct MerchantPollOrderConcludeState
    830 {
    831   /**
    832    * The interpreter state.
    833    */
    834   struct TALER_TESTING_Interpreter *is;
    835 
    836   /**
    837    * Reference to a command that can provide a poll order start command.
    838    */
    839   const char *start_reference;
    840 
    841   /**
    842    * Task to wait for the deadline.
    843    */
    844   struct GNUNET_SCHEDULER_Task *task;
    845 
    846   /**
    847    * Expected HTTP response status code.
    848    */
    849   unsigned int expected_http_status;
    850 };
    851 
    852 
    853 struct MerchantPollOrderStartState
    854 {
    855   /**
    856    * The merchant base URL.
    857    */
    858   const char *merchant_url;
    859 
    860   /**
    861    * The handle to the current GET /private/orders/$ORDER_ID request.
    862    */
    863   struct TALER_MERCHANT_GetPrivateOrderHandle *ogh;
    864 
    865   /**
    866    * The interpreter state.
    867    */
    868   struct TALER_TESTING_Interpreter *is;
    869 
    870   /**
    871    * Reference to a command that created an order.
    872    */
    873   const char *order_id;
    874 
    875   /**
    876    * How long to wait for server to return a response.
    877    */
    878   struct GNUNET_TIME_Relative timeout;
    879 
    880   /**
    881    * Conclude state waiting for completion (if any).
    882    */
    883   struct MerchantPollOrderConcludeState *cs;
    884 
    885   /**
    886    * The HTTP status code returned by the backend.
    887    */
    888   unsigned int http_status;
    889 
    890   /**
    891    * When the request should be completed by.
    892    */
    893   struct GNUNET_TIME_Absolute deadline;
    894 };
    895 
    896 
    897 /**
    898  * Task called when either the timeout for the GET /private/order/$ID command
    899  * expired or we got a response.  Checks if the result is what we expected.
    900  *
    901  * @param cls a `struct MerchantPollOrderConcludeState`
    902  */
    903 static void
    904 conclude_task (void *cls)
    905 {
    906   struct MerchantPollOrderConcludeState *ppc = cls;
    907   const struct TALER_TESTING_Command *poll_cmd;
    908   struct MerchantPollOrderStartState *cps;
    909   struct GNUNET_TIME_Absolute now;
    910 
    911   ppc->task = NULL;
    912   poll_cmd =
    913     TALER_TESTING_interpreter_lookup_command (ppc->is,
    914                                               ppc->start_reference);
    915   if (NULL == poll_cmd)
    916     TALER_TESTING_FAIL (ppc->is);
    917   cps = poll_cmd->cls;
    918   if (NULL != cps->ogh)
    919   {
    920     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    921                 "Expected poll GET /private/orders/$ORDER_ID to have completed, but it did not!\n");
    922     TALER_TESTING_FAIL (ppc->is);
    923   }
    924   if (cps->http_status != ppc->expected_http_status)
    925   {
    926     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    927                 "Expected HTTP status %u, got %u\n",
    928                 ppc->expected_http_status,
    929                 cps->http_status);
    930     TALER_TESTING_FAIL (ppc->is);
    931   }
    932   now = GNUNET_TIME_absolute_get ();
    933   if ((GNUNET_TIME_absolute_add (cps->deadline,
    934                                  GNUNET_TIME_UNIT_SECONDS).abs_value_us <
    935        now.abs_value_us) )
    936   {
    937     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    938                 "Expected answer to be delayed until %llu, but got response at %llu\n",
    939                 (unsigned long long) cps->deadline.abs_value_us,
    940                 (unsigned long long) now.abs_value_us);
    941     TALER_TESTING_FAIL (ppc->is);
    942   }
    943   TALER_TESTING_interpreter_next (ppc->is);
    944 }
    945 
    946 
    947 /**
    948  * Callback to process a GET /private/orders/$ID request
    949  *
    950  * @param cls closure
    951  * @param osr order status response details
    952  */
    953 static void
    954 merchant_poll_order_cb (
    955   void *cls,
    956   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    957 {
    958   struct MerchantPollOrderStartState *pos = cls;
    959   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    960 
    961   pos->ogh = NULL;
    962   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    963               "GET /private/orders/$ID finished with status %u.\n",
    964               hr->http_status);
    965   pos->http_status = hr->http_status;
    966   switch (hr->http_status)
    967   {
    968   case MHD_HTTP_OK:
    969     // FIXME: keep data from 'osr' here for checking?
    970     break;
    971   default:
    972     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    973                 "Unhandled HTTP status.\n");
    974   }
    975   if (NULL != pos->cs)
    976   {
    977     GNUNET_SCHEDULER_cancel (pos->cs->task);
    978     pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
    979                                               pos->cs);
    980   }
    981 }
    982 
    983 
    984 /**
    985  * Run the "GET order" CMD.
    986  *
    987  * @param cls closure.
    988  * @param cmd command being run now.
    989  * @param is interpreter state.
    990  */
    991 static void
    992 merchant_poll_order_start_run (void *cls,
    993                                const struct TALER_TESTING_Command *cmd,
    994                                struct TALER_TESTING_Interpreter *is)
    995 {
    996   struct MerchantPollOrderStartState *pos = cls;
    997 
    998   /* add 1s grace time to timeout */
    999   pos->deadline
   1000     = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
   1001                                 GNUNET_TIME_UNIT_SECONDS);
   1002   pos->is = is;
   1003   pos->ogh = TALER_MERCHANT_get_private_order_create (
   1004     TALER_TESTING_interpreter_get_context (is),
   1005     pos->merchant_url,
   1006     pos->order_id);
   1007   GNUNET_assert (NULL != pos->ogh);
   1008   if (pos->timeout.rel_value_us > 0)
   1009   {
   1010     TALER_MERCHANT_get_private_order_set_options (
   1011       pos->ogh,
   1012       TALER_MERCHANT_get_private_order_option_timeout (
   1013         pos->timeout));
   1014   }
   1015   {
   1016     enum TALER_ErrorCode ec;
   1017 
   1018     ec = TALER_MERCHANT_get_private_order_start (
   1019       pos->ogh,
   1020       &merchant_poll_order_cb,
   1021       pos);
   1022     GNUNET_assert (TALER_EC_NONE == ec);
   1023   }
   1024   /* We CONTINUE to run the interpreter while the long-polled command
   1025      completes asynchronously! */
   1026   TALER_TESTING_interpreter_next (pos->is);
   1027 }
   1028 
   1029 
   1030 /**
   1031  * Free the state of a "GET order" CMD, and possibly
   1032  * cancel a pending operation thereof.
   1033  *
   1034  * @param cls closure.
   1035  * @param cmd command being run.
   1036  */
   1037 static void
   1038 merchant_poll_order_start_cleanup (void *cls,
   1039                                    const struct TALER_TESTING_Command *cmd)
   1040 {
   1041   struct MerchantPollOrderStartState *pos = cls;
   1042 
   1043   if (NULL != pos->ogh)
   1044   {
   1045     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1046                 "Command `%s' was not terminated\n",
   1047                 TALER_TESTING_interpreter_get_current_label (
   1048                   pos->is));
   1049     TALER_MERCHANT_get_private_order_cancel (pos->ogh);
   1050   }
   1051   GNUNET_free (pos);
   1052 }
   1053 
   1054 
   1055 struct TALER_TESTING_Command
   1056 TALER_TESTING_cmd_poll_order_start (
   1057   const char *label,
   1058   const char *merchant_url,
   1059   const char *order_id,
   1060   struct GNUNET_TIME_Relative timeout)
   1061 {
   1062   struct MerchantPollOrderStartState *pos;
   1063 
   1064   pos = GNUNET_new (struct MerchantPollOrderStartState);
   1065   pos->order_id = order_id;
   1066   pos->merchant_url = merchant_url;
   1067   pos->timeout = timeout;
   1068   {
   1069     struct TALER_TESTING_Command cmd = {
   1070       .cls = pos,
   1071       .label = label,
   1072       .run = &merchant_poll_order_start_run,
   1073       .cleanup = &merchant_poll_order_start_cleanup
   1074     };
   1075 
   1076     return cmd;
   1077   }
   1078 }
   1079 
   1080 
   1081 /**
   1082  * Run the "GET order conclude" CMD.
   1083  *
   1084  * @param cls closure.
   1085  * @param cmd command being run now.
   1086  * @param is interpreter state.
   1087  */
   1088 static void
   1089 merchant_poll_order_conclude_run (void *cls,
   1090                                   const struct TALER_TESTING_Command *cmd,
   1091                                   struct TALER_TESTING_Interpreter *is)
   1092 {
   1093   struct MerchantPollOrderConcludeState *poc = cls;
   1094   const struct TALER_TESTING_Command *poll_cmd;
   1095   struct MerchantPollOrderStartState *pos;
   1096 
   1097   poc->is = is;
   1098   poll_cmd =
   1099     TALER_TESTING_interpreter_lookup_command (is,
   1100                                               poc->start_reference);
   1101   if (NULL == poll_cmd)
   1102     TALER_TESTING_FAIL (poc->is);
   1103   GNUNET_assert (poll_cmd->run == &merchant_poll_order_start_run);
   1104   pos = poll_cmd->cls;
   1105   pos->cs = poc;
   1106   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1107               "Waiting on GET /private/orders/$ID of %s (%s)\n",
   1108               poc->start_reference,
   1109               (NULL == pos->ogh)
   1110               ? "finished"
   1111               : "active");
   1112   if (NULL == pos->ogh)
   1113     poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
   1114                                           poc);
   1115   else
   1116     poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
   1117                                          &conclude_task,
   1118                                          poc);
   1119 }
   1120 
   1121 
   1122 /**
   1123  * Free the state of a "GET order" CMD, and possibly
   1124  * cancel a pending operation thereof.
   1125  *
   1126  * @param cls closure.
   1127  * @param cmd command being run.
   1128  */
   1129 static void
   1130 merchant_poll_order_conclude_cleanup (void *cls,
   1131                                       const struct TALER_TESTING_Command *cmd)
   1132 {
   1133   struct MerchantPollOrderConcludeState *poc = cls;
   1134 
   1135   if (NULL != poc->task)
   1136   {
   1137     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1138                 "Command `%s' was not terminated\n",
   1139                 TALER_TESTING_interpreter_get_current_label (
   1140                   poc->is));
   1141     GNUNET_SCHEDULER_cancel (poc->task);
   1142     poc->task = NULL;
   1143   }
   1144   GNUNET_free (poc);
   1145 }
   1146 
   1147 
   1148 struct TALER_TESTING_Command
   1149 TALER_TESTING_cmd_poll_order_conclude (const char *label,
   1150                                        unsigned int http_status,
   1151                                        const char *poll_start_reference)
   1152 {
   1153   struct MerchantPollOrderConcludeState *cps;
   1154 
   1155   cps = GNUNET_new (struct MerchantPollOrderConcludeState);
   1156   cps->start_reference = poll_start_reference;
   1157   cps->expected_http_status = http_status;
   1158   {
   1159     struct TALER_TESTING_Command cmd = {
   1160       .cls = cps,
   1161       .label = label,
   1162       .run = &merchant_poll_order_conclude_run,
   1163       .cleanup = &merchant_poll_order_conclude_cleanup
   1164     };
   1165 
   1166     return cmd;
   1167   }
   1168 }
   1169 
   1170 
   1171 /* end of testing_api_cmd_merchant_get_order.c */