LCOV - code coverage report
Current view: top level - lib - merchant_api_post-orders-ORDER_ID-abort.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 175 0
Test Date: 2026-04-12 12:58:13 Functions: 0.0 % 5 0

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2026 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-orders-ORDER_ID-abort-new.c
      21              :  * @brief Implementation of the POST /orders/$ID/abort request
      22              :  * @author Christian Grothoff
      23              :  */
      24              : #include "taler/platform.h"
      25              : #include <curl/curl.h>
      26              : #include <jansson.h>
      27              : #include <microhttpd.h> /* just for HTTP status codes */
      28              : #include <gnunet/gnunet_util_lib.h>
      29              : #include <gnunet/gnunet_curl_lib.h>
      30              : #include <taler/merchant/post-orders-ORDER_ID-abort.h>
      31              : #include "merchant_api_curl_defaults.h"
      32              : #include "merchant_api_common.h"
      33              : #include <taler/taler_json_lib.h>
      34              : #include <taler/taler_curl_lib.h>
      35              : #include <taler/taler_signatures.h>
      36              : 
      37              : 
      38              : /**
      39              :  * Maximum number of refunds we return.
      40              :  */
      41              : #define MAX_REFUNDS 1024
      42              : 
      43              : 
      44              : /**
      45              :  * Handle for a POST /orders/$ORDER_ID/abort operation.
      46              :  */
      47              : struct TALER_MERCHANT_PostOrdersAbortHandle
      48              : {
      49              :   /**
      50              :    * Base URL of the merchant backend.
      51              :    */
      52              :   char *base_url;
      53              : 
      54              :   /**
      55              :    * The full 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_PostOrdersAbortCallback cb;
      68              : 
      69              :   /**
      70              :    * Closure for @a cb.
      71              :    */
      72              :   TALER_MERCHANT_POST_ORDERS_ABORT_RESULT_CLOSURE *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              :    * Order identifier.
      86              :    */
      87              :   char *order_id;
      88              : 
      89              :   /**
      90              :    * Public key of the merchant.
      91              :    */
      92              :   struct TALER_MerchantPublicKeyP merchant_pub;
      93              : 
      94              :   /**
      95              :    * Hash of the contract terms.
      96              :    */
      97              :   struct TALER_PrivateContractHashP h_contract;
      98              : 
      99              :   /**
     100              :    * The coins we are aborting on.
     101              :    */
     102              :   struct TALER_MERCHANT_PostOrdersAbortCoin *coins;
     103              : 
     104              :   /**
     105              :    * Number of @e coins.
     106              :    */
     107              :   unsigned int num_coins;
     108              : };
     109              : 
     110              : 
     111              : /**
     112              :  * Check that the response for an abort is well-formed,
     113              :  * and call the application callback with the result if it is
     114              :  * OK. Otherwise returns #GNUNET_SYSERR.
     115              :  *
     116              :  * @param poah handle to operation that created the reply
     117              :  * @param[in] ar abort response, partially initialized
     118              :  * @param json the reply to parse
     119              :  * @return #GNUNET_OK on success
     120              :  */
     121              : static enum GNUNET_GenericReturnValue
     122            0 : check_abort_refund (struct TALER_MERCHANT_PostOrdersAbortHandle *poah,
     123              :                     struct TALER_MERCHANT_PostOrdersAbortResponse *ar,
     124              :                     const json_t *json)
     125              : {
     126              :   const json_t *refunds;
     127              :   unsigned int num_refunds;
     128              :   struct GNUNET_JSON_Specification spec[] = {
     129            0 :     GNUNET_JSON_spec_array_const ("refunds",
     130              :                                   &refunds),
     131            0 :     GNUNET_JSON_spec_end ()
     132              :   };
     133              : 
     134            0 :   if (GNUNET_OK !=
     135            0 :       GNUNET_JSON_parse (json,
     136              :                          spec,
     137              :                          NULL, NULL))
     138              :   {
     139            0 :     GNUNET_break_op (0);
     140            0 :     return GNUNET_SYSERR;
     141              :   }
     142            0 :   num_refunds = (unsigned int) json_array_size (refunds);
     143            0 :   if ( (json_array_size (refunds) != (size_t) num_refunds) ||
     144              :        (num_refunds > MAX_REFUNDS) )
     145              :   {
     146            0 :     GNUNET_break (0);
     147            0 :     return GNUNET_SYSERR;
     148              :   }
     149              : 
     150            0 :   {
     151            0 :     struct TALER_MERCHANT_PostOrdersAbortedCoin res[GNUNET_NZL (num_refunds)];
     152              : 
     153            0 :     for (unsigned int i = 0; i<num_refunds; i++)
     154              :     {
     155            0 :       json_t *refund = json_array_get (refunds, i);
     156              :       uint32_t exchange_status;
     157              :       struct GNUNET_JSON_Specification spec_es[] = {
     158            0 :         GNUNET_JSON_spec_uint32 ("exchange_status",
     159              :                                  &exchange_status),
     160            0 :         GNUNET_JSON_spec_end ()
     161              :       };
     162              : 
     163            0 :       if (GNUNET_OK !=
     164            0 :           GNUNET_JSON_parse (refund,
     165              :                              spec_es,
     166              :                              NULL, NULL))
     167              :       {
     168            0 :         GNUNET_break_op (0);
     169            0 :         return GNUNET_SYSERR;
     170              :       }
     171            0 :       if (MHD_HTTP_OK == exchange_status)
     172              :       {
     173              :         struct GNUNET_JSON_Specification spec_detail[] = {
     174            0 :           GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     175              :                                        &res[i].exchange_sig),
     176            0 :           GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     177              :                                        &res[i].exchange_pub),
     178            0 :           GNUNET_JSON_spec_end ()
     179              :         };
     180              : 
     181            0 :         if (GNUNET_OK !=
     182            0 :             GNUNET_JSON_parse (refund,
     183              :                                spec_detail,
     184              :                                NULL, NULL))
     185              :         {
     186            0 :           GNUNET_break_op (0);
     187            0 :           return GNUNET_SYSERR;
     188              :         }
     189            0 :         res[i].coin_pub = poah->coins[i].coin_pub;
     190              : 
     191            0 :         if (GNUNET_OK !=
     192            0 :             TALER_exchange_online_refund_confirmation_verify (
     193            0 :               &poah->h_contract,
     194            0 :               &poah->coins[i].coin_pub,
     195            0 :               &poah->merchant_pub,
     196              :               0,   /* transaction id */
     197            0 :               &poah->coins[i].amount_with_fee,
     198            0 :               &res[i].exchange_pub,
     199            0 :               &res[i].exchange_sig))
     200              :         {
     201            0 :           GNUNET_break_op (0);
     202            0 :           return GNUNET_SYSERR;
     203              :         }
     204              :       }
     205              :     }
     206            0 :     switch (ar->hr.http_status)
     207              :     {
     208            0 :     case MHD_HTTP_OK:
     209            0 :       ar->details.ok.num_aborts = num_refunds;
     210            0 :       ar->details.ok.aborts = res;
     211            0 :       break;
     212            0 :     case MHD_HTTP_BAD_GATEWAY:
     213            0 :       ar->details.bad_gateway.num_aborts = num_refunds;
     214            0 :       ar->details.bad_gateway.aborts = res;
     215            0 :       break;
     216            0 :     default:
     217            0 :       GNUNET_assert (0);
     218              :     }
     219            0 :     poah->cb (poah->cb_cls,
     220              :               ar);
     221            0 :     poah->cb = NULL;
     222              :   }
     223            0 :   return GNUNET_OK;
     224              : }
     225              : 
     226              : 
     227              : /**
     228              :  * Function called when we're done processing the
     229              :  * HTTP POST /orders/$ID/abort request.
     230              :  *
     231              :  * @param cls the `struct TALER_MERCHANT_PostOrdersAbortHandle`
     232              :  * @param response_code HTTP response code, 0 on error
     233              :  * @param response response body, NULL if not in JSON
     234              :  */
     235              : static void
     236            0 : handle_abort_finished (void *cls,
     237              :                        long response_code,
     238              :                        const void *response)
     239              : {
     240            0 :   struct TALER_MERCHANT_PostOrdersAbortHandle *poah = cls;
     241            0 :   const json_t *json = response;
     242            0 :   struct TALER_MERCHANT_PostOrdersAbortResponse ar = {
     243            0 :     .hr.http_status = (unsigned int) response_code,
     244              :     .hr.reply = json
     245              :   };
     246              : 
     247            0 :   poah->job = NULL;
     248            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     249              :               "POST /orders/$ID/abort completed with response code %u\n",
     250              :               (unsigned int) response_code);
     251            0 :   switch (response_code)
     252              :   {
     253            0 :   case 0:
     254            0 :     ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     255            0 :     break;
     256            0 :   case MHD_HTTP_OK:
     257            0 :     if (GNUNET_OK ==
     258            0 :         check_abort_refund (poah,
     259              :                             &ar,
     260              :                             json))
     261              :     {
     262            0 :       TALER_MERCHANT_post_orders_abort_cancel (poah);
     263            0 :       return;
     264              :     }
     265            0 :     ar.hr.http_status = 0;
     266            0 :     ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     267            0 :     break;
     268            0 :   case MHD_HTTP_BAD_REQUEST:
     269            0 :     ar.hr.ec = TALER_JSON_get_error_code (json);
     270            0 :     ar.hr.hint = TALER_JSON_get_error_hint (json);
     271            0 :     break;
     272            0 :   case MHD_HTTP_FORBIDDEN:
     273            0 :     ar.hr.ec = TALER_JSON_get_error_code (json);
     274            0 :     ar.hr.hint = TALER_JSON_get_error_hint (json);
     275            0 :     break;
     276            0 :   case MHD_HTTP_NOT_FOUND:
     277            0 :     ar.hr.ec = TALER_JSON_get_error_code (json);
     278            0 :     ar.hr.hint = TALER_JSON_get_error_hint (json);
     279            0 :     break;
     280            0 :   case MHD_HTTP_REQUEST_TIMEOUT:
     281            0 :     ar.hr.ec = TALER_JSON_get_error_code (json);
     282            0 :     ar.hr.hint = TALER_JSON_get_error_hint (json);
     283            0 :     break;
     284            0 :   case MHD_HTTP_PRECONDITION_FAILED:
     285            0 :     ar.hr.ec = TALER_JSON_get_error_code (json);
     286            0 :     ar.hr.hint = TALER_JSON_get_error_hint (json);
     287            0 :     break;
     288            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     289            0 :     ar.hr.ec = TALER_JSON_get_error_code (json);
     290            0 :     ar.hr.hint = TALER_JSON_get_error_hint (json);
     291            0 :     break;
     292            0 :   case MHD_HTTP_BAD_GATEWAY:
     293            0 :     if (GNUNET_OK ==
     294            0 :         check_abort_refund (poah,
     295              :                             &ar,
     296              :                             json))
     297              :     {
     298            0 :       TALER_MERCHANT_post_orders_abort_cancel (poah);
     299            0 :       return;
     300              :     }
     301            0 :     ar.hr.http_status = 0;
     302            0 :     ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     303            0 :     break;
     304            0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     305            0 :     TALER_MERCHANT_parse_error_details_ (json,
     306              :                                          response_code,
     307              :                                          &ar.hr);
     308            0 :     ar.details.gateway_timeout.exchange_ec = ar.hr.exchange_code;
     309              :     ar.details.gateway_timeout.exchange_http_status
     310            0 :       = ar.hr.exchange_http_status;
     311            0 :     break;
     312            0 :   default:
     313            0 :     TALER_MERCHANT_parse_error_details_ (json,
     314              :                                          response_code,
     315              :                                          &ar.hr);
     316            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     317              :                 "Unexpected response code %u/%d\n",
     318              :                 (unsigned int) response_code,
     319              :                 (int) ar.hr.ec);
     320            0 :     GNUNET_break_op (0);
     321            0 :     break;
     322              :   }
     323            0 :   poah->cb (poah->cb_cls,
     324              :             &ar);
     325            0 :   TALER_MERCHANT_post_orders_abort_cancel (poah);
     326              : }
     327              : 
     328              : 
     329              : struct TALER_MERCHANT_PostOrdersAbortHandle *
     330            0 : TALER_MERCHANT_post_orders_abort_create (
     331              :   struct GNUNET_CURL_Context *ctx,
     332              :   const char *url,
     333              :   const char *order_id,
     334              :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     335              :   const struct TALER_PrivateContractHashP *h_contract,
     336              :   unsigned int num_coins,
     337              :   const struct TALER_MERCHANT_PostOrdersAbortCoin coins[static num_coins])
     338            0 : {
     339              :   struct TALER_MERCHANT_PostOrdersAbortHandle *poah;
     340              : 
     341            0 :   poah = GNUNET_new (struct TALER_MERCHANT_PostOrdersAbortHandle);
     342            0 :   poah->ctx = ctx;
     343            0 :   poah->base_url = GNUNET_strdup (url);
     344            0 :   poah->order_id = GNUNET_strdup (order_id);
     345            0 :   poah->merchant_pub = *merchant_pub;
     346            0 :   poah->h_contract = *h_contract;
     347            0 :   poah->num_coins = num_coins;
     348            0 :   poah->coins = GNUNET_new_array (num_coins,
     349              :                                   struct TALER_MERCHANT_PostOrdersAbortCoin);
     350            0 :   GNUNET_memcpy (
     351              :     poah->coins,
     352              :     coins,
     353              :     num_coins * sizeof (struct TALER_MERCHANT_PostOrdersAbortCoin));
     354            0 :   return poah;
     355              : }
     356              : 
     357              : 
     358              : enum TALER_ErrorCode
     359            0 : TALER_MERCHANT_post_orders_abort_start (
     360              :   struct TALER_MERCHANT_PostOrdersAbortHandle *poah,
     361              :   TALER_MERCHANT_PostOrdersAbortCallback cb,
     362              :   TALER_MERCHANT_POST_ORDERS_ABORT_RESULT_CLOSURE *cb_cls)
     363              : {
     364              :   json_t *abort_obj;
     365              :   json_t *j_coins;
     366              :   CURL *eh;
     367              : 
     368            0 :   poah->cb = cb;
     369            0 :   poah->cb_cls = cb_cls;
     370              :   {
     371              :     char *path;
     372              : 
     373            0 :     GNUNET_asprintf (&path,
     374              :                      "orders/%s/abort",
     375              :                      poah->order_id);
     376            0 :     poah->url = TALER_url_join (poah->base_url,
     377              :                                 path,
     378              :                                 NULL);
     379            0 :     GNUNET_free (path);
     380              :   }
     381            0 :   if (NULL == poah->url)
     382            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     383            0 :   j_coins = json_array ();
     384            0 :   if (NULL == j_coins)
     385              :   {
     386            0 :     GNUNET_break (0);
     387            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     388              :   }
     389            0 :   for (unsigned int i = 0; i<poah->num_coins; i++)
     390              :   {
     391            0 :     const struct TALER_MERCHANT_PostOrdersAbortCoin *ac = &poah->coins[i];
     392              :     json_t *j_coin;
     393              : 
     394            0 :     j_coin = GNUNET_JSON_PACK (
     395              :       GNUNET_JSON_pack_data_auto ("coin_pub",
     396              :                                   &ac->coin_pub),
     397              :       TALER_JSON_pack_amount ("contribution",
     398              :                               &ac->amount_with_fee),
     399              :       GNUNET_JSON_pack_string ("exchange_url",
     400              :                                ac->exchange_url));
     401            0 :     if (0 !=
     402            0 :         json_array_append_new (j_coins,
     403              :                                j_coin))
     404              :     {
     405            0 :       GNUNET_break (0);
     406            0 :       json_decref (j_coins);
     407            0 :       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     408              :     }
     409              :   }
     410            0 :   abort_obj = GNUNET_JSON_PACK (
     411              :     GNUNET_JSON_pack_array_steal ("coins",
     412              :                                   j_coins),
     413              :     GNUNET_JSON_pack_data_auto ("h_contract",
     414              :                                 &poah->h_contract));
     415            0 :   eh = TALER_MERCHANT_curl_easy_get_ (poah->url);
     416            0 :   if ( (NULL == eh) ||
     417              :        (GNUNET_OK !=
     418            0 :         TALER_curl_easy_post (&poah->post_ctx,
     419              :                               eh,
     420              :                               abort_obj)) )
     421              :   {
     422            0 :     GNUNET_break (0);
     423            0 :     json_decref (abort_obj);
     424            0 :     if (NULL != eh)
     425            0 :       curl_easy_cleanup (eh);
     426            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     427              :   }
     428            0 :   json_decref (abort_obj);
     429            0 :   poah->job = GNUNET_CURL_job_add2 (poah->ctx,
     430              :                                     eh,
     431            0 :                                     poah->post_ctx.headers,
     432              :                                     &handle_abort_finished,
     433              :                                     poah);
     434            0 :   if (NULL == poah->job)
     435            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     436            0 :   return TALER_EC_NONE;
     437              : }
     438              : 
     439              : 
     440              : void
     441            0 : TALER_MERCHANT_post_orders_abort_cancel (
     442              :   struct TALER_MERCHANT_PostOrdersAbortHandle *poah)
     443              : {
     444            0 :   if (NULL != poah->job)
     445              :   {
     446            0 :     GNUNET_CURL_job_cancel (poah->job);
     447            0 :     poah->job = NULL;
     448              :   }
     449            0 :   TALER_curl_easy_post_finished (&poah->post_ctx);
     450            0 :   GNUNET_free (poah->coins);
     451            0 :   GNUNET_free (poah->order_id);
     452            0 :   GNUNET_free (poah->url);
     453            0 :   GNUNET_free (poah->base_url);
     454            0 :   GNUNET_free (poah);
     455            0 : }
     456              : 
     457              : 
     458              : /* end of merchant_api_post-orders-ORDER_ID-abort-new.c */
        

Generated by: LCOV version 2.0-1