merchant

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

mb_credit.c (10020B)


      1 /*
      2   This file is part of GNU Taler
      3   Copyright (C) 2017--2023 Taler Systems SA
      4 
      5   Taler is free software; you can redistribute it and/or
      6   modify it under the terms of the GNU General Public License
      7   as published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   Taler is distributed in the hope that it will be useful,
     11   but 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,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file bank/mb_credit.c
     21  * @brief Implementation of the /history
     22  *        requests of the libeufin's Taler merchant facade
     23  * @author Christian Grothoff
     24  * @author Marcello Stanisci
     25  */
     26 #include "platform.h"
     27 #include "mb_common.h"
     28 #include <microhttpd.h> /* just for HTTP status codes */
     29 
     30 
     31 /**
     32  * @brief A /history Handle
     33  */
     34 struct TALER_MERCHANT_BANK_CreditHistoryHandle
     35 {
     36 
     37   /**
     38    * The url for this request.
     39    */
     40   char *request_url;
     41 
     42   /**
     43    * Handle for the request.
     44    */
     45   struct GNUNET_CURL_Job *job;
     46 
     47   /**
     48    * Function to call with the result.
     49    */
     50   TALER_MERCHANT_BANK_CreditHistoryCallback hcb;
     51 
     52   /**
     53    * Closure for @a cb.
     54    */
     55   void *hcb_cls;
     56 };
     57 
     58 
     59 /**
     60  * Parse history given in JSON format and invoke the callback on each item.
     61  *
     62  * @param hh handle to the account history request
     63  * @param history JSON array with the history
     64  * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
     65  *         were set,
     66  *         #GNUNET_SYSERR if there was a protocol violation in @a history
     67  */
     68 static enum GNUNET_GenericReturnValue
     69 parse_account_history (struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh,
     70                        const json_t *history)
     71 {
     72   const json_t *history_array;
     73   struct TALER_FullPayto credit_account_uri;
     74   struct GNUNET_JSON_Specification spec[] = {
     75     GNUNET_JSON_spec_array_const ("incoming_transactions",
     76                                   &history_array),
     77     TALER_JSON_spec_full_payto_uri ("credit_account",
     78                                     &credit_account_uri),
     79     GNUNET_JSON_spec_end ()
     80   };
     81 
     82   if (GNUNET_OK !=
     83       GNUNET_JSON_parse (history,
     84                          spec,
     85                          NULL,
     86                          NULL))
     87   {
     88     GNUNET_break_op (0);
     89     return GNUNET_SYSERR;
     90   }
     91   for (unsigned int i = 0; i<json_array_size (history_array); i++)
     92   {
     93     struct TALER_MERCHANT_BANK_CreditDetails td;
     94     uint64_t row_id;
     95     struct GNUNET_JSON_Specification hist_spec[] = {
     96       TALER_JSON_spec_amount_any ("amount",
     97                                   &td.amount),
     98       GNUNET_JSON_spec_timestamp ("date",
     99                                   &td.execution_date),
    100       GNUNET_JSON_spec_uint64 ("row_id",
    101                                &row_id),
    102       GNUNET_JSON_spec_string ("subject",
    103                                &td.wire_subject),
    104       TALER_JSON_spec_full_payto_uri ("debit_account",
    105                                       &td.debit_account_uri),
    106       GNUNET_JSON_spec_end ()
    107     };
    108     json_t *transaction = json_array_get (history_array,
    109                                           i);
    110 
    111     if (GNUNET_OK !=
    112         GNUNET_JSON_parse (transaction,
    113                            hist_spec,
    114                            NULL, NULL))
    115     {
    116       GNUNET_break_op (0);
    117       return GNUNET_SYSERR;
    118     }
    119     td.credit_account_uri = credit_account_uri;
    120     if (GNUNET_OK !=
    121         hh->hcb (hh->hcb_cls,
    122                  MHD_HTTP_OK,
    123                  TALER_EC_NONE,
    124                  row_id,
    125                  &td))
    126     {
    127       hh->hcb = NULL;
    128       GNUNET_JSON_parse_free (hist_spec);
    129       return GNUNET_OK;
    130     }
    131     GNUNET_JSON_parse_free (hist_spec);
    132   }
    133   return GNUNET_OK;
    134 }
    135 
    136 
    137 /**
    138  * Function called when we're done processing the
    139  * HTTP "/history" request.
    140  *
    141  * @param cls the `struct TALER_MERCHANT_BANK_CreditHistoryHandle`
    142  * @param response_code HTTP response code, 0 on error
    143  * @param response parsed JSON result, NULL on error
    144  */
    145 static void
    146 handle_credit_history_finished (void *cls,
    147                                 long response_code,
    148                                 const void *response)
    149 {
    150   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh = cls;
    151   const json_t *j = response;
    152   enum TALER_ErrorCode ec;
    153 
    154   hh->job = NULL;
    155   switch (response_code)
    156   {
    157   case 0:
    158     ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    159     break;
    160   case MHD_HTTP_OK:
    161     if (GNUNET_OK !=
    162         parse_account_history (hh,
    163                                j))
    164     {
    165       GNUNET_break_op (0);
    166       response_code = 0;
    167       ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    168       break;
    169     }
    170     response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
    171     ec = TALER_EC_NONE;
    172     break;
    173   case MHD_HTTP_NO_CONTENT:
    174     ec = TALER_EC_NONE;
    175     break;
    176   case MHD_HTTP_BAD_REQUEST:
    177     /* This should never happen, either us or the bank is buggy
    178        (or API version conflict); just pass JSON reply to the application */
    179     GNUNET_break_op (0);
    180     ec = TALER_JSON_get_error_code (j);
    181     break;
    182   case MHD_HTTP_UNAUTHORIZED:
    183     /* Nothing really to verify, bank says the HTTP Authentication
    184        failed. May happen if HTTP authentication is used and the
    185        user supplied a wrong username/password combination. */
    186     ec = TALER_JSON_get_error_code (j);
    187     break;
    188   case MHD_HTTP_NOT_FOUND:
    189     /* Nothing really to verify: the bank is either unaware
    190        of the endpoint (not a bank), or of the account.
    191        We should pass the JSON (?) reply to the application */
    192     ec = TALER_JSON_get_error_code (j);
    193     break;
    194   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    195     /* Server had an internal issue; we should retry, but this API
    196        leaves this to the application */
    197     ec = TALER_JSON_get_error_code (j);
    198     break;
    199   default:
    200     /* unexpected response code */
    201     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    202                 "Unexpected response code %u\n",
    203                 (unsigned int) response_code);
    204     ec = TALER_JSON_get_error_code (j);
    205     break;
    206   }
    207   if (NULL != hh->hcb)
    208     hh->hcb (hh->hcb_cls,
    209              response_code,
    210              ec,
    211              0LLU,
    212              NULL);
    213   TALER_MERCHANT_BANK_credit_history_cancel (hh);
    214 }
    215 
    216 
    217 struct TALER_MERCHANT_BANK_CreditHistoryHandle *
    218 TALER_MERCHANT_BANK_credit_history (
    219   struct GNUNET_CURL_Context *ctx,
    220   const struct TALER_MERCHANT_BANK_AuthenticationData *auth,
    221   uint64_t start_row,
    222   int64_t num_results,
    223   struct GNUNET_TIME_Relative timeout,
    224   TALER_MERCHANT_BANK_CreditHistoryCallback hres_cb,
    225   void *hres_cb_cls)
    226 {
    227   char url[128];
    228   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh;
    229   CURL *eh;
    230   unsigned long long tms;
    231 
    232   if (0 == num_results)
    233   {
    234     GNUNET_break (0);
    235     return NULL;
    236   }
    237 
    238   tms = (unsigned long long) (timeout.rel_value_us
    239                               / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    240   if ( ( (UINT64_MAX == start_row) &&
    241          (0 > num_results) ) ||
    242        ( (0 == start_row) &&
    243          (0 < num_results) ) )
    244   {
    245     if ( (0 < num_results) &&
    246          (! GNUNET_TIME_relative_is_zero (timeout)) )
    247       GNUNET_snprintf (url,
    248                        sizeof (url),
    249                        "history?delta=%lld&long_poll_ms=%llu",
    250                        (long long) num_results,
    251                        tms);
    252     else
    253       GNUNET_snprintf (url,
    254                        sizeof (url),
    255                        "history?delta=%lld",
    256                        (long long) num_results);
    257   }
    258   else
    259   {
    260     if ( (0 < num_results) &&
    261          (! GNUNET_TIME_relative_is_zero (timeout)) )
    262       GNUNET_snprintf (url,
    263                        sizeof (url),
    264                        "history?delta=%lld&start=%llu&long_poll_ms=%llu",
    265                        (long long) num_results,
    266                        (unsigned long long) start_row,
    267                        tms);
    268     else
    269       GNUNET_snprintf (url,
    270                        sizeof (url),
    271                        "history?delta=%lld&start=%llu",
    272                        (long long) num_results,
    273                        (unsigned long long) start_row);
    274   }
    275   hh = GNUNET_new (struct TALER_MERCHANT_BANK_CreditHistoryHandle);
    276   hh->hcb = hres_cb;
    277   hh->hcb_cls = hres_cb_cls;
    278   hh->request_url = TALER_url_join (auth->wire_gateway_url,
    279                                     url,
    280                                     NULL);
    281   if (NULL == hh->request_url)
    282   {
    283     GNUNET_free (hh);
    284     GNUNET_break (0);
    285     return NULL;
    286   }
    287   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    288               "Requesting credit history at `%s'\n",
    289               hh->request_url);
    290   eh = curl_easy_init ();
    291   if ( (NULL == eh) ||
    292        (GNUNET_OK !=
    293         TALER_MERCHANT_BANK_setup_auth_ (eh,
    294                                          auth)) ||
    295        (CURLE_OK !=
    296         curl_easy_setopt (eh,
    297                           CURLOPT_URL,
    298                           hh->request_url)) )
    299   {
    300     GNUNET_break (0);
    301     TALER_MERCHANT_BANK_credit_history_cancel (hh);
    302     if (NULL != eh)
    303       curl_easy_cleanup (eh);
    304     return NULL;
    305   }
    306   if (0 != tms)
    307   {
    308     GNUNET_break (CURLE_OK ==
    309                   curl_easy_setopt (eh,
    310                                     CURLOPT_TIMEOUT_MS,
    311                                     (long) (tms + 100L)));
    312   }
    313 #if DEBUG
    314   GNUNET_break (CURLE_OK ==
    315                 curl_easy_setopt (eh,
    316                                   CURLOPT_VERBOSE,
    317                                   1L));
    318 #endif
    319   hh->job = GNUNET_CURL_job_add2 (ctx,
    320                                   eh,
    321                                   NULL,
    322                                   &handle_credit_history_finished,
    323                                   hh);
    324   return hh;
    325 }
    326 
    327 
    328 void
    329 TALER_MERCHANT_BANK_credit_history_cancel (
    330   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh)
    331 {
    332   if (NULL != hh->job)
    333   {
    334     GNUNET_CURL_job_cancel (hh->job);
    335     hh->job = NULL;
    336   }
    337   GNUNET_free (hh->request_url);
    338   GNUNET_free (hh);
    339 }
    340 
    341 
    342 /* end of mb_credit.c */