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:
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