exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

bank_api_debit.c (10246B)


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