diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c | 220 |
1 files changed, 112 insertions, 108 deletions
diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c index 3953fa06..67e1410b 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c +++ b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014-2021 Taler Systems SA + (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -26,6 +26,7 @@ #include <taler/taler_json_lib.h> #include "taler-merchant-httpd_private-post-orders-ID-refund.h" #include "taler-merchant-httpd_private-get-orders.h" +#include "taler-merchant-httpd_helper.h" /** @@ -82,43 +83,21 @@ make_taler_refund_uri (struct MHD_Connection *connection, const char *instance_id, const char *order_id) { - const char *host; - const char *forwarded_host; - const char *uri_path; - struct GNUNET_Buffer buf = { 0 }; + struct GNUNET_Buffer buf; GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != order_id); - host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "Host"); - forwarded_host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != forwarded_host) - host = forwarded_host; - if (NULL == host) + if (GNUNET_OK != + TMH_taler_uri_by_connection (connection, + "refund", + instance_id, + &buf)) { - /* Should never happen, at least the host header should be defined */ GNUNET_break (0); return NULL; } - GNUNET_buffer_write_str (&buf, "taler"); - if (GNUNET_NO == TALER_mhd_is_https (connection)) - GNUNET_buffer_write_str (&buf, "+http"); - GNUNET_buffer_write_str (&buf, "://refund/"); - GNUNET_buffer_write_str (&buf, host); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, uri_path); - if (0 != strcmp ("default", instance_id)) - { - GNUNET_buffer_write_path (&buf, "instances"); - GNUNET_buffer_write_path (&buf, instance_id); - } - GNUNET_buffer_write_path (&buf, order_id); + GNUNET_buffer_write_path (&buf, + order_id); GNUNET_buffer_write_path (&buf, ""); /* Trailing slash */ return GNUNET_buffer_reap_str (&buf); @@ -142,34 +121,70 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, struct TALER_Amount refund; const char *reason; struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("refund", - TMH_currency, - &refund), + TALER_JSON_spec_amount_any ("refund", + &refund), GNUNET_JSON_spec_string ("reason", &reason), GNUNET_JSON_spec_end () }; enum TALER_MERCHANTDB_RefundStatus rs; struct TALER_PrivateContractHashP h_contract; + json_t *contract_terms; + struct GNUNET_TIME_Timestamp timestamp; + + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + { + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + } { enum GNUNET_DB_QueryStatus qs; - json_t *contract_terms; uint64_t order_serial; struct GNUNET_TIME_Timestamp refund_deadline; - struct GNUNET_TIME_Timestamp timestamp; - bool paid = false; qs = TMH_db->lookup_contract_terms (TMH_db->cls, hc->instance->settings.id, hc->infix, &contract_terms, &order_serial, - &paid, NULL); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { - struct GNUNET_JSON_Specification spec[] = { + if (qs < 0) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_contract_terms"); + } + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, + hc->infix); + } + if (GNUNET_OK != + TALER_JSON_contract_hash (contract_terms, + &h_contract)) + { + GNUNET_break (0); + json_decref (contract_terms); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, + "Could not hash contract terms"); + } + { + struct GNUNET_JSON_Specification cspec[] = { GNUNET_JSON_spec_timestamp ("refund_deadline", &refund_deadline), GNUNET_JSON_spec_timestamp ("timestamp", @@ -179,11 +194,10 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, if (GNUNET_YES != GNUNET_JSON_parse (contract_terms, - spec, + cspec, NULL, NULL)) { GNUNET_break (0); - GNUNET_JSON_parse_free (spec); json_decref (contract_terms); return TALER_MHD_reply_with_error ( connection, @@ -191,12 +205,12 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, "mandatory fields missing"); } - json_decref (contract_terms); if (GNUNET_TIME_timestamp_cmp (timestamp, ==, refund_deadline)) { /* refund was never allowed, so we should refuse hard */ + json_decref (contract_terms); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_FORBIDDEN, @@ -210,25 +224,6 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, wire the funds, so we will try to give the refund anyway */ } } - else - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, - hc->infix); - } - } - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - hc->request_body, - spec); - if (GNUNET_OK != res) - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; } TMH_db->preflight (TMH_db->cls); @@ -239,6 +234,7 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, "increase refund")) { GNUNET_break (0); + json_decref (contract_terms); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_START_FAILED, @@ -259,8 +255,41 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, if (TALER_MERCHANTDB_RS_SUCCESS == rs) { enum GNUNET_DB_QueryStatus qs; + json_t *rargs; - qs = TMH_db->commit (TMH_db->cls); + rargs = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_timestamp ("timestamp", + timestamp), + GNUNET_JSON_pack_string ("order_id", + hc->infix), + GNUNET_JSON_pack_object_incref ("contract_terms", + contract_terms), + TALER_JSON_pack_amount ("refund_amount", + &refund), + GNUNET_JSON_pack_string ("reason", + reason) + ); + GNUNET_assert (NULL != rargs); + qs = TMH_trigger_webhook ( + hc->instance->settings.id, + "refund", + rargs); + json_decref (rargs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); + rs = TALER_MERCHANTDB_RS_HARD_ERROR; + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + TMH_db->rollback (TMH_db->cls); + continue; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + qs = TMH_db->commit (TMH_db->cls); + break; + } if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_break (0); @@ -274,9 +303,18 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, } break; } /* retries loop */ + json_decref (contract_terms); switch (rs) { + case TALER_MERCHANTDB_RS_BAD_CURRENCY: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Refund amount %s is not in the currency of the original payment\n", + TALER_amount2s (&refund)); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + "Order was paid in a different currency"); case TALER_MERCHANTDB_RS_TOO_HIGH: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Refusing refund amount %s that is larger than original payment\n", @@ -292,53 +330,19 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_DB_COMMIT_FAILED, NULL); case TALER_MERCHANTDB_RS_NO_SUCH_ORDER: - { - /* We know the order exists from the - "lookup_contract_terms" at the beginning; - so if we get 'no such order' here, it - must be read as "no PAID order" */ - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_ORDER_UNPAID, - hc->infix); - } + /* We know the order exists from the + "lookup_contract_terms" at the beginning; + so if we get 'no such order' here, it + must be read as "no PAID order" */ + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_ORDER_UNPAID, + hc->infix); case TALER_MERCHANTDB_RS_SUCCESS: - { - enum GNUNET_DB_QueryStatus qs; - json_t *contract_terms; - uint64_t order_serial; - bool paid; - - qs = TMH_db->lookup_contract_terms (TMH_db->cls, - hc->instance->settings.id, - hc->infix, - &contract_terms, - &order_serial, - &paid, - NULL); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, - hc->infix); - } - if (GNUNET_OK != - TALER_JSON_contract_hash (contract_terms, - &h_contract)) - { - GNUNET_break (0); - json_decref (contract_terms); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, - "Could not hash contract terms"); - } - json_decref (contract_terms); - } + /* continued below */ break; - } + } /* end switch */ { struct GNUNET_TIME_Timestamp timestamp; |