merchant

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

merchant_api_post_transfers.c (7673B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 2.1, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
     12 
     13   You should have received a copy of the GNU Lesser General Public License along with
     14   TALER; see the file COPYING.LGPL.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file merchant_api_post_transfers.c
     19  * @brief Implementation of the POST /transfers request of the merchant's HTTP API
     20  * @author Marcello Stanisci
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"
     24 #include <curl/curl.h>
     25 #include <jansson.h>
     26 #include <microhttpd.h> /* just for HTTP status codes */
     27 #include <gnunet/gnunet_util_lib.h>
     28 #include "taler_merchant_service.h"
     29 #include "merchant_api_curl_defaults.h"
     30 #include "merchant_api_common.h"
     31 #include <taler/taler_curl_lib.h>
     32 #include <taler/taler_json_lib.h>
     33 
     34 
     35 /**
     36  * @brief A handle for POSTing transfer data.
     37  */
     38 struct TALER_MERCHANT_PostTransfersHandle
     39 {
     40 
     41   /**
     42    * The url for this request.
     43    */
     44   char *url;
     45 
     46   /**
     47    * Handle for the request.
     48    */
     49   struct GNUNET_CURL_Job *job;
     50 
     51   /**
     52    * Function to call with the result.
     53    */
     54   TALER_MERCHANT_PostTransfersCallback cb;
     55 
     56   /**
     57    * Closure for @a cb.
     58    */
     59   void *cb_cls;
     60 
     61   /**
     62    * Reference to the execution context.
     63    */
     64   struct GNUNET_CURL_Context *ctx;
     65 
     66   /**
     67    * Minor context that holds body and headers.
     68    */
     69   struct TALER_CURL_PostContext post_ctx;
     70 
     71 };
     72 
     73 
     74 /**
     75  * Function called when we're done processing the
     76  * HTTP POST /transfers request.
     77  *
     78  * @param cls the `struct TALER_MERCHANT_PostTransfersHandle`
     79  * @param response_code HTTP response code, 0 on error
     80  * @param response response body, NULL if not in JSON
     81  */
     82 static void
     83 handle_post_transfers_finished (void *cls,
     84                                 long response_code,
     85                                 const void *response)
     86 {
     87   struct TALER_MERCHANT_PostTransfersHandle *pth = cls;
     88   struct TALER_MERCHANT_PostTransfersResponse ptr = {
     89     .hr.reply = response,
     90     .hr.http_status = (unsigned int) response_code
     91   };
     92 
     93   pth->job = NULL;
     94   switch (response_code)
     95   {
     96   case 0:
     97     ptr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     98     break;
     99   case MHD_HTTP_NO_CONTENT:
    100     break;
    101   case MHD_HTTP_UNAUTHORIZED:
    102     ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
    103     ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
    104     /* Nothing really to verify, merchant says we need to authenticate. */
    105     break;
    106   case MHD_HTTP_NOT_FOUND:
    107     /* Nothing really to verify, this should never
    108        happen, we should pass the JSON reply to the application */
    109     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    110                 "Did not find any data\n");
    111     ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
    112     ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
    113     break;
    114   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    115     /* Server had an internal issue; we should retry, but this API
    116        leaves this to the application */
    117     ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
    118     ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
    119     break;
    120   case MHD_HTTP_BAD_GATEWAY:
    121     /* Exchange had an issue; we should retry, but this API
    122        leaves this to the application */
    123     ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
    124     ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
    125     {
    126       uint32_t ehc;
    127       struct GNUNET_JSON_Specification ispec[] = {
    128         TALER_JSON_spec_ec ("exchange_code",
    129                             &ptr.details.bad_gateway.exchange_ec),
    130         GNUNET_JSON_spec_uint32 ("exchange_http_status",
    131                                  &ehc),
    132         GNUNET_JSON_spec_end ()
    133       };
    134 
    135       if (GNUNET_OK !=
    136           GNUNET_JSON_parse (ptr.hr.reply,
    137                              ispec,
    138                              NULL, NULL))
    139       {
    140         GNUNET_break_op (0);
    141         ptr.details.bad_gateway.exchange_http_status = 0;
    142         ptr.details.bad_gateway.exchange_ec = TALER_EC_NONE;
    143         break;
    144       }
    145       else
    146       {
    147         ptr.details.bad_gateway.exchange_http_status
    148           = (unsigned int) ehc;
    149         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    150                     "Exchange returned %u/%u\n",
    151                     (unsigned int) ptr.details.bad_gateway.exchange_ec,
    152                     (unsigned int) ehc);
    153       }
    154     }
    155     break;
    156   case MHD_HTTP_GATEWAY_TIMEOUT:
    157     /* Server had an internal issue; we should retry, but this API
    158        leaves this to the application */
    159     ptr.hr.ec = TALER_JSON_get_error_code (ptr.hr.reply);
    160     ptr.hr.hint = TALER_JSON_get_error_hint (ptr.hr.reply);
    161     break;
    162   default:
    163     /* unexpected response code */
    164     GNUNET_break_op (0);
    165     TALER_MERCHANT_parse_error_details_ (ptr.hr.reply,
    166                                          response_code,
    167                                          &ptr.hr);
    168     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    169                 "Unexpected response code %u/%d\n",
    170                 (unsigned int) ptr.hr.http_status,
    171                 (int) ptr.hr.ec);
    172     break;
    173   }
    174   pth->cb (pth->cb_cls,
    175            &ptr);
    176   TALER_MERCHANT_transfers_post_cancel (pth);
    177 }
    178 
    179 
    180 struct TALER_MERCHANT_PostTransfersHandle *
    181 TALER_MERCHANT_transfers_post (
    182   struct GNUNET_CURL_Context *ctx,
    183   const char *backend_url,
    184   const struct TALER_Amount *credit_amount,
    185   const struct TALER_WireTransferIdentifierRawP *wtid,
    186   struct TALER_FullPayto payto_uri,
    187   const char *exchange_url,
    188   TALER_MERCHANT_PostTransfersCallback cb,
    189   void *cb_cls)
    190 {
    191   struct TALER_MERCHANT_PostTransfersHandle *pth;
    192   CURL *eh;
    193   json_t *req;
    194 
    195   pth = GNUNET_new (struct TALER_MERCHANT_PostTransfersHandle);
    196   pth->ctx = ctx;
    197   pth->cb = cb;
    198   pth->cb_cls = cb_cls;
    199   pth->url = TALER_url_join (backend_url,
    200                              "private/transfers",
    201                              NULL);
    202   if (NULL == pth->url)
    203   {
    204     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    205                 "Could not construct request URL.\n");
    206     GNUNET_free (pth);
    207     return NULL;
    208   }
    209   req = GNUNET_JSON_PACK (
    210     TALER_JSON_pack_amount ("credit_amount",
    211                             credit_amount),
    212     GNUNET_JSON_pack_data_auto ("wtid",
    213                                 wtid),
    214     TALER_JSON_pack_full_payto ("payto_uri",
    215                                 payto_uri),
    216     GNUNET_JSON_pack_string ("exchange_url",
    217                              exchange_url));
    218   eh = TALER_MERCHANT_curl_easy_get_ (pth->url);
    219   if (GNUNET_OK !=
    220       TALER_curl_easy_post (&pth->post_ctx,
    221                             eh,
    222                             req))
    223   {
    224     GNUNET_break (0);
    225     curl_easy_cleanup (eh);
    226     json_decref (req);
    227     GNUNET_free (pth->url);
    228     GNUNET_free (pth);
    229     return NULL;
    230   }
    231   json_decref (req);
    232   pth->job = GNUNET_CURL_job_add2 (ctx,
    233                                    eh,
    234                                    pth->post_ctx.headers,
    235                                    &handle_post_transfers_finished,
    236                                    pth);
    237   return pth;
    238 }
    239 
    240 
    241 void
    242 TALER_MERCHANT_transfers_post_cancel (
    243   struct TALER_MERCHANT_PostTransfersHandle *pth)
    244 {
    245   if (NULL != pth->job)
    246   {
    247     GNUNET_CURL_job_cancel (pth->job);
    248     pth->job = NULL;
    249   }
    250   GNUNET_free (pth->url);
    251   TALER_curl_easy_post_finished (&pth->post_ctx);
    252   GNUNET_free (pth);
    253 }
    254 
    255 
    256 /* end of merchant_api_post_transfers.c */