merchant

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

testing_api_cmd_post_transfers.c (13222B)


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