merchant

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

merchant_api_post_order_paid.c (7751B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-2021 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Lesser General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      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 Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General
     16   Public License along with TALER; see the file COPYING.LGPL.
     17   If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file merchant_api_post_order_paid.c
     21  * @brief Implementation of the POST /order/$ID/paid request
     22  *        of the merchant's HTTP API
     23  * @author Jonathan Buchanan
     24  */
     25 #include "platform.h"
     26 #include <curl/curl.h>
     27 #include <jansson.h>
     28 #include <microhttpd.h> /* just for HTTP status codes */
     29 #include <gnunet/gnunet_util_lib.h>
     30 #include <gnunet/gnunet_curl_lib.h>
     31 #include "taler_merchant_service.h"
     32 #include "merchant_api_curl_defaults.h"
     33 #include "merchant_api_common.h"
     34 #include <taler/taler_json_lib.h>
     35 #include <taler/taler_signatures.h>
     36 #include <taler/taler_exchange_service.h>
     37 #include <taler/taler_curl_lib.h>
     38 
     39 
     40 /**
     41  * @brief Handle to a POST /orders/$ID/paid operation at a merchant.
     42  */
     43 struct TALER_MERCHANT_OrderPaidHandle
     44 {
     45 
     46   /**
     47    * The url for this request.
     48    */
     49   char *url;
     50 
     51   /**
     52    * Handle for the request.
     53    */
     54   struct GNUNET_CURL_Job *job;
     55 
     56   /**
     57    * Function to call with the result.
     58    */
     59   TALER_MERCHANT_OrderPaidCallback paid_cb;
     60 
     61   /**
     62    * Closure for @a paid_cb.
     63    */
     64   void *paid_cb_cls;
     65 
     66   /**
     67    * Reference to the execution context.
     68    */
     69   struct GNUNET_CURL_Context *ctx;
     70 
     71   /**
     72    * Minor context that holds body and headers.
     73    */
     74   struct TALER_CURL_PostContext post_ctx;
     75 };
     76 
     77 
     78 /**
     79  * Function called when we're done processing the
     80  * HTTP /paid request.
     81  *
     82  * @param cls the `struct TALER_MERCHANT_OrderPaidHandle`
     83  * @param response_code HTTP response code, 0 on error
     84  * @param response response body, NULL if not in JSON
     85  */
     86 static void
     87 handle_paid_finished (void *cls,
     88                       long response_code,
     89                       const void *response)
     90 {
     91   struct TALER_MERCHANT_OrderPaidHandle *oph = cls;
     92   const json_t *json = response;
     93   struct TALER_MERCHANT_OrderPaidResponse opr = {
     94     .hr.http_status = (unsigned int) response_code,
     95     .hr.reply = json
     96   };
     97 
     98   oph->job = NULL;
     99   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    100               "/paid completed with response code %u\n",
    101               (unsigned int) response_code);
    102   switch (response_code)
    103   {
    104   case 0:
    105     opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    106     break;
    107   case MHD_HTTP_OK:
    108     {
    109       bool refunded;
    110       struct GNUNET_JSON_Specification spec[] = {
    111         GNUNET_JSON_spec_bool ("refunded",
    112                                &refunded),
    113         GNUNET_JSON_spec_end ()
    114       };
    115 
    116       if (GNUNET_OK !=
    117           GNUNET_JSON_parse (opr.hr.reply,
    118                              spec,
    119                              NULL,
    120                              NULL))
    121       {
    122         GNUNET_break_op (0);
    123         opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    124         break;
    125       }
    126       break;
    127     }
    128     break;
    129   case MHD_HTTP_BAD_REQUEST:
    130     opr.hr.ec = TALER_JSON_get_error_code (json);
    131     opr.hr.hint = TALER_JSON_get_error_hint (json);
    132     /* This should never happen, either us
    133      * or the merchant is buggy (or API version conflict);
    134      * just pass JSON reply to the application */
    135     break;
    136   case MHD_HTTP_FORBIDDEN:
    137     opr.hr.ec = TALER_JSON_get_error_code (json);
    138     opr.hr.hint = TALER_JSON_get_error_hint (json);
    139     /* The signature provided was invalid */
    140     break;
    141   case MHD_HTTP_NOT_FOUND:
    142     opr.hr.ec = TALER_JSON_get_error_code (json);
    143     opr.hr.hint = TALER_JSON_get_error_hint (json);
    144     /* Nothing really to verify, this should never
    145        happen, we should pass the JSON reply to the
    146        application */
    147     break;
    148   case MHD_HTTP_CONFLICT:
    149     opr.hr.ec = TALER_JSON_get_error_code (json);
    150     opr.hr.hint = TALER_JSON_get_error_hint (json);
    151     /* The hashed contract terms don't match with the order_id. */
    152     break;
    153   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    154     opr.hr.ec = TALER_JSON_get_error_code (json);
    155     opr.hr.hint = TALER_JSON_get_error_hint (json);
    156     /* Server had an internal issue; we should retry,
    157        but this API leaves this to the application */
    158     break;
    159   case MHD_HTTP_SERVICE_UNAVAILABLE:
    160     TALER_MERCHANT_parse_error_details_ (json,
    161                                          response_code,
    162                                          &opr.hr);
    163     /* Exchange couldn't respond properly; the retry is
    164        left to the application */
    165     break;
    166   default:
    167     TALER_MERCHANT_parse_error_details_ (json,
    168                                          response_code,
    169                                          &opr.hr);
    170     /* unexpected response code */
    171     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    172                 "Unexpected response code %u/%d\n",
    173                 (unsigned int) response_code,
    174                 (int) opr.hr.ec);
    175     GNUNET_break_op (0);
    176     break;
    177   }
    178   oph->paid_cb (oph->paid_cb_cls,
    179                 &opr);
    180   TALER_MERCHANT_order_paid_cancel (oph);
    181 }
    182 
    183 
    184 struct TALER_MERCHANT_OrderPaidHandle *
    185 TALER_MERCHANT_order_paid (
    186   struct GNUNET_CURL_Context *ctx,
    187   const char *merchant_url,
    188   const char *order_id,
    189   const char *session_id,
    190   const struct TALER_PrivateContractHashP *h_contract_terms,
    191   const struct GNUNET_HashCode *wallet_data_hash,
    192   const struct TALER_MerchantSignatureP *merchant_sig,
    193   TALER_MERCHANT_OrderPaidCallback paid_cb,
    194   void *paid_cb_cls)
    195 {
    196   struct TALER_MERCHANT_OrderPaidHandle *oph;
    197   json_t *req_obj;
    198 
    199   (void) wallet_data_hash;
    200   req_obj = GNUNET_JSON_PACK (
    201     GNUNET_JSON_pack_data_auto ("sig",
    202                                 merchant_sig),
    203     GNUNET_JSON_pack_data_auto ("h_contract",
    204                                 h_contract_terms),
    205     GNUNET_JSON_pack_string ("session_id",
    206                              session_id));
    207   oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle);
    208   oph->ctx = ctx;
    209   oph->paid_cb = paid_cb;
    210   oph->paid_cb_cls = paid_cb_cls;
    211   {
    212     char *path;
    213 
    214     GNUNET_asprintf (&path,
    215                      "orders/%s/paid",
    216                      order_id);
    217     oph->url = TALER_url_join (merchant_url,
    218                                path,
    219                                NULL);
    220     GNUNET_free (path);
    221   }
    222   if (NULL == oph->url)
    223   {
    224     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    225                 "Could not construct request URL.\n");
    226     json_decref (req_obj);
    227     GNUNET_free (oph);
    228     return NULL;
    229   }
    230   {
    231     CURL *eh;
    232 
    233     eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
    234     if (GNUNET_OK !=
    235         TALER_curl_easy_post (&oph->post_ctx,
    236                               eh,
    237                               req_obj))
    238     {
    239       GNUNET_break (0);
    240       curl_easy_cleanup (eh);
    241       json_decref (req_obj);
    242       GNUNET_free (oph);
    243       return NULL;
    244     }
    245     json_decref (req_obj);
    246     oph->job = GNUNET_CURL_job_add2 (ctx,
    247                                      eh,
    248                                      oph->post_ctx.headers,
    249                                      &handle_paid_finished,
    250                                      oph);
    251   }
    252   return oph;
    253 }
    254 
    255 
    256 void
    257 TALER_MERCHANT_order_paid_cancel (
    258   struct TALER_MERCHANT_OrderPaidHandle *oph)
    259 {
    260   if (NULL != oph->job)
    261   {
    262     GNUNET_CURL_job_cancel (oph->job);
    263     oph->job = NULL;
    264   }
    265   TALER_curl_easy_post_finished (&oph->post_ctx);
    266   GNUNET_free (oph->url);
    267   GNUNET_free (oph);
    268 }
    269 
    270 
    271 /* end of merchant_api_post_order_paid.c */