Line data    Source code 
       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            4 : handle_paid_finished (void *cls,
      88              :                       long response_code,
      89              :                       const void *response)
      90              : {
      91            4 :   struct TALER_MERCHANT_OrderPaidHandle *oph = cls;
      92            4 :   const json_t *json = response;
      93            4 :   struct TALER_MERCHANT_OrderPaidResponse opr = {
      94            4 :     .hr.http_status = (unsigned int) response_code,
      95              :     .hr.reply = json
      96              :   };
      97              : 
      98            4 :   oph->job = NULL;
      99            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     100              :               "/paid completed with response code %u\n",
     101              :               (unsigned int) response_code);
     102            4 :   switch (response_code)
     103              :   {
     104            0 :   case 0:
     105            0 :     opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     106            0 :     break;
     107            4 :   case MHD_HTTP_OK:
     108              :     {
     109              :       bool refunded;
     110              :       struct GNUNET_JSON_Specification spec[] = {
     111            4 :         GNUNET_JSON_spec_bool ("refunded",
     112              :                                &refunded),
     113            4 :         GNUNET_JSON_spec_end ()
     114              :       };
     115              : 
     116            4 :       if (GNUNET_OK !=
     117            4 :           GNUNET_JSON_parse (opr.hr.reply,
     118              :                              spec,
     119              :                              NULL,
     120              :                              NULL))
     121              :       {
     122            0 :         GNUNET_break_op (0);
     123            0 :         opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     124            0 :         break;
     125              :       }
     126            4 :       break;
     127              :     }
     128              :     break;
     129            0 :   case MHD_HTTP_BAD_REQUEST:
     130            0 :     opr.hr.ec = TALER_JSON_get_error_code (json);
     131            0 :     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            0 :     break;
     136            0 :   case MHD_HTTP_FORBIDDEN:
     137            0 :     opr.hr.ec = TALER_JSON_get_error_code (json);
     138            0 :     opr.hr.hint = TALER_JSON_get_error_hint (json);
     139              :     /* The signature provided was invalid */
     140            0 :     break;
     141            0 :   case MHD_HTTP_NOT_FOUND:
     142            0 :     opr.hr.ec = TALER_JSON_get_error_code (json);
     143            0 :     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            0 :     break;
     148            0 :   case MHD_HTTP_CONFLICT:
     149            0 :     opr.hr.ec = TALER_JSON_get_error_code (json);
     150            0 :     opr.hr.hint = TALER_JSON_get_error_hint (json);
     151              :     /* The hashed contract terms don't match with the order_id. */
     152            0 :     break;
     153            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     154            0 :     opr.hr.ec = TALER_JSON_get_error_code (json);
     155            0 :     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            0 :     break;
     159            0 :   case MHD_HTTP_SERVICE_UNAVAILABLE:
     160            0 :     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            0 :     break;
     166            0 :   default:
     167            0 :     TALER_MERCHANT_parse_error_details_ (json,
     168              :                                          response_code,
     169              :                                          &opr.hr);
     170              :     /* unexpected response code */
     171            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     172              :                 "Unexpected response code %u/%d\n",
     173              :                 (unsigned int) response_code,
     174              :                 (int) opr.hr.ec);
     175            0 :     GNUNET_break_op (0);
     176            0 :     break;
     177              :   }
     178            4 :   oph->paid_cb (oph->paid_cb_cls,
     179              :                 &opr);
     180            4 :   TALER_MERCHANT_order_paid_cancel (oph);
     181            4 : }
     182              : 
     183              : 
     184              : struct TALER_MERCHANT_OrderPaidHandle *
     185            4 : 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            4 :   req_obj = GNUNET_JSON_PACK (
     200              :     GNUNET_JSON_pack_data_auto ("sig",
     201              :                                 merchant_sig),
     202              :     GNUNET_JSON_pack_data_auto ("h_contract",
     203              :                                 h_contract_terms),
     204              :     GNUNET_JSON_pack_allow_null (
     205              :       GNUNET_JSON_pack_data_auto ("wallet_data_hash",
     206              :                                   wallet_data_hash)),
     207              :     GNUNET_JSON_pack_string ("session_id",
     208              :                              session_id));
     209            4 :   oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle);
     210            4 :   oph->ctx = ctx;
     211            4 :   oph->paid_cb = paid_cb;
     212            4 :   oph->paid_cb_cls = paid_cb_cls;
     213              :   {
     214              :     char *path;
     215              : 
     216            4 :     GNUNET_asprintf (&path,
     217              :                      "orders/%s/paid",
     218              :                      order_id);
     219            4 :     oph->url = TALER_url_join (merchant_url,
     220              :                                path,
     221              :                                NULL);
     222            4 :     GNUNET_free (path);
     223              :   }
     224            4 :   if (NULL == oph->url)
     225              :   {
     226            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     227              :                 "Could not construct request URL.\n");
     228            0 :     json_decref (req_obj);
     229            0 :     GNUNET_free (oph);
     230            0 :     return NULL;
     231              :   }
     232              :   {
     233              :     CURL *eh;
     234              : 
     235            4 :     eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
     236            4 :     if (GNUNET_OK !=
     237            4 :         TALER_curl_easy_post (&oph->post_ctx,
     238              :                               eh,
     239              :                               req_obj))
     240              :     {
     241            0 :       GNUNET_break (0);
     242            0 :       curl_easy_cleanup (eh);
     243            0 :       json_decref (req_obj);
     244            0 :       GNUNET_free (oph);
     245            0 :       return NULL;
     246              :     }
     247            4 :     json_decref (req_obj);
     248            8 :     oph->job = GNUNET_CURL_job_add2 (ctx,
     249              :                                      eh,
     250            4 :                                      oph->post_ctx.headers,
     251              :                                      &handle_paid_finished,
     252              :                                      oph);
     253              :   }
     254            4 :   return oph;
     255              : }
     256              : 
     257              : 
     258              : void
     259            4 : TALER_MERCHANT_order_paid_cancel (
     260              :   struct TALER_MERCHANT_OrderPaidHandle *oph)
     261              : {
     262            4 :   if (NULL != oph->job)
     263              :   {
     264            0 :     GNUNET_CURL_job_cancel (oph->job);
     265            0 :     oph->job = NULL;
     266              :   }
     267            4 :   TALER_curl_easy_post_finished (&oph->post_ctx);
     268            4 :   GNUNET_free (oph->url);
     269            4 :   GNUNET_free (oph);
     270            4 : }
     271              : 
     272              : 
     273              : /* end of merchant_api_post_order_paid.c */
        
               |