merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit c518e5ee51cd2a57b0a6c768cff2cbc46c04f62e
parent 812df5ba2672e0d9d5b06c47d05bf0636274586f
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu,  5 Feb 2026 21:04:32 +0100

add new endpoints for #9961 and #10965

Diffstat:
Msrc/backend/Makefile.am | 4++++
Msrc/backend/taler-merchant-httpd_dispatcher.c | 11+++++++++++
Asrc/backend/taler-merchant-httpd_post-orders-ID-unclaim.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backend/taler-merchant-httpd_post-orders-ID-unclaim.h | 40++++++++++++++++++++++++++++++++++++++++
Asrc/backend/taler-merchant-httpd_private-get-incoming-ID.c | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backend/taler-merchant-httpd_private-get-incoming-ID.h | 41+++++++++++++++++++++++++++++++++++++++++
6 files changed, 417 insertions(+), 0 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -159,6 +159,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-get-otp-devices-ID.h \ taler-merchant-httpd_private-get-incoming.c \ taler-merchant-httpd_private-get-incoming.h \ + taler-merchant-httpd_private-get-incoming-ID.c \ + taler-merchant-httpd_private-get-incoming-ID.h \ taler-merchant-httpd_private-get-transfers.c \ taler-merchant-httpd_private-get-transfers.h \ taler-merchant-httpd_private-get-templates.c \ @@ -237,6 +239,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_post-orders-ID-paid.h \ taler-merchant-httpd_post-orders-ID-refund.c \ taler-merchant-httpd_post-orders-ID-refund.h \ + taler-merchant-httpd_post-orders-ID-unclaim.c \ + taler-merchant-httpd_post-orders-ID-unclaim.h \ taler-merchant-httpd_post-templates-ID.c \ taler-merchant-httpd_post-templates-ID.h \ taler-merchant-httpd_post-reports-ID.c \ diff --git a/src/backend/taler-merchant-httpd_dispatcher.c b/src/backend/taler-merchant-httpd_dispatcher.c @@ -99,6 +99,7 @@ #include "taler-merchant-httpd_post-orders-ID-claim.h" #include "taler-merchant-httpd_post-orders-ID-paid.h" #include "taler-merchant-httpd_post-orders-ID-pay.h" +#include "taler-merchant-httpd_post-orders-ID-unclaim.h" #include "taler-merchant-httpd_post-templates-ID.h" #include "taler-merchant-httpd_post-orders-ID-refund.h" #include "taler-merchant-httpd_get-webui.h" @@ -1143,6 +1144,16 @@ determine_handler_group (const char **urlp, to set a conservative bound for sane wallets */ .max_upload = 1024 * 1024 }, + /* POST /orders/$ID/unclaim: */ + { + .url_prefix = "/orders/", + .have_id_segment = true, + .url_suffix = "unclaim", + .method = MHD_HTTP_METHOD_POST, + .handler = &TMH_post_orders_ID_unclaim, + /* the body should be very small */ + .max_upload = 1024 + }, /* POST /orders/$ID/pay: */ { .url_prefix = "/orders/", diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-unclaim.c b/src/backend/taler-merchant-httpd_post-orders-ID-unclaim.c @@ -0,0 +1,122 @@ +/* + This file is part of TALER + (C) 2026 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 <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_post-orders-ID-unclaim.c + * @brief headers for POST /orders/$ID/unclaim handler + * @author Christian Grothoff + */ +#include "platform.h" +#include <jansson.h> +#include <taler/taler_signatures.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_json_lib.h> +#include "taler-merchant-httpd_private-get-orders.h" +#include "taler-merchant-httpd_post-orders-ID-unclaim.h" + + +MHD_RESULT +TMH_post_orders_ID_unclaim (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + const char *order_id = hc->infix; + struct GNUNET_CRYPTO_EddsaPublicKey nonce; + struct GNUNET_CRYPTO_EddsaSignature nsig; + struct GNUNET_HashCode h_contract; + enum GNUNET_DB_QueryStatus qs; + + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("unclaim_sig", + &nsig), + GNUNET_JSON_spec_fixed_auto ("nonce", + &nonce), + GNUNET_JSON_spec_fixed_auto ("h_contract", + &h_contract), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + } + + if (GNUNET_OK != + TALER_wallet_order_unclaim_verify (&h_contract, + &nonce, + &nsig)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "unclaim_sig"); + } + TMH_db->preflight (TMH_db->cls); + qs = TMH_db->insert_unclaim_signature (TMH_db->cls, + hc->instance->settings.id, + &hc->instance->merchant_pub, + order_id, + &nonce, + &h_contract, + &nsig); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_COMMIT_FAILED, + "insert_unclaim_signature"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_SOFT_FAILURE, + "insert_unclaim_signature"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_NOT_FOUND, + order_id); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; /* Good! return signature (below) */ + } + + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); +} + + +/* end of taler-merchant-httpd_post-orders-ID-unclaim.c */ diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-unclaim.h b/src/backend/taler-merchant-httpd_post-orders-ID-unclaim.h @@ -0,0 +1,40 @@ +/* + This file is part of TALER + (C) 2026 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_post-orders-ID-unclaim.h + * @brief headers for POST /orders/$ID/unclaim handler + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_POST_ORDERS_ID_UNCLAIM_H +#define TALER_MERCHANT_HTTPD_POST_ORDERS_ID_UNCLAIM_H +#include <microhttpd.h> +#include "taler-merchant-httpd.h" + +/** + * Manage a POST /orders/$ID/unclaim request. Allows the client to + * unclaim an order. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_post_orders_ID_unclaim (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-incoming-ID.c b/src/backend/taler-merchant-httpd_private-get-incoming-ID.c @@ -0,0 +1,199 @@ +/* + This file is part of TALER + (C) 2026 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-incoming-ID.c + * @brief implement API for obtaining details about an expected incoming wire transfer + * @author Christian Grothoff + */ +#include "platform.h" +#include <jansson.h> +#include <taler/taler_json_lib.h> +#include "taler-merchant-httpd_private-get-incoming-ID.h" + + +/** + * Function called with information about orders aggregated into + * a wire transfer. + * Generate a response (array entry) based on the given arguments. + * + * @param cls closure with a `json_t *` array to build up the response + * @param order_id ID of the order that was paid and aggregated + * @param remaining_deposit deposited amount minus any refunds + * @param deposit_fee deposit fees paid to the exchange for the order + */ +static void +reconciliation_cb (void *cls, + const char *order_id, + const struct TALER_Amount *remaining_deposit, + const struct TALER_Amount *deposit_fee) +{ + json_t *rd = cls; + json_t *r; + + r = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("order_id", + order_id), + TALER_JSON_pack_amount ("remaining_deposit", + remaining_deposit), + TALER_JSON_pack_amount ("deposit_fee", + deposit_fee)); + GNUNET_assert (0 == + json_array_append_new (rd, + r)); +} + + +/** + * Manages a GET /private/incoming call. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_incoming_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + unsigned long long serial_id; + struct TALER_Amount wire_fee; + bool no_fee; + + { + char dummy; + + if (1 != + sscanf (hc->infix, + "%llu%c", + &serial_id, + &dummy)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "transfer ID must be a number"); + } + } + + TMH_db->preflight (TMH_db->cls); + { + struct GNUNET_TIME_Timestamp execution_date; + char *payto_uri; + struct TALER_MasterPublicKeyP master_pub; + enum GNUNET_DB_QueryStatus qs; + + qs = TMH_db->lookup_expected_transfer (TMH_db->cls, + hc->instance->settings.id, + serial_id, + &execution_date, + &payto_uri, + &master_pub); + if (0 > qs) + { + /* Simple select queries should not cause serialization issues */ + 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_GENERIC_DB_FETCH_FAILED, + "lookup_expected_transfer"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_EXPECTED_TRANSFER_UNKNOWN, + hc->infix); + } + + { + char *method; + struct GNUNET_TIME_Timestamp start_date; + struct GNUNET_TIME_Timestamp end_date; + struct TALER_MasterSignatureP master_sig; + struct TALER_WireFeeSet fees; + + method = TALER_payto_get_method (payto_uri); + GNUNET_free (payto_uri); + qs = TMH_db->lookup_wire_fee (TMH_db->cls, + &master_pub, + method, + execution_date, + &fees, + &start_date, + &end_date, + &master_sig); + GNUNET_free (method); + if (0 > qs) + { + /* Simple select queries should not cause serialization issues */ + 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_GENERIC_DB_FETCH_FAILED, + "lookup_wire_fee"); + } + no_fee = (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs); + if (! no_fee) + wire_fee = fees.wire; + } + + } + + { + enum GNUNET_DB_QueryStatus qs; + json_t *rd; + + rd = json_array (); + GNUNET_assert (NULL != rd); + qs = TMH_db->lookup_reconciliation_details (TMH_db->cls, + hc->instance->settings.id, + serial_id, + &reconciliation_cb, + rd); + if (0 > qs) + { + /* Simple select queries should not cause serialization issues */ + 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_GENERIC_DB_FETCH_FAILED, + "lookup_reconciliation_details"); + } + + return TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_allow_null ( + TALER_JSON_pack_amount ("wire_fee", + no_fee ? NULL : &wire_fee)), + GNUNET_JSON_pack_array_steal ("reconciliation_details", + rd)); + } +} + + +/* end of taler-merchant-httpd_private-get-incoming-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-incoming-ID.h b/src/backend/taler-merchant-httpd_private-get-incoming-ID.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2026 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-incoming-ID.h + * @brief headers for GET /incoming/$ID handler + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_INCOMING_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_INCOMING_ID_H +#include <microhttpd.h> +#include "taler-merchant-httpd.h" + + +/** + * Manages a GET /private/incoming/$ID call. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_incoming_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + + +#endif