merchant

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

testing_api_cmd_post_transfers.c (13317B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020, 2023, 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 src/testing/testing_api_cmd_post_transfers.c
     21  * @brief command to test POST /transfers
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 struct PostTransfersState;
     26 #define TALER_MERCHANT_POST_PRIVATE_TRANSFERS_RESULT_CLOSURE struct PostTransfersState
     27 #include <taler/taler_exchange_service.h>
     28 #include <taler/taler_testing_lib.h>
     29 #include "taler/taler_merchant_service.h"
     30 #include "taler/taler_merchant_testing_lib.h"
     31 #include <taler/merchant/post-private-transfers.h>
     32 
     33 
     34 /**
     35  * State of a "POST /transfers" CMD.
     36  */
     37 struct PostTransfersState
     38 {
     39 
     40   /**
     41    * Handle for a "POST /transfers" request.
     42    */
     43   struct TALER_MERCHANT_PostPrivateTransfersHandle *pth;
     44 
     45   /**
     46    * Handle for a "GET" bank account history request.
     47    */
     48   struct TALER_BANK_DebitHistoryHandle *dhh;
     49 
     50   /**
     51    * The interpreter state.
     52    */
     53   struct TALER_TESTING_Interpreter *is;
     54 
     55   /**
     56    * Base URL of the merchant serving the request.
     57    */
     58   const char *merchant_url;
     59 
     60   /**
     61    * URL of the bank to run history on.
     62    */
     63   char *exchange_url;
     64 
     65   /**
     66    * Credit account of the merchant.
     67    */
     68   struct TALER_FullPayto credit_account;
     69 
     70   /**
     71    * Payto URI to filter on.
     72    */
     73   struct TALER_FullPayto payto_uri;
     74 
     75   /**
     76    * Set to the hash of the @e payto_uri.
     77    */
     78   struct TALER_FullPaytoHashP h_payto;
     79 
     80   /**
     81    * Set to the hash of the normalized @e payto_uri.
     82    */
     83   struct TALER_NormalizedPaytoHashP h_normalized_payto;
     84 
     85   /**
     86    * Authentication details to authenticate to the bank.
     87    */
     88   struct TALER_BANK_AuthenticationData auth;
     89 
     90   /**
     91    * Set once we discovered the WTID.
     92    */
     93   struct TALER_WireTransferIdentifierRawP wtid;
     94 
     95   /**
     96    * the credit amount to look for at @e bank_url.
     97    */
     98   struct TALER_Amount credit_amount;
     99 
    100   /**
    101    * Expected HTTP response code.
    102    */
    103   unsigned int http_status;
    104 
    105   /**
    106    * Array of deposit command labels we expect to see aggregated.
    107    */
    108   const char **deposits;
    109 
    110   /**
    111    * Serial number of the wire transfer in the merchant backend,
    112    * set by #TALER_TESTING_cmd_merchant_get_transfers(). 0 if unknown.
    113    */
    114   uint64_t serial;
    115 
    116   /**
    117    * Length of @e deposits.
    118    */
    119   unsigned int deposits_length;
    120 
    121 };
    122 
    123 
    124 /**
    125  * Callback for a POST /transfers operation.
    126  *
    127  * @param cls closure for this function
    128  * @param ptr response details
    129  */
    130 static void
    131 transfers_cb (struct PostTransfersState *pts,
    132               const struct TALER_MERCHANT_PostPrivateTransfersResponse *ptr)
    133 {
    134 
    135   pts->pth = NULL;
    136   if (pts->http_status != ptr->hr.http_status)
    137   {
    138     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    139                 "Unexpected response code %u (%d) to command %s\n",
    140                 ptr->hr.http_status,
    141                 (int) ptr->hr.ec,
    142                 TALER_TESTING_interpreter_get_current_label (pts->is));
    143     GNUNET_break (0);
    144     TALER_TESTING_interpreter_fail (pts->is);
    145     return;
    146   }
    147   switch (ptr->hr.http_status)
    148   {
    149   case MHD_HTTP_NO_CONTENT:
    150     break;
    151   case MHD_HTTP_UNAUTHORIZED:
    152     break;
    153   case MHD_HTTP_NOT_FOUND:
    154     break;
    155   case MHD_HTTP_GATEWAY_TIMEOUT:
    156     break;
    157   default:
    158     GNUNET_break (0);
    159     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    160                 "Unhandled HTTP status %u for POST /transfers.\n",
    161                 ptr->hr.http_status);
    162   }
    163   TALER_TESTING_interpreter_next (pts->is);
    164 }
    165 
    166 
    167 /**
    168  * Offers information from the POST /transfers CMD state to other
    169  * commands.
    170  *
    171  * @param cls closure
    172  * @param[out] ret result (could be anything)
    173  * @param trait name of the trait
    174  * @param index index number of the object to extract.
    175  * @return #GNUNET_OK on success
    176  */
    177 static enum GNUNET_GenericReturnValue
    178 post_transfers_traits (void *cls,
    179                        const void **ret,
    180                        const char *trait,
    181                        unsigned int index)
    182 {
    183   struct PostTransfersState *pts = cls;
    184   struct TALER_TESTING_Trait traits[] = {
    185     TALER_TESTING_make_trait_wtid (&pts->wtid),
    186     TALER_TESTING_make_trait_credit_payto_uri (&pts->credit_account),
    187     TALER_TESTING_make_trait_h_full_payto (&pts->h_payto),
    188     TALER_TESTING_make_trait_h_normalized_payto (&pts->h_normalized_payto),
    189     TALER_TESTING_make_trait_amount (&pts->credit_amount),
    190     TALER_TESTING_make_trait_exchange_url (pts->exchange_url),
    191     TALER_TESTING_make_trait_bank_row (&pts->serial),
    192     TALER_TESTING_trait_end (),
    193   };
    194 
    195   return TALER_TESTING_get_trait (traits,
    196                                   ret,
    197                                   trait,
    198                                   index);
    199 }
    200 
    201 
    202 /**
    203  * Run the "POST /transfers" CMD. First, get the bank history to find
    204  * the wtid.
    205  *
    206  * @param cls closure.
    207  * @param cmd command being run now.
    208  * @param is interpreter state.
    209  */
    210 static void
    211 post_transfers_run2 (void *cls,
    212                      const struct TALER_TESTING_Command *cmd,
    213                      struct TALER_TESTING_Interpreter *is)
    214 {
    215   struct PostTransfersState *pts = cls;
    216 
    217   pts->is = is;
    218   pts->pth = TALER_MERCHANT_post_private_transfers_create (
    219     TALER_TESTING_interpreter_get_context (pts->is),
    220     pts->merchant_url,
    221     &pts->credit_amount,
    222     &pts->wtid,
    223     pts->credit_account,
    224     pts->exchange_url);
    225   {
    226     enum TALER_ErrorCode ec;
    227 
    228     ec = TALER_MERCHANT_post_private_transfers_start (
    229       pts->pth,
    230       &transfers_cb,
    231       pts);
    232     GNUNET_assert (TALER_EC_NONE == ec);
    233   }
    234 }
    235 
    236 
    237 /**
    238  * Callbacks of this type are used to serve the result of asking
    239  * the bank for the debit transaction history.
    240  *
    241  * @param cls closure with a `struct PostTransfersState *`
    242  * @param reply details from the HTTP response code
    243  */
    244 static void
    245 debit_cb (
    246   void *cls,
    247   const struct TALER_BANK_DebitHistoryResponse *reply)
    248 {
    249   struct PostTransfersState *pts = cls;
    250 
    251   pts->dhh = NULL;
    252   switch (reply->http_status)
    253   {
    254   case MHD_HTTP_OK:
    255     /* handled below */
    256     break;
    257   case MHD_HTTP_NO_CONTENT:
    258     GNUNET_break (0);
    259     TALER_TESTING_interpreter_fail (pts->is);
    260     return;
    261   default:
    262     GNUNET_break (0);
    263     TALER_TESTING_interpreter_fail (pts->is);
    264     return;
    265   }
    266   for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
    267   {
    268     const struct TALER_BANK_DebitDetails *details
    269       = &reply->details.ok.details[i];
    270 
    271     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    272                 "Bank reports transfer of %s to %s\n",
    273                 TALER_amount2s (&details->amount),
    274                 details->credit_account_uri.full_payto);
    275     if (0 != TALER_amount_cmp (&pts->credit_amount,
    276                                &details->amount))
    277       continue;
    278     pts->wtid = details->wtid;
    279     pts->credit_account.full_payto
    280       = GNUNET_strdup (details->credit_account_uri.full_payto);
    281     pts->exchange_url = GNUNET_strdup (details->exchange_base_url);
    282     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    283                 "Bank transfer found, checking with merchant backend at %s about %s from %s to %s with %s\n",
    284                 pts->merchant_url,
    285                 TALER_amount2s (&pts->credit_amount),
    286                 pts->payto_uri.full_payto,
    287                 pts->exchange_url,
    288                 TALER_B2S (&pts->wtid));
    289     pts->pth = TALER_MERCHANT_post_private_transfers_create (
    290       TALER_TESTING_interpreter_get_context (pts->is),
    291       pts->merchant_url,
    292       &pts->credit_amount,
    293       &pts->wtid,
    294       pts->credit_account,
    295       pts->exchange_url);
    296     {
    297       enum TALER_ErrorCode ec;
    298 
    299       ec = TALER_MERCHANT_post_private_transfers_start (
    300         pts->pth,
    301         &transfers_cb,
    302         pts);
    303       GNUNET_assert (TALER_EC_NONE == ec);
    304     }
    305     break;
    306   }
    307   if (NULL == pts->pth)
    308   {
    309     GNUNET_break (0);
    310     TALER_TESTING_interpreter_fail (pts->is);
    311     return;
    312   }
    313 }
    314 
    315 
    316 /**
    317  * Run the "POST /transfers" CMD. First, get the bank history to find
    318  * the wtid.
    319  *
    320  * @param cls closure.
    321  * @param cmd command being run now.
    322  * @param is interpreter state.
    323  */
    324 static void
    325 post_transfers_run (void *cls,
    326                     const struct TALER_TESTING_Command *cmd,
    327                     struct TALER_TESTING_Interpreter *is)
    328 {
    329   struct PostTransfersState *pts = cls;
    330 
    331   pts->is = is;
    332   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    333               "Looking for transfer of %s from %s at bank\n",
    334               TALER_amount2s (&pts->credit_amount),
    335               pts->payto_uri.full_payto);
    336   pts->dhh = TALER_BANK_debit_history (TALER_TESTING_interpreter_get_context (
    337                                          is),
    338                                        &pts->auth,
    339                                        UINT64_MAX,
    340                                        -INT64_MAX,
    341                                        GNUNET_TIME_UNIT_ZERO,
    342                                        &debit_cb,
    343                                        pts);
    344   GNUNET_assert (NULL != pts->dhh);
    345 }
    346 
    347 
    348 /**
    349  * Free the state of a "POST product" CMD, and possibly
    350  * cancel a pending operation thereof.
    351  *
    352  * @param cls closure.
    353  * @param cmd command being run.
    354  */
    355 static void
    356 post_transfers_cleanup (void *cls,
    357                         const struct TALER_TESTING_Command *cmd)
    358 {
    359   struct PostTransfersState *pts = cls;
    360 
    361   if (NULL != pts->pth)
    362   {
    363     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    364                 "POST /transfers operation did not complete\n");
    365     TALER_MERCHANT_post_private_transfers_cancel (pts->pth);
    366   }
    367   if (NULL != pts->dhh)
    368   {
    369     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    370                 "GET debit history operation did not complete\n");
    371     TALER_BANK_debit_history_cancel (pts->dhh);
    372   }
    373   GNUNET_array_grow (pts->deposits,
    374                      pts->deposits_length,
    375                      0);
    376   GNUNET_free (pts->exchange_url);
    377   GNUNET_free (pts->credit_account.full_payto);
    378   GNUNET_free (pts);
    379 }
    380 
    381 
    382 struct TALER_TESTING_Command
    383 TALER_TESTING_cmd_merchant_post_transfer (
    384   const char *label,
    385   const struct TALER_BANK_AuthenticationData *auth,
    386   struct TALER_FullPayto payto_uri,
    387   const char *merchant_url,
    388   const char *credit_amount,
    389   unsigned int http_code,
    390   ...)
    391 {
    392   struct PostTransfersState *pts;
    393 
    394   pts = GNUNET_new (struct PostTransfersState);
    395   pts->merchant_url = merchant_url;
    396   pts->auth = *auth;
    397   pts->payto_uri = payto_uri;
    398   TALER_full_payto_hash (payto_uri,
    399                          &pts->h_payto);
    400   TALER_full_payto_normalize_and_hash (payto_uri,
    401                                        &pts->h_normalized_payto);
    402   GNUNET_assert (GNUNET_OK ==
    403                  TALER_string_to_amount (credit_amount,
    404                                          &pts->credit_amount));
    405   pts->http_status = http_code;
    406   {
    407     const char *clabel;
    408     va_list ap;
    409 
    410     va_start (ap, http_code);
    411     while (NULL != (clabel = va_arg (ap, const char *)))
    412     {
    413       GNUNET_array_append (pts->deposits,
    414                            pts->deposits_length,
    415                            clabel);
    416     }
    417     va_end (ap);
    418   }
    419   {
    420     struct TALER_TESTING_Command cmd = {
    421       .cls = pts,
    422       .label = label,
    423       .run = &post_transfers_run,
    424       .cleanup = &post_transfers_cleanup,
    425       .traits = &post_transfers_traits
    426     };
    427 
    428     return cmd;
    429   }
    430 }
    431 
    432 
    433 struct TALER_TESTING_Command
    434 TALER_TESTING_cmd_merchant_post_transfer2 (
    435   const char *label,
    436   const char *merchant_url,
    437   struct TALER_FullPayto payto_uri,
    438   const char *credit_amount,
    439   const char *wtid,
    440   const char *exchange_url,
    441   unsigned int http_code)
    442 {
    443   struct PostTransfersState *pts;
    444 
    445   pts = GNUNET_new (struct PostTransfersState);
    446   pts->merchant_url = merchant_url;
    447   pts->credit_account.full_payto
    448     = GNUNET_strdup (payto_uri.full_payto);
    449   pts->exchange_url = GNUNET_strdup (exchange_url);
    450   GNUNET_assert (GNUNET_OK ==
    451                  TALER_string_to_amount (credit_amount,
    452                                          &pts->credit_amount));
    453   if (NULL == wtid)
    454   {
    455     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    456                                 &pts->wtid,
    457                                 sizeof (pts->wtid));
    458   }
    459   else
    460   {
    461     GNUNET_assert (GNUNET_OK ==
    462                    GNUNET_STRINGS_string_to_data (wtid,
    463                                                   strlen (wtid),
    464                                                   &pts->wtid,
    465                                                   sizeof (pts->wtid)));
    466   }
    467   pts->http_status = http_code;
    468   {
    469     struct TALER_TESTING_Command cmd = {
    470       .cls = pts,
    471       .label = label,
    472       .run = &post_transfers_run2,
    473       .cleanup = &post_transfers_cleanup,
    474       .traits = &post_transfers_traits
    475     };
    476 
    477     return cmd;
    478   }
    479 }
    480 
    481 
    482 void
    483 TALER_TESTING_cmd_merchant_post_transfer_set_serial (
    484   struct TALER_TESTING_Command *cmd,
    485   uint64_t serial)
    486 {
    487   struct PostTransfersState *pts = cmd->cls;
    488 
    489   GNUNET_assert (cmd->run = &post_transfers_run);
    490   pts->serial = serial;
    491 }
    492 
    493 
    494 /* end of testing_api_cmd_post_transfers.c */