LCOV - code coverage report
Current view: top level - lib - merchant_api_wallet_post_order_refund.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 73 129 56.6 %
Date: 2025-06-23 16:22:09 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2020-2023 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Lesser General Public License as published by the Free Software
       7             :   Foundation; either version 2.1, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Lesser General Public License along with
      14             :   TALER; see the file COPYING.LGPL.  If not, see
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file merchant_api_wallet_post_order_refund.c
      19             :  * @brief Implementation of the (public) POST /orders/ID/refund request
      20             :  * @author Jonathan Buchanan
      21             :  */
      22             : #include "platform.h"
      23             : #include <curl/curl.h>
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_curl_lib.h>
      28             : #include "taler_merchant_service.h"
      29             : #include "merchant_api_common.h"
      30             : #include "merchant_api_curl_defaults.h"
      31             : #include <taler/taler_json_lib.h>
      32             : #include <taler/taler_signatures.h>
      33             : #include <taler/taler_curl_lib.h>
      34             : 
      35             : /**
      36             :  * Maximum number of refunds we return.
      37             :  */
      38             : #define MAX_REFUNDS 1024
      39             : 
      40             : /**
      41             :  * Handle for a (public) POST /orders/ID/refund operation.
      42             :  */
      43             : struct TALER_MERCHANT_WalletOrderRefundHandle
      44             : {
      45             :   /**
      46             :    * Complete URL where the backend offers /refund
      47             :    */
      48             :   char *url;
      49             : 
      50             :   /**
      51             :    * Minor context that holds body and headers.
      52             :    */
      53             :   struct TALER_CURL_PostContext post_ctx;
      54             : 
      55             :   /**
      56             :    * The CURL context to connect to the backend
      57             :    */
      58             :   struct GNUNET_CURL_Context *ctx;
      59             : 
      60             :   /**
      61             :    * The callback to pass the backend response to
      62             :    */
      63             :   TALER_MERCHANT_WalletRefundCallback cb;
      64             : 
      65             :   /**
      66             :    * Clasure to pass to the callback
      67             :    */
      68             :   void *cb_cls;
      69             : 
      70             :   /**
      71             :    * Handle for the request
      72             :    */
      73             :   struct GNUNET_CURL_Job *job;
      74             : };
      75             : 
      76             : 
      77             : /**
      78             :  * Callback to process (public) POST /orders/ID/refund response
      79             :  *
      80             :  * @param cls the `struct TALER_MERCHANT_OrderRefundHandle`
      81             :  * @param response_code HTTP response code, 0 on error
      82             :  * @param response response body, NULL if not JSON
      83             :  */
      84             : static void
      85           2 : handle_refund_finished (void *cls,
      86             :                         long response_code,
      87             :                         const void *response)
      88             : {
      89           2 :   struct TALER_MERCHANT_WalletOrderRefundHandle *orh = cls;
      90           2 :   const json_t *json = response;
      91           2 :   struct TALER_MERCHANT_WalletRefundResponse wrr = {
      92           2 :     .hr.http_status = (unsigned int) response_code,
      93             :     .hr.reply = json
      94             :   };
      95             : 
      96           2 :   orh->job = NULL;
      97           2 :   switch (response_code)
      98             :   {
      99           0 :   case 0:
     100           0 :     wrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     101           0 :     break;
     102           2 :   case MHD_HTTP_OK:
     103             :     {
     104             :       const json_t *refunds;
     105             :       unsigned int refund_len;
     106             :       struct GNUNET_JSON_Specification spec[] = {
     107           2 :         TALER_JSON_spec_amount_any (
     108             :           "refund_amount",
     109             :           &wrr.details.ok.refund_amount),
     110           2 :         GNUNET_JSON_spec_array_const (
     111             :           "refunds",
     112             :           &refunds),
     113           2 :         GNUNET_JSON_spec_fixed_auto (
     114             :           "merchant_pub",
     115             :           &wrr.details.ok.merchant_pub),
     116           2 :         GNUNET_JSON_spec_end ()
     117             :       };
     118             : 
     119           2 :       if (GNUNET_OK !=
     120           2 :           GNUNET_JSON_parse (json,
     121             :                              spec,
     122             :                              NULL, NULL))
     123             :       {
     124           0 :         GNUNET_break_op (0);
     125           0 :         wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     126           0 :         wrr.hr.http_status = 0;
     127           0 :         break;
     128             :       }
     129           2 :       refund_len = json_array_size (refunds);
     130           2 :       if ( (json_array_size (refunds) != (size_t)  refund_len) ||
     131             :            (refund_len > MAX_REFUNDS) )
     132             :       {
     133           0 :         GNUNET_break (0);
     134           0 :         wrr.hr.ec = TALER_EC_GENERIC_ALLOCATION_FAILURE;
     135           0 :         wrr.hr.http_status = 0;
     136           0 :         break;
     137             :       }
     138           2 :       {
     139           2 :         struct TALER_MERCHANT_RefundDetail rds[GNUNET_NZL (refund_len)];
     140             : 
     141           2 :         memset (rds,
     142             :                 0,
     143             :                 sizeof (rds));
     144           6 :         for (unsigned int i = 0; i<refund_len; i++)
     145             :         {
     146           4 :           struct TALER_MERCHANT_RefundDetail *rd = &rds[i];
     147           4 :           const json_t *jrefund = json_array_get (refunds,
     148             :                                                   i);
     149             :           const char *refund_status_type;
     150             :           uint32_t exchange_status;
     151           4 :           uint32_t eec = 0;
     152             :           struct GNUNET_JSON_Specification espec[] = {
     153           4 :             GNUNET_JSON_spec_string ("type",
     154             :                                      &refund_status_type),
     155           4 :             GNUNET_JSON_spec_uint32 ("exchange_status",
     156             :                                      &exchange_status),
     157           4 :             GNUNET_JSON_spec_uint64 ("rtransaction_id",
     158             :                                      &rd->rtransaction_id),
     159           4 :             GNUNET_JSON_spec_fixed_auto ("coin_pub",
     160             :                                          &rd->coin_pub),
     161           4 :             TALER_JSON_spec_amount_any ("refund_amount",
     162             :                                         &rd->refund_amount),
     163           4 :             GNUNET_JSON_spec_mark_optional (
     164             :               GNUNET_JSON_spec_object_const ("exchange_reply",
     165             :                                              &rd->hr.reply),
     166             :               NULL),
     167           4 :             GNUNET_JSON_spec_mark_optional (
     168             :               GNUNET_JSON_spec_uint32 ("exchange_code",
     169             :                                        &eec),
     170             :               NULL),
     171           4 :             GNUNET_JSON_spec_end ()
     172             :           };
     173             : 
     174           4 :           if (GNUNET_OK !=
     175           4 :               GNUNET_JSON_parse (jrefund,
     176             :                                  espec,
     177             :                                  NULL, NULL))
     178             :           {
     179           0 :             GNUNET_break_op (0);
     180           0 :             wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     181           0 :             wrr.hr.http_status = 0;
     182           0 :             goto finish;
     183             :           }
     184             : 
     185           4 :           rd->hr.http_status = exchange_status;
     186           4 :           rd->hr.ec = (enum TALER_ErrorCode) eec;
     187           4 :           switch (exchange_status)
     188             :           {
     189           4 :           case MHD_HTTP_OK:
     190             :             {
     191             :               struct GNUNET_JSON_Specification rspec[] = {
     192           4 :                 GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     193             :                                              &rd->details.ok.exchange_sig),
     194           4 :                 GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     195             :                                              &rd->details.ok.exchange_pub),
     196           4 :                 GNUNET_JSON_spec_end ()
     197             :               };
     198             : 
     199           4 :               if (GNUNET_OK !=
     200           4 :                   GNUNET_JSON_parse (jrefund,
     201             :                                      rspec,
     202             :                                      NULL,
     203             :                                      NULL))
     204             :               {
     205           0 :                 GNUNET_break_op (0);
     206           0 :                 wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     207           0 :                 wrr.hr.http_status = 0;
     208           0 :                 goto finish;
     209             :               }
     210             :               /* check that type field is correct */
     211           4 :               if (0 != strcmp ("success",
     212             :                                refund_status_type))
     213             :               {
     214           0 :                 GNUNET_break_op (0);
     215           0 :                 wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     216           0 :                 wrr.hr.http_status = 0;
     217           0 :                 goto finish;
     218             :               }
     219             :             }
     220           4 :             break; /* end MHD_HTTP_OK */
     221           0 :           default:
     222           0 :             if (0 != strcmp ("failure",
     223             :                              refund_status_type))
     224             :             {
     225           0 :               GNUNET_break_op (0);
     226           0 :               wrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     227           0 :               wrr.hr.http_status = 0;
     228           0 :               goto finish;
     229             :             }
     230             :           } /* switch on exchange status code */
     231             :         } /* for all refunds */
     232             : 
     233           2 :         wrr.details.ok.refunds = rds;
     234           2 :         wrr.details.ok.refunds_length = refund_len;
     235           2 :         orh->cb (orh->cb_cls,
     236             :                  &wrr);
     237           2 :         TALER_MERCHANT_wallet_post_order_refund_cancel (orh);
     238           2 :         return;
     239             :       } /* end 'rds' scope */
     240             :     } /* case MHD_HTTP_OK */
     241             :     break;
     242           0 :   case MHD_HTTP_NO_CONTENT:
     243           0 :     break;
     244           0 :   case MHD_HTTP_CONFLICT:
     245             :   case MHD_HTTP_NOT_FOUND:
     246           0 :     wrr.hr.ec = TALER_JSON_get_error_code (json);
     247           0 :     wrr.hr.hint = TALER_JSON_get_error_hint (json);
     248           0 :     break;
     249           0 :   default:
     250           0 :     GNUNET_break_op (0); /* unexpected status code */
     251           0 :     TALER_MERCHANT_parse_error_details_ (json,
     252             :                                          response_code,
     253             :                                          &wrr.hr);
     254           0 :     break;
     255             :   }
     256           0 : finish:
     257           0 :   orh->cb (orh->cb_cls,
     258             :            &wrr);
     259           0 :   TALER_MERCHANT_wallet_post_order_refund_cancel (orh);
     260             : }
     261             : 
     262             : 
     263             : struct TALER_MERCHANT_WalletOrderRefundHandle *
     264           2 : TALER_MERCHANT_wallet_post_order_refund (
     265             :   struct GNUNET_CURL_Context *ctx,
     266             :   const char *backend_url,
     267             :   const char *order_id,
     268             :   const struct TALER_PrivateContractHashP *h_contract_terms,
     269             :   TALER_MERCHANT_WalletRefundCallback cb,
     270             :   void *cb_cls)
     271             : {
     272             :   struct TALER_MERCHANT_WalletOrderRefundHandle *orh;
     273             :   json_t *req;
     274             :   CURL *eh;
     275             : 
     276           2 :   orh = GNUNET_new (struct TALER_MERCHANT_WalletOrderRefundHandle);
     277           2 :   orh->ctx = ctx;
     278           2 :   orh->cb = cb;
     279           2 :   orh->cb_cls = cb_cls;
     280             :   {
     281             :     char *path;
     282             : 
     283           2 :     GNUNET_asprintf (&path,
     284             :                      "orders/%s/refund",
     285             :                      order_id);
     286           2 :     orh->url = TALER_url_join (backend_url,
     287             :                                path,
     288             :                                NULL);
     289           2 :     GNUNET_free (path);
     290             :   }
     291           2 :   if (NULL == orh->url)
     292             :   {
     293           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     294             :                 "Could not construct request URL.\n");
     295           0 :     GNUNET_free (orh);
     296           0 :     return NULL;
     297             :   }
     298           2 :   req = GNUNET_JSON_PACK (
     299             :     GNUNET_JSON_pack_data_auto ("h_contract",
     300             :                                 h_contract_terms));
     301           2 :   eh = TALER_MERCHANT_curl_easy_get_ (orh->url);
     302           2 :   if (GNUNET_OK !=
     303           2 :       TALER_curl_easy_post (&orh->post_ctx,
     304             :                             eh,
     305             :                             req))
     306             :   {
     307           0 :     GNUNET_break (0);
     308           0 :     json_decref (req);
     309           0 :     curl_easy_cleanup (eh);
     310           0 :     GNUNET_free (orh->url);
     311           0 :     GNUNET_free (orh);
     312           0 :     return NULL;
     313             :   }
     314           2 :   json_decref (req);
     315           4 :   orh->job = GNUNET_CURL_job_add2 (ctx,
     316             :                                    eh,
     317           2 :                                    orh->post_ctx.headers,
     318             :                                    &handle_refund_finished,
     319             :                                    orh);
     320           2 :   if (NULL == orh->job)
     321             :   {
     322           0 :     GNUNET_free (orh->url);
     323           0 :     GNUNET_free (orh);
     324           0 :     return NULL;
     325             :   }
     326           2 :   return orh;
     327             : }
     328             : 
     329             : 
     330             : void
     331           2 : TALER_MERCHANT_wallet_post_order_refund_cancel (
     332             :   struct TALER_MERCHANT_WalletOrderRefundHandle *orh)
     333             : {
     334           2 :   if (NULL != orh->job)
     335             :   {
     336           0 :     GNUNET_CURL_job_cancel (orh->job);
     337           0 :     orh->job = NULL;
     338             :   }
     339           2 :   TALER_curl_easy_post_finished (&orh->post_ctx);
     340           2 :   GNUNET_free (orh->url);
     341           2 :   GNUNET_free (orh);
     342           2 : }
     343             : 
     344             : 
     345             : /* end of merchant_api_wallet_post_order_refund.c */

Generated by: LCOV version 1.16