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

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2020-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-refund-new.c
      21              :  * @brief Implementation of the POST /orders/$ORDER_ID/refund 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-refund.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/refund operation.
      46              :  */
      47              : struct TALER_MERCHANT_PostOrdersRefundHandle
      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_PostOrdersRefundCallback cb;
      68              : 
      69              :   /**
      70              :    * Closure for @a cb.
      71              :    */
      72              :   TALER_MERCHANT_POST_ORDERS_REFUND_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              :    * Hash of the contract terms.
      91              :    */
      92              :   struct TALER_PrivateContractHashP h_contract_terms;
      93              : };
      94              : 
      95              : 
      96              : /**
      97              :  * Function called when we're done processing the
      98              :  * HTTP POST /orders/$ORDER_ID/refund request.
      99              :  *
     100              :  * @param cls the `struct TALER_MERCHANT_PostOrdersRefundHandle`
     101              :  * @param response_code HTTP response code, 0 on error
     102              :  * @param response response body, NULL if not in JSON
     103              :  */
     104              : static void
     105            0 : handle_refund_finished (void *cls,
     106              :                         long response_code,
     107              :                         const void *response)
     108              : {
     109            0 :   struct TALER_MERCHANT_PostOrdersRefundHandle *porh = cls;
     110            0 :   const json_t *json = response;
     111            0 :   struct TALER_MERCHANT_PostOrdersRefundResponse orr = {
     112            0 :     .hr.http_status = (unsigned int) response_code,
     113              :     .hr.reply = json
     114              :   };
     115              : 
     116            0 :   porh->job = NULL;
     117            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     118              :               "POST /orders/$ID/refund completed with response code %u\n",
     119              :               (unsigned int) response_code);
     120            0 :   switch (response_code)
     121              :   {
     122            0 :   case 0:
     123            0 :     orr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     124            0 :     break;
     125            0 :   case MHD_HTTP_OK:
     126              :     {
     127              :       const json_t *refunds;
     128              :       unsigned int refund_len;
     129              :       struct GNUNET_JSON_Specification spec[] = {
     130            0 :         GNUNET_JSON_spec_array_const (
     131              :           "refunds",
     132              :           &refunds),
     133            0 :         TALER_JSON_spec_amount_any (
     134              :           "refund_amount",
     135              :           &orr.details.ok.refund_amount),
     136            0 :         GNUNET_JSON_spec_fixed_auto (
     137              :           "merchant_pub",
     138              :           &orr.details.ok.merchant_pub),
     139            0 :         GNUNET_JSON_spec_end ()
     140              :       };
     141              : 
     142            0 :       if (GNUNET_OK !=
     143            0 :           GNUNET_JSON_parse (json,
     144              :                              spec,
     145              :                              NULL, NULL))
     146              :       {
     147            0 :         GNUNET_break_op (0);
     148            0 :         orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     149            0 :         orr.hr.http_status = 0;
     150            0 :         break;
     151              :       }
     152            0 :       refund_len = (unsigned int) json_array_size (refunds);
     153            0 :       if ( (json_array_size (refunds) != (size_t) refund_len) ||
     154              :            (refund_len > MAX_REFUNDS) )
     155              :       {
     156            0 :         GNUNET_break (0);
     157            0 :         orr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
     158            0 :         orr.hr.http_status = 0;
     159            0 :         break;
     160              :       }
     161            0 :       {
     162            0 :         struct TALER_MERCHANT_PostOrdersRefundDetail rds[GNUNET_NZL (
     163              :                                                            refund_len)];
     164              : 
     165            0 :         memset (rds,
     166              :                 0,
     167              :                 sizeof (rds));
     168            0 :         for (unsigned int i = 0; i < refund_len; i++)
     169              :         {
     170            0 :           struct TALER_MERCHANT_PostOrdersRefundDetail *rd = &rds[i];
     171            0 :           const json_t *jrefund = json_array_get (refunds,
     172              :                                                   i);
     173              :           const char *refund_status_type;
     174              :           uint32_t exchange_status;
     175            0 :           uint32_t eec = 0;
     176              :           struct GNUNET_JSON_Specification espec[] = {
     177            0 :             GNUNET_JSON_spec_string ("type",
     178              :                                      &refund_status_type),
     179            0 :             GNUNET_JSON_spec_uint32 ("exchange_status",
     180              :                                      &exchange_status),
     181            0 :             GNUNET_JSON_spec_uint64 ("rtransaction_id",
     182              :                                      &rd->rtransaction_id),
     183            0 :             GNUNET_JSON_spec_fixed_auto ("coin_pub",
     184              :                                          &rd->coin_pub),
     185            0 :             TALER_JSON_spec_amount_any ("refund_amount",
     186              :                                         &rd->refund_amount),
     187            0 :             GNUNET_JSON_spec_mark_optional (
     188              :               GNUNET_JSON_spec_object_const ("exchange_reply",
     189              :                                              &rd->exchange_reply),
     190              :               NULL),
     191            0 :             GNUNET_JSON_spec_mark_optional (
     192              :               GNUNET_JSON_spec_uint32 ("exchange_code",
     193              :                                        &eec),
     194              :               NULL),
     195            0 :             GNUNET_JSON_spec_end ()
     196              :           };
     197              : 
     198            0 :           if (GNUNET_OK !=
     199            0 :               GNUNET_JSON_parse (jrefund,
     200              :                                  espec,
     201              :                                  NULL, NULL))
     202              :           {
     203            0 :             GNUNET_break_op (0);
     204            0 :             orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     205            0 :             orr.hr.http_status = 0;
     206            0 :             goto finish;
     207              :           }
     208              : 
     209            0 :           rd->exchange_http_status = exchange_status;
     210            0 :           rd->ec = (enum TALER_ErrorCode) eec;
     211            0 :           switch (exchange_status)
     212              :           {
     213            0 :           case MHD_HTTP_OK:
     214              :             {
     215              :               struct GNUNET_JSON_Specification rspec[] = {
     216            0 :                 GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     217              :                                              &rd->exchange_sig),
     218            0 :                 GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     219              :                                              &rd->exchange_pub),
     220            0 :                 GNUNET_JSON_spec_end ()
     221              :               };
     222              : 
     223            0 :               if (GNUNET_OK !=
     224            0 :                   GNUNET_JSON_parse (jrefund,
     225              :                                      rspec,
     226              :                                      NULL,
     227              :                                      NULL))
     228              :               {
     229            0 :                 GNUNET_break_op (0);
     230            0 :                 orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     231            0 :                 orr.hr.http_status = 0;
     232            0 :                 goto finish;
     233              :               }
     234            0 :               if (0 != strcmp ("success",
     235              :                                refund_status_type))
     236              :               {
     237            0 :                 GNUNET_break_op (0);
     238            0 :                 orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     239            0 :                 orr.hr.http_status = 0;
     240            0 :                 goto finish;
     241              :               }
     242              :             }
     243            0 :             break;
     244            0 :           default:
     245            0 :             if (0 != strcmp ("failure",
     246              :                              refund_status_type))
     247              :             {
     248            0 :               GNUNET_break_op (0);
     249            0 :               orr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     250            0 :               orr.hr.http_status = 0;
     251            0 :               goto finish;
     252              :             }
     253              :           }
     254              :         }
     255              : 
     256            0 :         orr.details.ok.refunds = rds;
     257            0 :         orr.details.ok.num_refunds = refund_len;
     258            0 :         porh->cb (porh->cb_cls,
     259              :                   &orr);
     260            0 :         TALER_MERCHANT_post_orders_refund_cancel (porh);
     261            0 :         return;
     262              :       }
     263              :     }
     264              :     break;
     265            0 :   case MHD_HTTP_NO_CONTENT:
     266            0 :     break;
     267            0 :   case MHD_HTTP_NOT_FOUND:
     268            0 :     orr.hr.ec = TALER_JSON_get_error_code (json);
     269            0 :     orr.hr.hint = TALER_JSON_get_error_hint (json);
     270            0 :     break;
     271            0 :   default:
     272            0 :     GNUNET_break_op (0);
     273            0 :     TALER_MERCHANT_parse_error_details_ (json,
     274              :                                          response_code,
     275              :                                          &orr.hr);
     276            0 :     break;
     277              :   }
     278            0 : finish:
     279            0 :   porh->cb (porh->cb_cls,
     280              :             &orr);
     281            0 :   TALER_MERCHANT_post_orders_refund_cancel (porh);
     282              : }
     283              : 
     284              : 
     285              : struct TALER_MERCHANT_PostOrdersRefundHandle *
     286            0 : TALER_MERCHANT_post_orders_refund_create (
     287              :   struct GNUNET_CURL_Context *ctx,
     288              :   const char *url,
     289              :   const char *order_id,
     290              :   const struct TALER_PrivateContractHashP *h_contract_terms)
     291              : {
     292              :   struct TALER_MERCHANT_PostOrdersRefundHandle *porh;
     293              : 
     294            0 :   porh = GNUNET_new (struct TALER_MERCHANT_PostOrdersRefundHandle);
     295            0 :   porh->ctx = ctx;
     296            0 :   porh->base_url = GNUNET_strdup (url);
     297            0 :   porh->order_id = GNUNET_strdup (order_id);
     298            0 :   porh->h_contract_terms = *h_contract_terms;
     299            0 :   return porh;
     300              : }
     301              : 
     302              : 
     303              : enum TALER_ErrorCode
     304            0 : TALER_MERCHANT_post_orders_refund_start (
     305              :   struct TALER_MERCHANT_PostOrdersRefundHandle *porh,
     306              :   TALER_MERCHANT_PostOrdersRefundCallback cb,
     307              :   TALER_MERCHANT_POST_ORDERS_REFUND_RESULT_CLOSURE *cb_cls)
     308              : {
     309              :   json_t *req_obj;
     310              :   CURL *eh;
     311              : 
     312            0 :   porh->cb = cb;
     313            0 :   porh->cb_cls = cb_cls;
     314              :   {
     315              :     char *path;
     316              : 
     317            0 :     GNUNET_asprintf (&path,
     318              :                      "orders/%s/refund",
     319              :                      porh->order_id);
     320            0 :     porh->url = TALER_url_join (porh->base_url,
     321              :                                 path,
     322              :                                 NULL);
     323            0 :     GNUNET_free (path);
     324              :   }
     325            0 :   if (NULL == porh->url)
     326            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     327            0 :   req_obj = GNUNET_JSON_PACK (
     328              :     GNUNET_JSON_pack_data_auto ("h_contract",
     329              :                                 &porh->h_contract_terms));
     330            0 :   eh = TALER_MERCHANT_curl_easy_get_ (porh->url);
     331            0 :   if ( (NULL == eh) ||
     332              :        (GNUNET_OK !=
     333            0 :         TALER_curl_easy_post (&porh->post_ctx,
     334              :                               eh,
     335              :                               req_obj)) )
     336              :   {
     337            0 :     GNUNET_break (0);
     338            0 :     json_decref (req_obj);
     339            0 :     if (NULL != eh)
     340            0 :       curl_easy_cleanup (eh);
     341            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     342              :   }
     343            0 :   json_decref (req_obj);
     344            0 :   porh->job = GNUNET_CURL_job_add2 (porh->ctx,
     345              :                                     eh,
     346            0 :                                     porh->post_ctx.headers,
     347              :                                     &handle_refund_finished,
     348              :                                     porh);
     349            0 :   if (NULL == porh->job)
     350            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     351            0 :   return TALER_EC_NONE;
     352              : }
     353              : 
     354              : 
     355              : void
     356            0 : TALER_MERCHANT_post_orders_refund_cancel (
     357              :   struct TALER_MERCHANT_PostOrdersRefundHandle *porh)
     358              : {
     359            0 :   if (NULL != porh->job)
     360              :   {
     361            0 :     GNUNET_CURL_job_cancel (porh->job);
     362            0 :     porh->job = NULL;
     363              :   }
     364            0 :   TALER_curl_easy_post_finished (&porh->post_ctx);
     365            0 :   GNUNET_free (porh->order_id);
     366            0 :   GNUNET_free (porh->url);
     367            0 :   GNUNET_free (porh->base_url);
     368            0 :   GNUNET_free (porh);
     369            0 : }
     370              : 
     371              : 
     372              : /* end of merchant_api_post-orders-ORDER_ID-refund-new.c */
        

Generated by: LCOV version 2.0-1