LCOV - code coverage report
Current view: top level - lib - merchant_api_post_order_abort.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 0 154 0.0 %
Date: 2022-08-25 06:17:04 Functions: 0 4 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-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_abort.c
      21             :  * @brief Implementation of the POST /orders/$ID/abort request
      22             :  *        of the merchant's HTTP API
      23             :  * @author Christian Grothoff
      24             :  * @author Marcello Stanisci
      25             :  */
      26             : #include "platform.h"
      27             : #include <curl/curl.h>
      28             : #include <jansson.h>
      29             : #include <microhttpd.h> /* just for HTTP status codes */
      30             : #include <gnunet/gnunet_util_lib.h>
      31             : #include <gnunet/gnunet_curl_lib.h>
      32             : #include "taler_merchant_service.h"
      33             : #include "merchant_api_curl_defaults.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 An abort Handle
      42             :  */
      43             : struct TALER_MERCHANT_OrderAbortHandle
      44             : {
      45             :   /**
      46             :    * Hash of the contract.
      47             :    */
      48             :   struct TALER_PrivateContractHashP h_contract_terms;
      49             : 
      50             :   /**
      51             :    * Public key of the merchant.
      52             :    */
      53             :   struct TALER_MerchantPublicKeyP merchant_pub;
      54             : 
      55             :   /**
      56             :    * The url for this request.
      57             :    */
      58             :   char *url;
      59             : 
      60             :   /**
      61             :    * Handle for the request.
      62             :    */
      63             :   struct GNUNET_CURL_Job *job;
      64             : 
      65             :   /**
      66             :    * Function to call with the result.
      67             :    */
      68             :   TALER_MERCHANT_AbortCallback abort_cb;
      69             : 
      70             :   /**
      71             :    * Closure for @a abort_cb.
      72             :    */
      73             :   void *abort_cb_cls;
      74             : 
      75             :   /**
      76             :    * Reference to the execution context.
      77             :    */
      78             :   struct GNUNET_CURL_Context *ctx;
      79             : 
      80             :   /**
      81             :    * Minor context that holds body and headers.
      82             :    */
      83             :   struct TALER_CURL_PostContext post_ctx;
      84             : 
      85             :   /**
      86             :    * The coins we are aborting on.
      87             :    */
      88             :   struct TALER_MERCHANT_AbortCoin *coins;
      89             : 
      90             :   /**
      91             :    * Number of @e coins we are paying with.
      92             :    */
      93             :   unsigned int num_coins;
      94             : 
      95             : };
      96             : 
      97             : 
      98             : /**
      99             :  * Check that the response for an abort is well-formed,
     100             :  * and call the application callback with the result if it is
     101             :  * OK. Otherwise returns #GNUNET_SYSERR.
     102             :  *
     103             :  * @param oah handle to operation that created the reply
     104             :  * @param json the reply to parse
     105             :  * @return #GNUNET_OK on success
     106             :  */
     107             : static int
     108           0 : check_abort_refund (struct TALER_MERCHANT_OrderAbortHandle *oah,
     109             :                     const json_t *json)
     110             : {
     111             :   json_t *refunds;
     112             :   unsigned int num_refunds;
     113             :   struct GNUNET_JSON_Specification spec[] = {
     114           0 :     GNUNET_JSON_spec_json ("refunds", &refunds),
     115           0 :     GNUNET_JSON_spec_end ()
     116             :   };
     117             : 
     118           0 :   if (GNUNET_OK !=
     119           0 :       GNUNET_JSON_parse (json,
     120             :                          spec,
     121             :                          NULL, NULL))
     122             :   {
     123           0 :     GNUNET_break_op (0);
     124           0 :     return GNUNET_SYSERR;
     125             :   }
     126           0 :   if (! json_is_array (refunds))
     127             :   {
     128           0 :     GNUNET_break_op (0);
     129           0 :     GNUNET_JSON_parse_free (spec);
     130           0 :     return GNUNET_SYSERR;
     131             :   }
     132           0 :   num_refunds = json_array_size (refunds);
     133           0 :   {
     134           0 :     struct TALER_MERCHANT_AbortedCoin res[GNUNET_NZL (num_refunds)];
     135             : 
     136           0 :     for (unsigned int i = 0; i<num_refunds; i++)
     137             :     {
     138           0 :       json_t *refund = json_array_get (refunds, i);
     139             :       uint32_t exchange_status;
     140             :       struct GNUNET_JSON_Specification spec_es[] = {
     141           0 :         GNUNET_JSON_spec_uint32 ("exchange_status",
     142             :                                  &exchange_status),
     143           0 :         GNUNET_JSON_spec_end ()
     144             :       };
     145             : 
     146           0 :       if (GNUNET_OK !=
     147           0 :           GNUNET_JSON_parse (refund,
     148             :                              spec_es,
     149             :                              NULL, NULL))
     150             :       {
     151           0 :         GNUNET_break_op (0);
     152           0 :         GNUNET_JSON_parse_free (spec);
     153           0 :         return GNUNET_SYSERR;
     154             :       }
     155           0 :       if (MHD_HTTP_OK == exchange_status)
     156             :       {
     157             :         struct GNUNET_JSON_Specification spec_detail[] = {
     158           0 :           GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     159             :                                        &res[i].exchange_sig),
     160           0 :           GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     161             :                                        &res[i].exchange_pub),
     162           0 :           GNUNET_JSON_spec_end ()
     163             :         };
     164             : 
     165           0 :         if (GNUNET_OK !=
     166           0 :             GNUNET_JSON_parse (refund,
     167             :                                spec_detail,
     168             :                                NULL, NULL))
     169             :         {
     170           0 :           GNUNET_break_op (0);
     171           0 :           GNUNET_JSON_parse_free (spec);
     172           0 :           return GNUNET_SYSERR;
     173             :         }
     174             : 
     175           0 :         if (GNUNET_OK !=
     176           0 :             TALER_exchange_online_refund_confirmation_verify (
     177           0 :               &oah->h_contract_terms,
     178           0 :               &oah->coins[i].coin_pub,
     179           0 :               &oah->merchant_pub,
     180             :               0,                                                   /* transaction id */
     181           0 :               &oah->coins[i].amount_with_fee,
     182           0 :               &res[i].exchange_pub,
     183           0 :               &res[i].exchange_sig))
     184             :         {
     185           0 :           GNUNET_break_op (0);
     186           0 :           GNUNET_JSON_parse_free (spec);
     187           0 :           return GNUNET_SYSERR;
     188             :         }
     189             :       }
     190             :     }
     191             :     {
     192           0 :       struct TALER_MERCHANT_HttpResponse hr = {
     193             :         .reply = json,
     194             :         .http_status = MHD_HTTP_OK
     195             :       };
     196             : 
     197           0 :       oah->abort_cb (oah->abort_cb_cls,
     198             :                      &hr,
     199           0 :                      &oah->merchant_pub,
     200             :                      num_refunds,
     201             :                      res);
     202             :     }
     203           0 :     oah->abort_cb = NULL;
     204             :   }
     205           0 :   GNUNET_JSON_parse_free (spec);
     206           0 :   return GNUNET_OK;
     207             : }
     208             : 
     209             : 
     210             : /**
     211             :  * Function called when we're done processing the
     212             :  * abort request.
     213             :  *
     214             :  * @param cls the `struct TALER_MERCHANT_OrderAbortHandle`
     215             :  * @param response_code HTTP response code, 0 on error
     216             :  * @param response response body, NULL if not in JSON
     217             :  */
     218             : static void
     219           0 : handle_abort_finished (void *cls,
     220             :                        long response_code,
     221             :                        const void *response)
     222             : {
     223           0 :   struct TALER_MERCHANT_OrderAbortHandle *oah = cls;
     224           0 :   const json_t *json = response;
     225           0 :   struct TALER_MERCHANT_HttpResponse hr = {
     226           0 :     .http_status = (unsigned int) response_code,
     227             :     .reply = json
     228             :   };
     229             : 
     230           0 :   oah->job = NULL;
     231           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     232             :               "/pay completed with response code %u\n",
     233             :               (unsigned int) response_code);
     234           0 :   switch (response_code)
     235             :   {
     236           0 :   case 0:
     237           0 :     hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     238           0 :     break;
     239           0 :   case MHD_HTTP_OK:
     240           0 :     if (GNUNET_OK ==
     241           0 :         check_abort_refund (oah,
     242             :                             json))
     243             :     {
     244           0 :       TALER_MERCHANT_order_abort_cancel (oah);
     245           0 :       return;
     246             :     }
     247           0 :     hr.http_status = 0;
     248           0 :     hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     249           0 :     break;
     250           0 :   case MHD_HTTP_BAD_REQUEST:
     251           0 :     hr.ec = TALER_JSON_get_error_code (json);
     252           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     253             :     /* This should never happen, either us or the
     254             :        merchant is buggy (or API version conflict); just
     255             :        pass JSON reply to the application */
     256           0 :     break;
     257           0 :   case MHD_HTTP_FORBIDDEN:
     258           0 :     hr.ec = TALER_JSON_get_error_code (json);
     259           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     260           0 :     break;
     261           0 :   case MHD_HTTP_NOT_FOUND:
     262           0 :     hr.ec = TALER_JSON_get_error_code (json);
     263           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     264             :     /* Nothing really to verify, this should never
     265             :  happen, we should pass the JSON reply to the
     266             :        application */
     267           0 :     break;
     268           0 :   case MHD_HTTP_REQUEST_TIMEOUT:
     269           0 :     hr.ec = TALER_JSON_get_error_code (json);
     270           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     271             :     /* Nothing really to verify, merchant says one of
     272             :        the signatures is invalid; as we checked them,
     273             :        this should never happen, we should pass the JSON
     274             :        reply to the application */
     275           0 :     break;
     276           0 :   case MHD_HTTP_PRECONDITION_FAILED:
     277             :     /* Our *payment* already succeeded fully. */
     278           0 :     hr.ec = TALER_JSON_get_error_code (json);
     279           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     280           0 :     break;
     281           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     282           0 :     hr.ec = TALER_JSON_get_error_code (json);
     283           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     284             :     /* Server had an internal issue; we should retry,
     285             :        but this API leaves this to the application */
     286           0 :     break;
     287           0 :   case MHD_HTTP_BAD_GATEWAY:
     288           0 :     TALER_MERCHANT_parse_error_details_ (json,
     289             :                                          response_code,
     290             :                                          &hr);
     291             :     /* Nothing really to verify, the merchant is blaming the exchange.
     292             :        We should pass the JSON reply to the application */
     293           0 :     break;
     294           0 :   default:
     295             :     /* unexpected response code */
     296           0 :     TALER_MERCHANT_parse_error_details_ (json,
     297             :                                          response_code,
     298             :                                          &hr);
     299           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     300             :                 "Unexpected response code %u/%d\n",
     301             :                 (unsigned int) response_code,
     302             :                 (int) hr.ec);
     303           0 :     GNUNET_break_op (0);
     304           0 :     break;
     305             :   }
     306           0 :   oah->abort_cb (oah->abort_cb_cls,
     307             :                  &hr,
     308             :                  NULL,
     309             :                  0,
     310             :                  NULL);
     311           0 :   TALER_MERCHANT_order_abort_cancel (oah);
     312             : }
     313             : 
     314             : 
     315             : struct TALER_MERCHANT_OrderAbortHandle *
     316           0 : TALER_MERCHANT_order_abort (struct GNUNET_CURL_Context *ctx,
     317             :                             const char *merchant_url,
     318             :                             const char *order_id,
     319             :                             const struct TALER_MerchantPublicKeyP *merchant_pub,
     320             :                             const struct TALER_PrivateContractHashP *h_contract,
     321             :                             unsigned int num_coins,
     322             :                             const struct TALER_MERCHANT_AbortCoin coins[],
     323             :                             TALER_MERCHANT_AbortCallback cb,
     324             :                             void *cb_cls)
     325             : {
     326             :   struct TALER_MERCHANT_OrderAbortHandle *oah;
     327             :   json_t *abort_obj;
     328             :   json_t *j_coins;
     329             : 
     330           0 :   j_coins = json_array ();
     331           0 :   if (NULL == j_coins)
     332             :   {
     333           0 :     GNUNET_break (0);
     334           0 :     return NULL;
     335             :   }
     336           0 :   for (unsigned int i = 0; i<num_coins; i++)
     337             :   {
     338           0 :     const struct TALER_MERCHANT_AbortCoin *ac = &coins[i];
     339             :     json_t *j_coin;
     340             : 
     341             :     /* create JSON for this coin */
     342           0 :     j_coin = GNUNET_JSON_PACK (
     343             :       GNUNET_JSON_pack_data_auto ("coin_pub",
     344             :                                   &ac->coin_pub),
     345             :       TALER_JSON_pack_amount ("contribution",
     346             :                               &ac->amount_with_fee),
     347             :       GNUNET_JSON_pack_string ("exchange_url",
     348             :                                ac->exchange_url));
     349           0 :     if (0 !=
     350           0 :         json_array_append_new (j_coins,
     351             :                                j_coin))
     352             :     {
     353           0 :       GNUNET_break (0);
     354           0 :       json_decref (j_coins);
     355           0 :       return NULL;
     356             :     }
     357             :   }
     358           0 :   abort_obj = GNUNET_JSON_PACK (
     359             :     GNUNET_JSON_pack_array_steal ("coins",
     360             :                                   j_coins),
     361             :     GNUNET_JSON_pack_data_auto ("h_contract",
     362             :                                 h_contract));
     363           0 :   oah = GNUNET_new (struct TALER_MERCHANT_OrderAbortHandle);
     364           0 :   oah->h_contract_terms = *h_contract;
     365           0 :   oah->merchant_pub = *merchant_pub;
     366           0 :   oah->ctx = ctx;
     367           0 :   oah->abort_cb = cb;
     368           0 :   oah->abort_cb_cls = cb_cls;
     369             :   {
     370             :     char *path;
     371             : 
     372           0 :     GNUNET_asprintf (&path,
     373             :                      "orders/%s/abort",
     374             :                      order_id);
     375           0 :     oah->url = TALER_url_join (merchant_url,
     376             :                                path,
     377             :                                NULL);
     378           0 :     GNUNET_free (path);
     379             :   }
     380           0 :   if (NULL == oah->url)
     381             :   {
     382           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     383             :                 "Could not construct request URL.\n");
     384           0 :     json_decref (abort_obj);
     385           0 :     GNUNET_free (oah);
     386           0 :     return NULL;
     387             :   }
     388           0 :   oah->num_coins = num_coins;
     389           0 :   oah->coins = GNUNET_new_array (num_coins,
     390             :                                  struct TALER_MERCHANT_AbortCoin);
     391           0 :   memcpy (oah->coins,
     392             :           coins,
     393             :           num_coins * sizeof (struct TALER_MERCHANT_AbortCoin));
     394             :   {
     395             :     CURL *eh;
     396             : 
     397           0 :     eh = TALER_MERCHANT_curl_easy_get_ (oah->url);
     398           0 :     if (GNUNET_OK !=
     399           0 :         TALER_curl_easy_post (&oah->post_ctx,
     400             :                               eh,
     401             :                               abort_obj))
     402             :     {
     403           0 :       GNUNET_break (0);
     404           0 :       curl_easy_cleanup (eh);
     405           0 :       json_decref (abort_obj);
     406           0 :       GNUNET_free (oah);
     407           0 :       return NULL;
     408             :     }
     409           0 :     json_decref (abort_obj);
     410           0 :     oah->job = GNUNET_CURL_job_add2 (ctx,
     411             :                                      eh,
     412           0 :                                      oah->post_ctx.headers,
     413             :                                      &handle_abort_finished,
     414             :                                      oah);
     415             :   }
     416           0 :   return oah;
     417             : }
     418             : 
     419             : 
     420             : void
     421           0 : TALER_MERCHANT_order_abort_cancel (struct TALER_MERCHANT_OrderAbortHandle *oah)
     422             : {
     423           0 :   if (NULL != oah->job)
     424             :   {
     425           0 :     GNUNET_CURL_job_cancel (oah->job);
     426           0 :     oah->job = NULL;
     427             :   }
     428           0 :   TALER_curl_easy_post_finished (&oah->post_ctx);
     429           0 :   GNUNET_free (oah->coins);
     430           0 :   GNUNET_free (oah->url);
     431           0 :   GNUNET_free (oah);
     432           0 : }
     433             : 
     434             : 
     435             : /* end of merchant_api_post_order_abort.c */

Generated by: LCOV version 1.14