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

            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 2.0-1