merchant

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

testing_api_cmd_wallet_post_orders_refund.c (8836B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU General Public License as
      7   published by the Free Software Foundation; either version 3, or
      8   (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file src/testing/testing_api_cmd_wallet_post_orders_refund.c
     21  * @brief command to test refunds.
     22  * @author Marcello Stanisci
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 struct WalletRefundState;
     27 #define TALER_MERCHANT_POST_ORDERS_REFUND_RESULT_CLOSURE struct WalletRefundState
     28 #include <taler/taler_exchange_service.h>
     29 #include <taler/taler_testing_lib.h>
     30 #include "taler/taler_merchant_service.h"
     31 #include "taler/taler_merchant_testing_lib.h"
     32 #include <taler/merchant/post-orders-ORDER_ID-refund.h>
     33 
     34 
     35 /**
     36  * State for an "obtain refunds" CMD.
     37  */
     38 struct WalletRefundState
     39 {
     40   /**
     41    * Operation handle for a (public) POST /orders/$ID/refund request.
     42    */
     43   struct TALER_MERCHANT_PostOrdersRefundHandle *orh;
     44 
     45   /**
     46    * Base URL of the merchant serving the request.
     47    */
     48   const char *merchant_url;
     49 
     50   /**
     51    * Interpreter state.
     52    */
     53   struct TALER_TESTING_Interpreter *is;
     54 
     55   /**
     56    * Expected HTTP response code.
     57    */
     58   unsigned int http_code;
     59 
     60   /**
     61    * Label of the command that created the order we want to obtain refunds for.
     62    */
     63   const char *proposal_reference;
     64 
     65   /**
     66    * A list of refunds associated with this order.
     67    */
     68   const char **refunds;
     69 
     70   /**
     71    * The length of @e refunds.
     72    */
     73   unsigned int refunds_length;
     74 };
     75 
     76 
     77 /**
     78  * Process POST /refund (increase) response; just checking
     79  * if the HTTP response code is the one expected.
     80  *
     81  * @param cls closure
     82  * @param wrr response
     83  */
     84 static void
     85 refund_cb (
     86   struct WalletRefundState *wrs,
     87   const struct TALER_MERCHANT_PostOrdersRefundResponse *wrr)
     88 {
     89 
     90   wrs->orh = NULL;
     91   if (wrs->http_code != wrr->hr.http_status)
     92   {
     93     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     94                 "Expected status %u, got %u(%d) for refund increase\n",
     95                 wrs->http_code,
     96                 wrr->hr.http_status,
     97                 (int) wrr->hr.ec);
     98     TALER_TESTING_FAIL (wrs->is);
     99   }
    100   switch (wrr->hr.http_status)
    101   {
    102   case MHD_HTTP_OK:
    103     {
    104       struct TALER_Amount refunded_total;
    105       if (wrr->details.ok.num_refunds != wrs->refunds_length)
    106       {
    107         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    108                     "Number of refunds found (%u) does not match expected (%u)\n",
    109                     wrr->details.ok.num_refunds,
    110                     wrs->refunds_length);
    111         TALER_TESTING_interpreter_fail (wrs->is);
    112         return;
    113       }
    114       if (wrr->details.ok.num_refunds > 0)
    115         GNUNET_assert (GNUNET_OK ==
    116                        TALER_amount_set_zero (
    117                          wrr->details.ok.refunds[0].refund_amount.currency,
    118                          &refunded_total));
    119       for (unsigned int i = 0; i < wrr->details.ok.num_refunds; ++i)
    120       {
    121         const struct TALER_MERCHANT_PostOrdersRefundDetail *refund
    122           = &wrr->details.ok.refunds[wrr->details.ok.num_refunds - 1 - i];
    123         const struct TALER_TESTING_Command *refund_cmd;
    124         const struct TALER_Amount *expected_amount;
    125 
    126         refund_cmd = TALER_TESTING_interpreter_lookup_command (
    127           wrs->is,
    128           wrs->refunds[i]);
    129 
    130         if (GNUNET_OK !=
    131             TALER_TESTING_get_trait_amount (refund_cmd,
    132                                             &expected_amount))
    133         {
    134           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    135                       "Could not fetch refund amount\n");
    136           TALER_TESTING_interpreter_fail (wrs->is);
    137           return;
    138         }
    139         /* The most recent refunds are returned first */
    140         GNUNET_assert (0 <= TALER_amount_add (&refunded_total,
    141                                               &refunded_total,
    142                                               &refund->refund_amount));
    143         if ( (GNUNET_OK !=
    144               TALER_amount_cmp_currency (expected_amount,
    145                                          &refunded_total)) ||
    146              (0 != TALER_amount_cmp (expected_amount,
    147                                      &refunded_total)) )
    148         {
    149           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    150                       "Refund amounts do not match\n");
    151           TALER_TESTING_interpreter_fail (wrs->is);
    152           return;
    153         }
    154       }
    155     }
    156     break;
    157   default:
    158     break;
    159   }
    160   TALER_TESTING_interpreter_next (wrs->is);
    161 }
    162 
    163 
    164 /**
    165  * Run the "refund increase" CMD.
    166  *
    167  * @param cls closure.
    168  * @param cmd command currently being run.
    169  * @param is the interpreter state.
    170  */
    171 static void
    172 obtain_refunds_run (void *cls,
    173                     const struct TALER_TESTING_Command *cmd,
    174                     struct TALER_TESTING_Interpreter *is)
    175 {
    176   struct WalletRefundState *wrs = cls;
    177   const struct TALER_TESTING_Command *proposal_cmd =
    178     TALER_TESTING_interpreter_lookup_command (is,
    179                                               wrs->proposal_reference);
    180   const struct TALER_PrivateContractHashP *h_contract_terms;
    181   const char *order_id;
    182 
    183   if (NULL == proposal_cmd)
    184     TALER_TESTING_FAIL (is);
    185   if (GNUNET_OK !=
    186       TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
    187                                                 &h_contract_terms))
    188     TALER_TESTING_FAIL (is);
    189 
    190   {
    191     const json_t *contract_terms;
    192     const char *error_name;
    193     unsigned int error_line;
    194 
    195     if (GNUNET_OK !=
    196         TALER_TESTING_get_trait_contract_terms (proposal_cmd,
    197                                                 &contract_terms))
    198       TALER_TESTING_FAIL (is);
    199     {
    200       /* Get information that needs to be put verbatim in the
    201        * deposit permission */
    202       struct GNUNET_JSON_Specification spec[] = {
    203         GNUNET_JSON_spec_string ("order_id",
    204                                  &order_id),
    205         GNUNET_JSON_spec_end ()
    206       };
    207 
    208       if (GNUNET_OK !=
    209           GNUNET_JSON_parse (contract_terms,
    210                              spec,
    211                              &error_name,
    212                              &error_line))
    213       {
    214         char *js;
    215 
    216         js = json_dumps (contract_terms,
    217                          JSON_INDENT (1));
    218         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    219                     "Parser failed on %s:%u for input `%s'\n",
    220                     error_name,
    221                     error_line,
    222                     js);
    223         free (js);
    224         TALER_TESTING_FAIL (is);
    225       }
    226     }
    227   }
    228 
    229   wrs->is = is;
    230   wrs->orh = TALER_MERCHANT_post_orders_refund_create (
    231     TALER_TESTING_interpreter_get_context (is),
    232     wrs->merchant_url,
    233     order_id,
    234     h_contract_terms);
    235   {
    236     enum TALER_ErrorCode ec;
    237 
    238     ec = TALER_MERCHANT_post_orders_refund_start (
    239       wrs->orh,
    240       &refund_cb,
    241       wrs);
    242     GNUNET_assert (TALER_EC_NONE == ec);
    243   }
    244 }
    245 
    246 
    247 /**
    248  * Free the state of a "refund increase" CMD, and
    249  * possibly cancel a pending "refund increase" operation.
    250  *
    251  * @param cls closure
    252  * @param cmd command currently being freed.
    253  */
    254 static void
    255 obtain_refunds_cleanup (void *cls,
    256                         const struct TALER_TESTING_Command *cmd)
    257 {
    258   struct WalletRefundState *wrs = cls;
    259 
    260   if (NULL != wrs->orh)
    261   {
    262     TALER_LOG_WARNING ("Refund operation did not complete\n");
    263     TALER_MERCHANT_post_orders_refund_cancel (wrs->orh);
    264   }
    265   GNUNET_array_grow (wrs->refunds,
    266                      wrs->refunds_length,
    267                      0);
    268   GNUNET_free (wrs);
    269 }
    270 
    271 
    272 struct TALER_TESTING_Command
    273 TALER_TESTING_cmd_wallet_order_refund (const char *label,
    274                                        const char *merchant_url,
    275                                        const char *order_ref,
    276                                        unsigned int http_code,
    277                                        ...)
    278 {
    279   struct WalletRefundState *wrs;
    280 
    281   wrs = GNUNET_new (struct WalletRefundState);
    282   wrs->merchant_url = merchant_url;
    283   wrs->proposal_reference = order_ref;
    284   wrs->http_code = http_code;
    285   wrs->refunds_length = 0;
    286   {
    287     const char *clabel;
    288     va_list ap;
    289 
    290     va_start (ap, http_code);
    291     while (NULL != (clabel = va_arg (ap, const char *)))
    292     {
    293       GNUNET_array_append (wrs->refunds,
    294                            wrs->refunds_length,
    295                            clabel);
    296     }
    297     va_end (ap);
    298   }
    299   {
    300     struct TALER_TESTING_Command cmd = {
    301       .cls = wrs,
    302       .label = label,
    303       .run = &obtain_refunds_run,
    304       .cleanup = &obtain_refunds_cleanup
    305     };
    306 
    307     return cmd;
    308   }
    309 }