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

Generated by: LCOV version 1.14