From 7cfd98c849c6eb21a1d3b0909e18c51649894ac4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 7 Apr 2020 18:07:13 +0200 Subject: split refund file by handler --- src/backend/Makefile.am | 2 + src/backend/taler-merchant-httpd.c | 14 +- src/backend/taler-merchant-httpd_refund.c | 458 --------------------- src/backend/taler-merchant-httpd_refund.h | 20 - src/backend/taler-merchant-httpd_refund_increase.c | 385 +++++++++++++++++ src/backend/taler-merchant-httpd_refund_increase.h | 49 +++ src/backend/taler-merchant-httpd_refund_lookup.c | 143 +++++++ src/backend/taler-merchant-httpd_refund_lookup.h | 48 +++ 8 files changed, 635 insertions(+), 484 deletions(-) create mode 100644 src/backend/taler-merchant-httpd_refund_increase.c create mode 100644 src/backend/taler-merchant-httpd_refund_increase.h create mode 100644 src/backend/taler-merchant-httpd_refund_lookup.c create mode 100644 src/backend/taler-merchant-httpd_refund_lookup.h (limited to 'src/backend') diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 1f583456..7ec1f6bf 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -30,6 +30,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_poll-payment.c taler-merchant-httpd_poll-payment.h \ taler-merchant-httpd_proposal.c taler-merchant-httpd_proposal.h \ taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \ + taler-merchant-httpd_refund_increase.c taler-merchant-httpd_refund_increase.h \ + taler-merchant-httpd_refund_lookup.c taler-merchant-httpd_refund_lookup.h \ taler-merchant-httpd_tip-authorize.c taler-merchant-httpd_tip-authorize.h \ taler-merchant-httpd_tip-pickup.c taler-merchant-httpd_tip-pickup.h \ taler-merchant-httpd_tip-query.c taler-merchant-httpd_tip-query.h \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index ba15625b..cb7e7011 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -31,22 +31,24 @@ #include #include "taler_merchantdb_lib.h" #include "taler-merchant-httpd.h" -#include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_auditors.h" +#include "taler-merchant-httpd_check-payment.h" #include "taler-merchant-httpd_exchanges.h" +#include "taler-merchant-httpd_history.h" +#include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_order.h" -#include "taler-merchant-httpd_proposal.h" #include "taler-merchant-httpd_pay.h" +#include "taler-merchant-httpd_poll-payment.h" +#include "taler-merchant-httpd_proposal.h" +#include "taler-merchant-httpd_refund.h" +#include "taler-merchant-httpd_refund_increase.h" +#include "taler-merchant-httpd_refund_lookup.h" #include "taler-merchant-httpd_track-transaction.h" #include "taler-merchant-httpd_track-transfer.h" #include "taler-merchant-httpd_tip-authorize.h" #include "taler-merchant-httpd_tip-pickup.h" #include "taler-merchant-httpd_tip-query.h" #include "taler-merchant-httpd_tip-reserve-helper.h" -#include "taler-merchant-httpd_history.h" -#include "taler-merchant-httpd_refund.h" -#include "taler-merchant-httpd_check-payment.h" -#include "taler-merchant-httpd_poll-payment.h" #include "taler-merchant-httpd_config.h" /** diff --git a/src/backend/taler-merchant-httpd_refund.c b/src/backend/taler-merchant-httpd_refund.c index e2f6126c..88e83033 100644 --- a/src/backend/taler-merchant-httpd_refund.c +++ b/src/backend/taler-merchant-httpd_refund.c @@ -60,464 +60,6 @@ struct ProcessRefundData }; -/** - * Information we keep for individual calls - * to requests that parse JSON, but keep no other state. - */ -struct TMH_JsonParseContext -{ - - /** - * This field MUST be first for handle_mhd_completion_callback() to work - * when it treats this struct as a `struct TM_HandlerContext`. - */ - struct TM_HandlerContext hc; - - /** - * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state. - */ - void *json_parse_context; -}; - - -/** - * Make a taler://refund URI - * - * @param connection MHD connection to take host and path from - * @param instance_id merchant's instance ID, must not be NULL - * @param order_id order ID to show a refund for, must not be NULL - * @returns the URI, must be freed with #GNUNET_free - */ -static char * -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; - const char *uri_instance_id; - const char *query; - char *result; - - GNUNET_assert (NULL != instance_id); - GNUNET_assert (NULL != order_id); - host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_HOST); - forwarded_host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - if (NULL != forwarded_host) - host = forwarded_host; - if (NULL == host) - { - /* Should never happen, at least the host header should be defined */ - GNUNET_break (0); - return NULL; - } - uri_path = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL == uri_path) - uri_path = "-"; - if (0 == strcmp (instance_id, - "default")) - uri_instance_id = "-"; - else - uri_instance_id = instance_id; - if (GNUNET_YES == TALER_mhd_is_https (connection)) - query = ""; - else - query = "?insecure=1"; - GNUNET_assert (0 < GNUNET_asprintf (&result, - "taler://refund/%s/%s/%s/%s%s", - host, - uri_path, - uri_instance_id, - order_id, - query)); - return result; -} - - -/** - * Custom cleanup routine for a `struct TMH_JsonParseContext`. - * - * @param hc the `struct TMH_JsonParseContext` to clean up. - */ -static void -json_parse_cleanup (struct TM_HandlerContext *hc) -{ - struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc; - - TALER_MHD_parse_post_cleanup_callback (jpc->json_parse_context); - GNUNET_free (jpc); -} - - -/** - * Handle request for increasing the refund associated with - * a contract. - * - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL - * @return MHD result code - */ -int -MH_handler_refund_increase (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi) -{ - int res; - struct TMH_JsonParseContext *ctx; - struct TALER_Amount refund; - json_t *root; - json_t *contract_terms; - const char *order_id; - const char *reason; - struct GNUNET_HashCode h_contract_terms; - struct GNUNET_CRYPTO_EddsaSignature sig; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("refund", &refund), - GNUNET_JSON_spec_string ("order_id", &order_id), - GNUNET_JSON_spec_string ("reason", &reason), - GNUNET_JSON_spec_end () - }; - enum GNUNET_DB_QueryStatus qs; - enum GNUNET_DB_QueryStatus qsx; - - if (NULL == *connection_cls) - { - ctx = GNUNET_new (struct TMH_JsonParseContext); - ctx->hc.cc = &json_parse_cleanup; - *connection_cls = ctx; - } - else - { - ctx = *connection_cls; - } - res = TALER_MHD_parse_post_json (connection, - &ctx->json_parse_context, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - return MHD_NO; - /* the POST's body has to be further fetched */ - if ( (GNUNET_NO == res) || - (NULL == root) ) - return MHD_YES; - - res = TALER_MHD_parse_json_data (connection, - root, - spec); - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - json_decref (root); - return MHD_YES; - } - if (GNUNET_SYSERR == res) - { - GNUNET_break_op (0); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_JSON_INVALID, - "Request body does not match specification"); - } - - db->preflight (db->cls); - /* Convert order id to h_contract_terms */ - qs = db->find_contract_terms (db->cls, - &contract_terms, - order_id, - &mi->pubkey); - if (0 > qs) - { - /* single, read-only SQL statements should never cause - serialization problems */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFUND_LOOKUP_DB_ERROR, - "An error occurred while retrieving payment data from db"); - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Unknown order id given: `%s'\n", - order_id); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_REFUND_ORDER_ID_UNKNOWN, - "order_id not found in database"); - } - - if (GNUNET_OK != - TALER_JSON_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - json_decref (contract_terms); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_INTERNAL_LOGIC_ERROR, - "Could not hash contract terms"); - } - for (unsigned int i = 0; istart (db->cls, - "increase refund")) - { - GNUNET_break (0); - json_decref (contract_terms); - json_decref (root); - return GNUNET_DB_STATUS_HARD_ERROR; - } - qs = db->increase_refund_for_contract_NT (db->cls, - &h_contract_terms, - &mi->pubkey, - &refund, - reason); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "increase refund returned %d\n", - qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - db->rollback (db->cls); - break; - } - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - db->rollback (db->cls); - continue; - } - /* Got one or more deposits */ - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - db->rollback (db->cls); - break; - } - qsx = db->commit (db->cls); - if (GNUNET_DB_STATUS_HARD_ERROR == qsx) - { - GNUNET_break (0); - qs = qsx; - break; - } - if (GNUNET_DB_STATUS_SOFT_ERROR != qsx) - break; - } - if (0 > qs) - { - /* Special report if retries insufficient */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - GNUNET_JSON_parse_free (spec); - json_decref (contract_terms); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFUND_MERCHANT_DB_COMMIT_ERROR, - "Internal database error or refund amount too big"); - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Refunded amount lower or equal to previous refund: %s\n", - TALER_amount2s (&refund)); - GNUNET_JSON_parse_free (spec); - json_decref (contract_terms); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_REFUND_INCONSISTENT_AMOUNT, - "Amount incorrect: not larger than the previous one"); - } - - /** - * Return to the frontend at this point. The frontend will then return - * a "402 Payment required" carrying a "X-Taler-Refund-Url: www" - * where 'www' is the URL where the wallet can automatically fetch - * the refund permission. - * - * Just a "200 OK" should be fine here, as the frontend has all - * the information needed to generate the right response. - */// - { - struct TALER_MerchantRefundConfirmationPS confirmation = { - .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND_OK), - .purpose.size = htonl (sizeof (confirmation)) - }; - - GNUNET_CRYPTO_hash (order_id, - strlen (order_id), - &confirmation.h_order_id); - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, - &confirmation.purpose, - &sig)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to sign successful refund confirmation\n"); - json_decref (contract_terms); - GNUNET_JSON_parse_free (spec); - json_decref (root); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFUND_MERCHANT_SIGNING_FAILED, - "Refund done, but failed to sign confirmation"); - - } - } - - { - int ret; - char *taler_refund_uri; - - taler_refund_uri = make_taler_refund_uri (connection, - mi->id, - order_id); - ret = TALER_MHD_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:o, s:o, s:s}", - "sig", GNUNET_JSON_from_data_auto (&sig), - "contract_terms", contract_terms, - "taler_refund_uri", taler_refund_uri); - GNUNET_free (taler_refund_uri); - GNUNET_JSON_parse_free (spec); - json_decref (root); - return ret; - } -} - - -/** - * Return refund situation about a contract. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL - * @return MHD result code - */ -int -MH_handler_refund_lookup (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi) -{ - const char *order_id; - struct GNUNET_HashCode h_contract_terms; - json_t *contract_terms; - enum GNUNET_DB_QueryStatus qs; - - order_id = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "order_id"); - if (NULL == order_id) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MISSING, - "order_id"); - } - - /* Convert order id to h_contract_terms */ - contract_terms = NULL; - db->preflight (db->cls); - qs = db->find_contract_terms (db->cls, - &contract_terms, - order_id, - &mi->pubkey); - if (0 > qs) - { - /* single, read-only SQL statements should never cause - serialization problems */ - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - /* Always report on hard error as well to enable diagnostics */ - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_REFUND_LOOKUP_DB_ERROR, - "database error looking up order_id from merchant_contract_terms table"); - } - - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Unknown order id given: `%s'\n", - order_id); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_REFUND_ORDER_ID_UNKNOWN, - "order_id not found in database"); - } - - if (GNUNET_OK != - TALER_JSON_hash (contract_terms, - &h_contract_terms)) - { - GNUNET_break (0); - json_decref (contract_terms); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_INTERNAL_LOGIC_ERROR, - "Could not hash contract terms"); - } - json_decref (contract_terms); - - { - json_t *response; - enum TALER_ErrorCode ec; - const char *errmsg; - - response = TM_get_refund_json (mi, - &h_contract_terms, - &ec, - &errmsg); - if (NULL == response) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - ec, - errmsg); - return TALER_MHD_reply_json_pack (connection, MHD_HTTP_OK, - "{s:o, s:o, s:o}", - "refund_permissions", - response, - "merchant_pub", - GNUNET_JSON_from_data_auto ( - &mi->pubkey), - "h_contract_terms", - GNUNET_JSON_from_data_auto ( - &h_contract_terms)); - } -} - - /** * Function called with information about a refund. * It is responsible for packing up the data to return. diff --git a/src/backend/taler-merchant-httpd_refund.h b/src/backend/taler-merchant-httpd_refund.h index 9713adb0..636d291a 100644 --- a/src/backend/taler-merchant-httpd_refund.h +++ b/src/backend/taler-merchant-httpd_refund.h @@ -26,26 +26,6 @@ #include "taler-merchant-httpd.h" -/** - * Handle request for increasing the refund associated with - * a contract. - * - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL - * @return MHD result code - */ -int -MH_handler_refund_increase (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi); - - /** * Return refund situation about a contract. * diff --git a/src/backend/taler-merchant-httpd_refund_increase.c b/src/backend/taler-merchant-httpd_refund_increase.c new file mode 100644 index 00000000..33cf4d99 --- /dev/null +++ b/src/backend/taler-merchant-httpd_refund_increase.c @@ -0,0 +1,385 @@ +/* + This file is part of TALER + (C) 2014-2020 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file backend/taler-merchant-httpd_refund_increase.c + * @brief Handle request to increase the refund for an order + * @author Marcello Stanisci + */ +#include "platform.h" +#include +#include +#include +#include "taler-merchant-httpd.h" +#include "taler-merchant-httpd_refund.h" + +/** + * How often do we retry the non-trivial refund INSERT database + * transaction? + */ +#define MAX_RETRIES 5 + + +/** + * Information we keep for individual calls + * to requests that parse JSON, but keep no other state. + */ +struct TMH_JsonParseContext +{ + + /** + * This field MUST be first for handle_mhd_completion_callback() to work + * when it treats this struct as a `struct TM_HandlerContext`. + */ + struct TM_HandlerContext hc; + + /** + * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state. + */ + void *json_parse_context; +}; + + +/** + * Make a taler://refund URI + * + * @param connection MHD connection to take host and path from + * @param instance_id merchant's instance ID, must not be NULL + * @param order_id order ID to show a refund for, must not be NULL + * @returns the URI, must be freed with #GNUNET_free + */ +static char * +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; + const char *uri_instance_id; + const char *query; + char *result; + + GNUNET_assert (NULL != instance_id); + GNUNET_assert (NULL != order_id); + host = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_HOST); + forwarded_host = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "X-Forwarded-Host"); + if (NULL != forwarded_host) + host = forwarded_host; + if (NULL == host) + { + /* Should never happen, at least the host header should be defined */ + GNUNET_break (0); + return NULL; + } + uri_path = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "X-Forwarded-Prefix"); + if (NULL == uri_path) + uri_path = "-"; + if (0 == strcmp (instance_id, + "default")) + uri_instance_id = "-"; + else + uri_instance_id = instance_id; + if (GNUNET_YES == TALER_mhd_is_https (connection)) + query = ""; + else + query = "?insecure=1"; + GNUNET_assert (0 < GNUNET_asprintf (&result, + "taler://refund/%s/%s/%s/%s%s", + host, + uri_path, + uri_instance_id, + order_id, + query)); + return result; +} + + +/** + * Custom cleanup routine for a `struct TMH_JsonParseContext`. + * + * @param hc the `struct TMH_JsonParseContext` to clean up. + */ +static void +json_parse_cleanup (struct TM_HandlerContext *hc) +{ + struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc; + + TALER_MHD_parse_post_cleanup_callback (jpc->json_parse_context); + GNUNET_free (jpc); +} + + +/** + * Handle request for increasing the refund associated with + * a contract. + * + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param mi merchant backend instance, never NULL + * @return MHD result code + */ +int +MH_handler_refund_increase (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size, + struct MerchantInstance *mi) +{ + int res; + struct TMH_JsonParseContext *ctx; + struct TALER_Amount refund; + json_t *root; + json_t *contract_terms; + const char *order_id; + const char *reason; + struct GNUNET_HashCode h_contract_terms; + struct GNUNET_CRYPTO_EddsaSignature sig; + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount ("refund", &refund), + GNUNET_JSON_spec_string ("order_id", &order_id), + GNUNET_JSON_spec_string ("reason", &reason), + GNUNET_JSON_spec_end () + }; + enum GNUNET_DB_QueryStatus qs; + enum GNUNET_DB_QueryStatus qsx; + + if (NULL == *connection_cls) + { + ctx = GNUNET_new (struct TMH_JsonParseContext); + ctx->hc.cc = &json_parse_cleanup; + *connection_cls = ctx; + } + else + { + ctx = *connection_cls; + } + res = TALER_MHD_parse_post_json (connection, + &ctx->json_parse_context, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + return MHD_NO; + /* the POST's body has to be further fetched */ + if ( (GNUNET_NO == res) || + (NULL == root) ) + return MHD_YES; + + res = TALER_MHD_parse_json_data (connection, + root, + spec); + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + json_decref (root); + return MHD_YES; + } + if (GNUNET_SYSERR == res) + { + GNUNET_break_op (0); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_JSON_INVALID, + "Request body does not match specification"); + } + + db->preflight (db->cls); + /* Convert order id to h_contract_terms */ + qs = db->find_contract_terms (db->cls, + &contract_terms, + order_id, + &mi->pubkey); + if (0 > qs) + { + /* single, read-only SQL statements should never cause + serialization problems */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFUND_LOOKUP_DB_ERROR, + "An error occurred while retrieving payment data from db"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Unknown order id given: `%s'\n", + order_id); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_REFUND_ORDER_ID_UNKNOWN, + "order_id not found in database"); + } + + if (GNUNET_OK != + TALER_JSON_hash (contract_terms, + &h_contract_terms)) + { + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + json_decref (contract_terms); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_INTERNAL_LOGIC_ERROR, + "Could not hash contract terms"); + } + for (unsigned int i = 0; istart (db->cls, + "increase refund")) + { + GNUNET_break (0); + json_decref (contract_terms); + json_decref (root); + return GNUNET_DB_STATUS_HARD_ERROR; + } + qs = db->increase_refund_for_contract_NT (db->cls, + &h_contract_terms, + &mi->pubkey, + &refund, + reason); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "increase refund returned %d\n", + qs); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + db->rollback (db->cls); + break; + } + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + db->rollback (db->cls); + continue; + } + /* Got one or more deposits */ + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + db->rollback (db->cls); + break; + } + qsx = db->commit (db->cls); + if (GNUNET_DB_STATUS_HARD_ERROR == qsx) + { + GNUNET_break (0); + qs = qsx; + break; + } + if (GNUNET_DB_STATUS_SOFT_ERROR != qsx) + break; + } + if (0 > qs) + { + /* Special report if retries insufficient */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + GNUNET_JSON_parse_free (spec); + json_decref (contract_terms); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFUND_MERCHANT_DB_COMMIT_ERROR, + "Internal database error or refund amount too big"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Refunded amount lower or equal to previous refund: %s\n", + TALER_amount2s (&refund)); + GNUNET_JSON_parse_free (spec); + json_decref (contract_terms); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_REFUND_INCONSISTENT_AMOUNT, + "Amount incorrect: not larger than the previous one"); + } + + /** + * Return to the frontend at this point. The frontend will then return + * a "402 Payment required" carrying a "X-Taler-Refund-Url: www" + * where 'www' is the URL where the wallet can automatically fetch + * the refund permission. + * + * Just a "200 OK" should be fine here, as the frontend has all + * the information needed to generate the right response. + */// + { + struct TALER_MerchantRefundConfirmationPS confirmation = { + .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND_OK), + .purpose.size = htonl (sizeof (confirmation)) + }; + + GNUNET_CRYPTO_hash (order_id, + strlen (order_id), + &confirmation.h_order_id); + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, + &confirmation.purpose, + &sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to sign successful refund confirmation\n"); + json_decref (contract_terms); + GNUNET_JSON_parse_free (spec); + json_decref (root); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFUND_MERCHANT_SIGNING_FAILED, + "Refund done, but failed to sign confirmation"); + + } + } + + { + int ret; + char *taler_refund_uri; + + taler_refund_uri = make_taler_refund_uri (connection, + mi->id, + order_id); + ret = TALER_MHD_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:o, s:s}", + "sig", GNUNET_JSON_from_data_auto (&sig), + "contract_terms", contract_terms, + "taler_refund_uri", taler_refund_uri); + GNUNET_free (taler_refund_uri); + GNUNET_JSON_parse_free (spec); + json_decref (root); + return ret; + } +} + + +/* end of taler-merchant-httpd_refund_increase.c */ diff --git a/src/backend/taler-merchant-httpd_refund_increase.h b/src/backend/taler-merchant-httpd_refund_increase.h new file mode 100644 index 00000000..82a48f4c --- /dev/null +++ b/src/backend/taler-merchant-httpd_refund_increase.h @@ -0,0 +1,49 @@ +/* + This file is part of TALER + (C) 2014, 2015, 2016, 2017 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see +*/ + +/** + * @file backend/taler-merchant-httpd_refund_increase.h + * @brief HTTP serving layer mainly intended to communicate with the frontend + * @author Marcello Stanisci + */ + +#ifndef TALER_MERCHANT_HTTPD_REFUND_INCREASE_H +#define TALER_MERCHANT_HTTPD_REFUND_INCREASE_H +#include +#include "taler-merchant-httpd.h" + + +/** + * Handle request for increasing the refund associated with + * a contract. + * + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param mi merchant backend instance, never NULL + * @return MHD result code + */ +int +MH_handler_refund_increase (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size, + struct MerchantInstance *mi); + + +#endif diff --git a/src/backend/taler-merchant-httpd_refund_lookup.c b/src/backend/taler-merchant-httpd_refund_lookup.c new file mode 100644 index 00000000..36a6b88f --- /dev/null +++ b/src/backend/taler-merchant-httpd_refund_lookup.c @@ -0,0 +1,143 @@ +/* + This file is part of TALER + (C) 2014-2020 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file backend/taler-merchant-httpd_refund_lookup.c + * @brief refund handling logic + * @author Marcello Stanisci + */ +#include "platform.h" +#include +#include +#include +#include "taler-merchant-httpd.h" +#include "taler-merchant-httpd_refund.h" + +/** + * How often do we retry the non-trivial refund INSERT database + * transaction? + */ +#define MAX_RETRIES 5 + + +/** + * Return refund situation about a contract. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param mi merchant backend instance, never NULL + * @return MHD result code + */ +int +MH_handler_refund_lookup (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size, + struct MerchantInstance *mi) +{ + const char *order_id; + struct GNUNET_HashCode h_contract_terms; + json_t *contract_terms; + enum GNUNET_DB_QueryStatus qs; + + order_id = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "order_id"); + if (NULL == order_id) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_PARAMETER_MISSING, + "order_id"); + } + + /* Convert order id to h_contract_terms */ + contract_terms = NULL; + db->preflight (db->cls); + qs = db->find_contract_terms (db->cls, + &contract_terms, + order_id, + &mi->pubkey); + if (0 > qs) + { + /* single, read-only SQL statements should never cause + serialization problems */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_REFUND_LOOKUP_DB_ERROR, + "database error looking up order_id from merchant_contract_terms table"); + } + + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Unknown order id given: `%s'\n", + order_id); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_REFUND_ORDER_ID_UNKNOWN, + "order_id not found in database"); + } + + if (GNUNET_OK != + TALER_JSON_hash (contract_terms, + &h_contract_terms)) + { + GNUNET_break (0); + json_decref (contract_terms); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_INTERNAL_LOGIC_ERROR, + "Could not hash contract terms"); + } + json_decref (contract_terms); + + { + json_t *response; + enum TALER_ErrorCode ec; + const char *errmsg; + + response = TM_get_refund_json (mi, + &h_contract_terms, + &ec, + &errmsg); + if (NULL == response) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + ec, + errmsg); + return TALER_MHD_reply_json_pack (connection, MHD_HTTP_OK, + "{s:o, s:o, s:o}", + "refund_permissions", + response, + "merchant_pub", + GNUNET_JSON_from_data_auto ( + &mi->pubkey), + "h_contract_terms", + GNUNET_JSON_from_data_auto ( + &h_contract_terms)); + } +} + + +/* end of taler-merchant-httpd_refund_lookup.c */ diff --git a/src/backend/taler-merchant-httpd_refund_lookup.h b/src/backend/taler-merchant-httpd_refund_lookup.h new file mode 100644 index 00000000..9d34ad88 --- /dev/null +++ b/src/backend/taler-merchant-httpd_refund_lookup.h @@ -0,0 +1,48 @@ +/* + This file is part of TALER + (C) 2014, 2015, 2016, 2017 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see +*/ + +/** + * @file backend/taler-merchant-httpd_refund_lookup.h + * @brief + * @author Marcello Stanisci + */ + +#ifndef TALER_MERCHANT_HTTPD_REFUND_LOOKUP_H +#define TALER_MERCHANT_HTTPD_REFUND_LOOKUP_H +#include +#include "taler-merchant-httpd.h" + + +/** + * Return refund situation about a contract. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param mi merchant backend instance, never NULL + * @return MHD result code + */ +int +MH_handler_refund_lookup (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size, + struct MerchantInstance *mi); + +#endif -- cgit v1.2.3