commit 60e73284d4930e8e396fe6e92d55dfbdbc683887
parent 287d4ce69a87b92f7b4ca4e4e668cb97c641b236
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 23 Mar 2026 14:14:54 +0100
more adaptations to latest bank API for v34
Diffstat:
7 files changed, 801 insertions(+), 783 deletions(-)
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
@@ -229,8 +229,8 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_post-private-transfers.h \
taler-merchant-httpd_post-private-webhooks.c \
taler-merchant-httpd_post-private-webhooks.h \
- taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c \
- taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h \
+ taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c \
+ taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h \
taler-merchant-httpd_post-challenge-ID.c \
taler-merchant-httpd_post-challenge-ID.h \
taler-merchant-httpd_post-challenge-ID-confirm.c \
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -50,7 +50,7 @@
#include "taler-merchant-httpd_get-private-orders.h"
#include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h"
#include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h"
-#include "taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h"
+#include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
/**
* Backlog for listen operation on unix-domain sockets.
diff --git a/src/backend/taler-merchant-httpd_dispatcher.c b/src/backend/taler-merchant-httpd_dispatcher.c
@@ -93,7 +93,7 @@
#include "taler-merchant-httpd_post-private-tokenfamilies.h"
#include "taler-merchant-httpd_post-private-transfers.h"
#include "taler-merchant-httpd_post-private-webhooks.h"
-#include "taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h"
+#include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
#include "taler-merchant-httpd_post-challenge-ID.h"
#include "taler-merchant-httpd_post-challenge-ID-confirm.h"
#include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h"
diff --git a/src/backend/taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c b/src/backend/taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c
@@ -0,0 +1,743 @@
+/*
+ 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 Affero General Public License
+ along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.c
+ * @brief Handle POST /private/accounts/$H_WIRE/kycauth requests
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include <jansson.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_mhd_lib.h>
+#include <taler/taler_bank_service.h>
+#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
+
+
+/**
+ * Processing phases for a kycauth POST request.
+ */
+enum KycAuthPhase
+{
+ /**
+ * Parse the request body and URL path.
+ */
+ KP_PARSE = 0,
+
+ /**
+ * Fetch exchange /keys (and optionally register with bank gateway).
+ * The connection is suspended in this phase.
+ */
+ KP_CHECK_EXCHANGES,
+
+ /**
+ * Return the prepared response to the client.
+ */
+ KP_RETURN_RESPONSE,
+
+ /**
+ * Return MHD_YES to finish handling.
+ */
+ KP_END_YES,
+
+ /**
+ * Return MHD_NO to close the connection.
+ */
+ KP_END_NO
+};
+
+
+struct ExchangeAccount
+{
+
+ /**
+ * Payto URI of the exchange, saved from /keys.
+ */
+ struct TALER_FullPayto payto;
+
+ /**
+ * Pending bank gateway registration handle, or NULL.
+ */
+ struct TALER_BANK_RegistrationHandle *brh;
+
+ /**
+ * Context we are operating in.
+ */
+ struct KycAuthContext *kac;
+
+ /**
+ * Transfer subjects to use for the account.
+ */
+ struct TALER_BANK_TransferSubject *subjects;
+
+ /**
+ * Length of the @e subjects array.
+ */
+ unsigned int num_subjects;
+
+ /**
+ * Expiration time for the given @e ts.
+ */
+ struct GNUNET_TIME_Timestamp expiration;
+};
+
+
+/**
+ * Per-request context for POST /private/accounts/$H_WIRE/kycauth.
+ */
+struct KycAuthContext
+{
+
+ /**
+ * Kept in doubly-linked list.
+ */
+ struct KycAuthContext *next;
+
+ /**
+ * Kept in doubly-linked list.
+ */
+ struct KycAuthContext *prev;
+
+ /**
+ * MHD connection we are handling.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Handler context for this request.
+ */
+ struct TMH_HandlerContext *hc;
+
+ /**
+ * Prepared HTTP response to return, or NULL.
+ */
+ struct MHD_Response *response;
+
+ /**
+ * Exchange URL from the request body.
+ */
+ char *exchange_url;
+
+ /**
+ * Payto URI of our merchant bank account (from DB).
+ */
+ struct TALER_FullPayto payto_uri;
+
+ /**
+ * Pending /keys lookup operation with the exchange.
+ */
+ struct TMH_EXCHANGES_KeysOperation *fo;
+
+ /**
+ * Tiny amount from exchange /keys, used as the transfer amount.
+ */
+ struct TALER_Amount tiny_amount;
+
+ /**
+ * Array of exchange wire account payto URIs, saved from /keys.
+ * Length is @e exchange_accounts_len.
+ */
+ struct ExchangeAccount *exchange_accounts;
+
+ /**
+ * Number of entries in @e exchange_accounts.
+ */
+ unsigned int exchange_accounts_len;
+
+ /**
+ * Number of active registrations in @e exchange_accounts.
+ */
+ unsigned int active_registrations;
+
+ /**
+ * HTTP status code to send with @e response.
+ * UINT_MAX indicates a hard error (return MHD_NO).
+ */
+ unsigned int response_code;
+
+ /**
+ * Current processing phase.
+ */
+ enum KycAuthPhase phase;
+
+ /**
+ * #GNUNET_NO if the connection is not suspended,
+ * #GNUNET_YES if the connection is suspended,
+ * #GNUNET_SYSERR if suspended and being force-resumed during shutdown.
+ */
+ enum GNUNET_GenericReturnValue suspended;
+
+};
+
+
+/**
+ * Head of the doubly-linked list of active kycauth contexts.
+ */
+static struct KycAuthContext *kac_head;
+
+/**
+ * Tail of the doubly-linked list of active kycauth contexts.
+ */
+static struct KycAuthContext *kac_tail;
+
+
+void
+TMH_force_kac_resume (void)
+{
+ for (struct KycAuthContext *kac = kac_head;
+ NULL != kac;
+ kac = kac->next)
+ {
+ if (NULL != kac->fo)
+ {
+ TMH_EXCHANGES_keys4exchange_cancel (kac->fo);
+ kac->fo = NULL;
+ }
+ if (GNUNET_YES == kac->suspended)
+ {
+ kac->suspended = GNUNET_SYSERR;
+ MHD_resume_connection (kac->connection);
+ }
+ }
+}
+
+
+/**
+ * Resume processing of @a kac after an async operation completed.
+ *
+ * @param[in,out] kac context to resume
+ */
+static void
+kac_resume (struct KycAuthContext *kac)
+{
+ GNUNET_assert (GNUNET_YES == kac->suspended);
+ kac->suspended = GNUNET_NO;
+ MHD_resume_connection (kac->connection);
+ TALER_MHD_daemon_trigger ();
+}
+
+
+/**
+ * Clean up a kycauth context, freeing all resources.
+ *
+ * @param cls a `struct KycAuthContext` to clean up
+ */
+static void
+kac_cleanup (void *cls)
+{
+ struct KycAuthContext *kac = cls;
+
+ if (NULL != kac->fo)
+ {
+ TMH_EXCHANGES_keys4exchange_cancel (kac->fo);
+ kac->fo = NULL;
+ }
+ if (NULL != kac->response)
+ {
+ MHD_destroy_response (kac->response);
+ kac->response = NULL;
+ }
+ GNUNET_free (kac->exchange_url);
+ GNUNET_free (kac->payto_uri.full_payto);
+ for (unsigned int i = 0; i < kac->exchange_accounts_len; i++)
+ {
+ struct ExchangeAccount *acc = &kac->exchange_accounts[i];
+
+ if (NULL != acc->brh)
+ {
+ TALER_BANK_registration_cancel (acc->brh);
+ acc->brh = NULL;
+ }
+ for (unsigned int j = 0; j<acc->num_subjects; j++)
+ TALER_BANK_transfer_subject_free (&acc->subjects[j]);
+ GNUNET_free (acc->subjects);
+ GNUNET_free (acc->payto.full_payto);
+ }
+ GNUNET_free (kac->exchange_accounts);
+ GNUNET_CONTAINER_DLL_remove (kac_head,
+ kac_tail,
+ kac);
+ GNUNET_free (kac);
+}
+
+
+/**
+ * Convert a @a ts (transfer subject) to a JSON object.
+ * The resulting object has a "type" field discriminating the format,
+ * matching the JSON format returned by the bank gateway's /registration.
+ *
+ * @param acc general account details
+ * @param ts the specific transfer subject to convert
+ * @return freshly allocated JSON object, or NULL on error
+ */
+static json_t *
+transfer_subject_to_json (const struct ExchangeAccount *acc,
+ const struct TALER_BANK_TransferSubject *ts)
+{
+ struct KycAuthContext *kac = acc->kac;
+
+ switch (ts->format)
+ {
+ case TALER_BANK_SUBJECT_FORMAT_SIMPLE:
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "SIMPLE"),
+ TALER_JSON_pack_amount ("credit_amount",
+ &kac->tiny_amount),
+ GNUNET_JSON_pack_data_auto ("subject",
+ &kac->hc->instance->merchant_pub));
+ case TALER_BANK_SUBJECT_FORMAT_URI:
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "URI"),
+ TALER_JSON_pack_amount ("credit_amount",
+ &kac->tiny_amount),
+ GNUNET_JSON_pack_string ("uri",
+ ts->details.uri.uri));
+ case TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL:
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "CH_QR_BILL"),
+ TALER_JSON_pack_amount ("credit_amount",
+ &ts->details.ch_qr_bill.credit_amount),
+ GNUNET_JSON_pack_string ("qr_reference_number",
+ ts->details.ch_qr_bill.qr_reference_number));
+ }
+ GNUNET_break (0);
+ return NULL;
+}
+
+
+/**
+ * Generate the final "OK" response for the request in @a kac.
+ * Builds the wire_instructions JSON array from the saved exchange accounts
+ * and the given transfer subject.
+ *
+ * @param[in,out] request with all the details ready to build a response
+ */
+static void
+generate_ok_response (struct KycAuthContext *kac)
+{
+ json_t *arr;
+
+ arr = json_array ();
+ GNUNET_assert (NULL != arr);
+ for (unsigned int i = 0; i < kac->exchange_accounts_len; i++)
+ {
+ const struct ExchangeAccount *acc = &kac->exchange_accounts[i];
+ json_t *subj;
+ json_t *entry;
+
+ for (unsigned int j = 0; j < acc->num_subjects; j++)
+ {
+ subj = transfer_subject_to_json (acc,
+ &acc->subjects[j]);
+ GNUNET_assert (NULL != subj);
+ entry = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ &kac->tiny_amount),
+ TALER_JSON_pack_full_payto ("target_payto",
+ acc->payto),
+ GNUNET_JSON_pack_object_steal ("subject",
+ subj),
+ GNUNET_JSON_pack_timestamp ("expiration",
+ acc->expiration));
+ GNUNET_assert (NULL != entry);
+ GNUNET_assert (0 ==
+ json_array_append_new (arr,
+ entry));
+ }
+ }
+ kac->response_code = MHD_HTTP_OK;
+ kac->response = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("wire_instructions",
+ arr));
+ kac->phase = KP_RETURN_RESPONSE;
+ kac_resume (kac);
+}
+
+
+/**
+ * Callback invoked with the result of the bank gateway /registration request.
+ *
+ * @param cls our `struct KycAuthContext`
+ * @param rr response details
+ */
+static void
+registration_cb (void *cls,
+ const struct TALER_BANK_RegistrationResponse *rr)
+{
+ struct ExchangeAccount *acc = cls;
+ struct KycAuthContext *kac = acc->kac;
+
+ acc->brh = NULL;
+ kac->active_registrations--;
+ if (MHD_HTTP_OK != rr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Bank gateway registration failed with HTTP status %u\n",
+ rr->http_status);
+ kac->response_code = MHD_HTTP_BAD_GATEWAY;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_BANK_GATEWAY_UNREACHABLE,
+ "bank gateway /registration failed");
+ }
+ GNUNET_free (acc->subjects);
+ acc->num_subjects = rr->details.ok.num_subjects;
+ acc->subjects = GNUNET_new_array (acc->num_subjects,
+ struct TALER_BANK_TransferSubject);
+ for (unsigned int i = 0; i<acc->num_subjects; i++)
+ {
+ TALER_BANK_transfer_subject_copy (&acc->subjects[i],
+ &rr->details.ok.subjects[i]);
+ }
+ acc->expiration = rr->details.ok.expiration;
+ if (0 != kac->active_registrations)
+ return; /* wait for more replies */
+ generate_ok_response (kac);
+}
+
+
+/**
+ * Callback invoked with the /keys of the selected exchange.
+ *
+ * Saves tiny_amount and exchange account payto URIs from the keys, then
+ * either calls the bank gateway /registration (if wire_transfer_gateway is
+ * present) or directly constructs the SIMPLE-format response.
+ *
+ * @param cls our `struct KycAuthContext`
+ * @param keys the exchange's key material, or NULL on failure
+ * @param exchange internal exchange handle (unused)
+ */
+static void
+process_keys_cb (void *cls,
+ struct TALER_EXCHANGE_Keys *keys,
+ struct TMH_Exchange *exchange)
+{
+ struct KycAuthContext *kac = cls;
+ struct TMH_MerchantInstance *mi = kac->hc->instance;
+
+ (void) exchange;
+ kac->fo = NULL;
+ if (NULL == keys)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Could not get /keys from exchange `%s'\n",
+ kac->exchange_url);
+ kac->response_code = MHD_HTTP_BAD_GATEWAY;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_EXCHANGE_UNREACHABLE,
+ kac->exchange_url);
+ kac->phase = KP_RETURN_RESPONSE;
+ kac_resume (kac);
+ return;
+ }
+ if (! keys->tiny_amount_available)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange `%s' did not provide tiny_amount in /keys\n",
+ kac->exchange_url);
+ kac->response_code = MHD_HTTP_BAD_GATEWAY;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_POST_ACCOUNTS_EXCHANGE_TOO_OLD,
+ "exchange /keys missing tiny_amount");
+ kac->phase = KP_RETURN_RESPONSE;
+ kac_resume (kac);
+ return;
+ }
+
+ /* Save tiny_amount and exchange wire accounts from keys */
+ kac->tiny_amount = keys->tiny_amount;
+ kac->exchange_accounts_len = keys->accounts_len;
+ if (0 == keys->accounts_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange `%s' did not provide any wire accounts in /keys\n",
+ kac->exchange_url);
+ kac->response_code = MHD_HTTP_BAD_GATEWAY;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS,
+ "exchange /keys missing wire accounts");
+ kac->phase = KP_RETURN_RESPONSE;
+ kac_resume (kac);
+ return;
+ }
+
+ kac->exchange_accounts
+ = GNUNET_new_array (keys->accounts_len,
+ struct ExchangeAccount);
+ for (unsigned int i = 0; i < keys->accounts_len; i++)
+ {
+ struct ExchangeAccount *acc = &kac->exchange_accounts[i];
+ struct TALER_ReserveMapAuthorizationPrivateKeyP apk;
+
+ GNUNET_CRYPTO_eddsa_key_create (&apk.eddsa_priv);
+ acc->kac = kac;
+ acc->payto.full_payto
+ = GNUNET_strdup (keys->accounts[i].fpayto_uri.full_payto);
+ acc->subjects = GNUNET_new (struct TALER_BANK_TransferSubject);
+ acc->num_subjects = 1;
+ acc->subjects[0].format = TALER_BANK_SUBJECT_FORMAT_SIMPLE;
+ acc->expiration = GNUNET_TIME_UNIT_FOREVER_TS;
+ if (NULL == keys->accounts[i].wire_transfer_gateway)
+ continue;
+ acc->brh = TALER_BANK_registration (
+ TMH_curl_ctx,
+ keys->accounts[i].wire_transfer_gateway,
+ &kac->tiny_amount,
+ TALER_BANK_REGISTRATION_TYPE_KYC,
+ (const union TALER_AccountPublicKeyP *) &mi->merchant_pub,
+ // FIXME: turn this into an option when modernizing the BANK API...
+ &apk,
+ false, /* recurrent */
+ ®istration_cb,
+ acc);
+ if (NULL == acc->brh)
+ {
+ GNUNET_break (0);
+ kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "could not start bank gateway registration");
+ kac->phase = KP_RETURN_RESPONSE;
+ kac_resume (kac);
+ return;
+ }
+ kac->active_registrations++;
+ }
+ if (0 != kac->active_registrations)
+ return;
+ generate_ok_response (kac);
+}
+
+
+/**
+ * Process the PARSE phase: validate the URL path, parse the request body,
+ * and look up the merchant account in the database.
+ *
+ * @param[in,out] kac context to process
+ */
+static void
+phase_parse (struct KycAuthContext *kac)
+{
+ const char *h_wire_s = kac->hc->infix;
+ struct TALER_MerchantWireHashP h_wire;
+ const char *exchange_url;
+ enum GNUNET_GenericReturnValue res;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("exchange_url",
+ &exchange_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ GNUNET_assert (NULL != h_wire_s);
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (h_wire_s,
+ strlen (h_wire_s),
+ &h_wire,
+ sizeof (h_wire)))
+ {
+ GNUNET_break_op (0);
+ kac->response_code = MHD_HTTP_BAD_REQUEST;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED,
+ h_wire_s);
+ kac->phase = KP_RETURN_RESPONSE;
+ return;
+ }
+
+ res = TALER_MHD_parse_json_data (kac->connection,
+ kac->hc->request_body,
+ spec);
+ if (GNUNET_OK != res)
+ {
+ kac->phase = (GNUNET_NO == res) ? KP_END_YES : KP_END_NO;
+ return;
+ }
+
+ /* Look up the merchant account in the database */
+ {
+ struct TALER_MERCHANTDB_AccountDetails ad = { 0 };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TMH_db->select_account (TMH_db->cls,
+ kac->hc->instance->settings.id,
+ &h_wire,
+ &ad);
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select_account");
+ kac->phase = KP_RETURN_RESPONSE;
+ return;
+ }
+ if (0 == qs)
+ {
+ GNUNET_JSON_parse_free (spec);
+ kac->response_code = MHD_HTTP_NOT_FOUND;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN,
+ h_wire_s);
+ kac->phase = KP_RETURN_RESPONSE;
+ return;
+ }
+ kac->payto_uri.full_payto = GNUNET_strdup (ad.payto_uri.full_payto);
+ /* Free fields we do not need */
+ GNUNET_free (ad.payto_uri.full_payto);
+ GNUNET_free (ad.credit_facade_url);
+ GNUNET_free (ad.extra_wire_subject_metadata);
+ json_decref (ad.credit_facade_credentials);
+ }
+
+ kac->exchange_url = GNUNET_strdup (exchange_url);
+ GNUNET_JSON_parse_free (spec);
+ kac->phase = KP_CHECK_EXCHANGES;
+}
+
+
+/**
+ * Process the CHECK_EXCHANGES phase: start an async /keys fetch from
+ * the requested exchange and suspend the connection.
+ *
+ * @param[in,out] kac context to process
+ */
+static void
+phase_check_exchanges (struct KycAuthContext *kac)
+{
+ kac->fo = TMH_EXCHANGES_keys4exchange (kac->exchange_url,
+ false,
+ &process_keys_cb,
+ kac);
+ if (NULL == kac->fo)
+ {
+ GNUNET_break_op (0);
+ kac->response_code = MHD_HTTP_BAD_REQUEST;
+ kac->response = TALER_MHD_make_error (
+ TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
+ kac->exchange_url);
+ kac->phase = KP_RETURN_RESPONSE;
+ return;
+ }
+ MHD_suspend_connection (kac->connection);
+ kac->suspended = GNUNET_YES;
+}
+
+
+/**
+ * Process the RETURN_RESPONSE phase: queue the prepared response into MHD.
+ *
+ * @param[in,out] kac context to process
+ */
+static void
+phase_return_response (struct KycAuthContext *kac)
+{
+ MHD_RESULT ret;
+
+ if (UINT_MAX == kac->response_code)
+ {
+ GNUNET_break (0);
+ kac->phase = KP_END_NO;
+ return;
+ }
+ GNUNET_assert (0 != kac->response_code);
+ ret = MHD_queue_response (kac->connection,
+ kac->response_code,
+ kac->response);
+ kac->phase = (MHD_YES == ret) ? KP_END_YES : KP_END_NO;
+}
+
+
+MHD_RESULT
+TMH_private_post_accounts_H_WIRE_kycauth (
+ const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc)
+{
+ struct KycAuthContext *kac = hc->ctx;
+
+ (void) rh;
+ GNUNET_assert (NULL != hc->infix);
+ if (NULL == kac)
+ {
+ kac = GNUNET_new (struct KycAuthContext);
+ kac->connection = connection;
+ kac->hc = hc;
+ hc->ctx = kac;
+ hc->cc = &kac_cleanup;
+ GNUNET_CONTAINER_DLL_insert (kac_head,
+ kac_tail,
+ kac);
+ }
+
+ while (1)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Processing POST /private/accounts/$H_WIRE/kycauth"
+ " in phase %d\n",
+ (int) kac->phase);
+ switch (kac->phase)
+ {
+ case KP_PARSE:
+ phase_parse (kac);
+ break;
+ case KP_CHECK_EXCHANGES:
+ phase_check_exchanges (kac);
+ break;
+ case KP_RETURN_RESPONSE:
+ phase_return_response (kac);
+ break;
+ case KP_END_YES:
+ return MHD_YES;
+ case KP_END_NO:
+ return MHD_NO;
+ default:
+ GNUNET_assert (0);
+ return MHD_NO;
+ }
+
+ switch (kac->suspended)
+ {
+ case GNUNET_SYSERR:
+ /* Shutdown in progress */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Processing POST /private/accounts/$H_WIRE/kycauth"
+ " ends due to shutdown in phase %d\n",
+ (int) kac->phase);
+ return MHD_NO;
+ case GNUNET_NO:
+ /* Not suspended, continue with next phase */
+ break;
+ case GNUNET_YES:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Processing POST /private/accounts/$H_WIRE/kycauth"
+ " suspended in phase %d\n",
+ (int) kac->phase);
+ return MHD_YES;
+ }
+ }
+ GNUNET_assert (0);
+ return MHD_YES;
+}
+
+
+/* end of taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c */
diff --git a/src/backend/taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h b/src/backend/taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h
@@ -0,0 +1,54 @@
+/*
+ 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 Affero General Public License
+ along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h
+ * @brief Handle POST /private/accounts/$H_WIRE/kycauth requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_POST_PRIVATE_ACCOUNTS_H_WIRE_KYCAUTH_H
+#define TALER_MERCHANT_HTTPD_POST_PRIVATE_ACCOUNTS_H_WIRE_KYCAUTH_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Force all suspended kycauth handler connections to resume (during shutdown).
+ */
+void
+TMH_force_kac_resume (void);
+
+
+/**
+ * Handle a POST /private/accounts/$H_WIRE/kycauth request.
+ *
+ * Returns wire transfer instructions for performing a KYC authentication
+ * transfer to a specific exchange on behalf of the merchant account
+ * identified by @e H_WIRE.
+ *
+ * @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_post_accounts_H_WIRE_kycauth (
+ const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
+
+
+/* end of taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c b/src/backend/taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c
@@ -1,725 +0,0 @@
-/*
- 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 Affero General Public License
- along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c
- * @brief Handle POST /private/accounts/$H_WIRE/kycauth requests
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <taler/taler_json_lib.h>
-#include <taler/taler_mhd_lib.h>
-#include <taler/taler_bank_service.h>
-#include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h"
-#include "taler_merchantdb_plugin.h"
-
-
-/**
- * Processing phases for a kycauth POST request.
- */
-enum KycAuthPhase
-{
- /**
- * Parse the request body and URL path.
- */
- KP_PARSE = 0,
-
- /**
- * Fetch exchange /keys (and optionally register with bank gateway).
- * The connection is suspended in this phase.
- */
- KP_CHECK_EXCHANGES,
-
- /**
- * Return the prepared response to the client.
- */
- KP_RETURN_RESPONSE,
-
- /**
- * Return MHD_YES to finish handling.
- */
- KP_END_YES,
-
- /**
- * Return MHD_NO to close the connection.
- */
- KP_END_NO
-};
-
-
-struct ExchangeAccount
-{
-
- /**
- * Payto URI of the exchange, saved from /keys.
- */
- struct TALER_FullPayto payto;
-
- /**
- * Pending bank gateway registration handle, or NULL.
- */
- struct TALER_BANK_RegistrationHandle *brh;
-
- /**
- * Context we are operating in.
- */
- struct KycAuthContext *kac;
-
- /**
- * Transfer subject to use for the account.
- */
- struct TALER_BANK_TransferSubject ts;
-
- /**
- * Expiration time for the given @e ts.
- */
- struct GNUNET_TIME_Timestamp expiration;
-};
-
-
-/**
- * Per-request context for POST /private/accounts/$H_WIRE/kycauth.
- */
-struct KycAuthContext
-{
-
- /**
- * Kept in doubly-linked list.
- */
- struct KycAuthContext *next;
-
- /**
- * Kept in doubly-linked list.
- */
- struct KycAuthContext *prev;
-
- /**
- * MHD connection we are handling.
- */
- struct MHD_Connection *connection;
-
- /**
- * Handler context for this request.
- */
- struct TMH_HandlerContext *hc;
-
- /**
- * Prepared HTTP response to return, or NULL.
- */
- struct MHD_Response *response;
-
- /**
- * Exchange URL from the request body.
- */
- char *exchange_url;
-
- /**
- * Payto URI of our merchant bank account (from DB).
- */
- struct TALER_FullPayto payto_uri;
-
- /**
- * Pending /keys lookup operation with the exchange.
- */
- struct TMH_EXCHANGES_KeysOperation *fo;
-
- /**
- * Tiny amount from exchange /keys, used as the transfer amount.
- */
- struct TALER_Amount tiny_amount;
-
- /**
- * Array of exchange wire account payto URIs, saved from /keys.
- * Length is @e exchange_accounts_len.
- */
- struct ExchangeAccount *exchange_accounts;
-
- /**
- * Number of entries in @e exchange_accounts.
- */
- unsigned int exchange_accounts_len;
-
- /**
- * Number of active registrations in @e exchange_accounts.
- */
- unsigned int active_registrations;
-
- /**
- * HTTP status code to send with @e response.
- * UINT_MAX indicates a hard error (return MHD_NO).
- */
- unsigned int response_code;
-
- /**
- * Current processing phase.
- */
- enum KycAuthPhase phase;
-
- /**
- * #GNUNET_NO if the connection is not suspended,
- * #GNUNET_YES if the connection is suspended,
- * #GNUNET_SYSERR if suspended and being force-resumed during shutdown.
- */
- enum GNUNET_GenericReturnValue suspended;
-
-};
-
-
-/**
- * Head of the doubly-linked list of active kycauth contexts.
- */
-static struct KycAuthContext *kac_head;
-
-/**
- * Tail of the doubly-linked list of active kycauth contexts.
- */
-static struct KycAuthContext *kac_tail;
-
-
-void
-TMH_force_kac_resume (void)
-{
- for (struct KycAuthContext *kac = kac_head;
- NULL != kac;
- kac = kac->next)
- {
- if (NULL != kac->fo)
- {
- TMH_EXCHANGES_keys4exchange_cancel (kac->fo);
- kac->fo = NULL;
- }
- if (GNUNET_YES == kac->suspended)
- {
- kac->suspended = GNUNET_SYSERR;
- MHD_resume_connection (kac->connection);
- }
- }
-}
-
-
-/**
- * Resume processing of @a kac after an async operation completed.
- *
- * @param[in,out] kac context to resume
- */
-static void
-kac_resume (struct KycAuthContext *kac)
-{
- GNUNET_assert (GNUNET_YES == kac->suspended);
- kac->suspended = GNUNET_NO;
- MHD_resume_connection (kac->connection);
- TALER_MHD_daemon_trigger ();
-}
-
-
-/**
- * Clean up a kycauth context, freeing all resources.
- *
- * @param cls a `struct KycAuthContext` to clean up
- */
-static void
-kac_cleanup (void *cls)
-{
- struct KycAuthContext *kac = cls;
-
- if (NULL != kac->fo)
- {
- TMH_EXCHANGES_keys4exchange_cancel (kac->fo);
- kac->fo = NULL;
- }
- if (NULL != kac->response)
- {
- MHD_destroy_response (kac->response);
- kac->response = NULL;
- }
- GNUNET_free (kac->exchange_url);
- GNUNET_free (kac->payto_uri.full_payto);
- for (unsigned int i = 0; i < kac->exchange_accounts_len; i++)
- {
- struct ExchangeAccount *acc = &kac->exchange_accounts[i];
-
- if (NULL != acc->brh)
- {
- TALER_BANK_registration_cancel (acc->brh);
- acc->brh = NULL;
- }
- TALER_BANK_transfer_subject_free (&acc->ts);
- GNUNET_free (acc->payto.full_payto);
- }
- GNUNET_free (kac->exchange_accounts);
- GNUNET_CONTAINER_DLL_remove (kac_head,
- kac_tail,
- kac);
- GNUNET_free (kac);
-}
-
-
-/**
- * Convert a @a ts (transfer subject) to a JSON object.
- * The resulting object has a "type" field discriminating the format,
- * matching the JSON format returned by the bank gateway's /registration.
- *
- * @param ts the transfer subject to convert
- * @param tiny_amount amount to include for formats that require it but
- * do not embed it (e.g. URI format)
- * @return freshly allocated JSON object, or NULL on error
- */
-static json_t *
-transfer_subject_to_json (const struct ExchangeAccount *acc)
-{
- struct KycAuthContext *kac = acc->kac;
-
- switch (acc->ts.format)
- {
- case TALER_BANK_SUBJECT_FORMAT_SIMPLE:
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("type",
- "SIMPLE"),
- TALER_JSON_pack_amount ("credit_amount",
- &kac->tiny_amount),
- GNUNET_JSON_pack_data_auto ("subject",
- &kac->hc->instance->merchant_pub));
- case TALER_BANK_SUBJECT_FORMAT_URI:
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("type",
- "URI"),
- TALER_JSON_pack_amount ("credit_amount",
- &kac->tiny_amount),
- GNUNET_JSON_pack_string ("uri",
- acc->ts.details.uri.uri));
- case TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL:
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("type",
- "CH_QR_BILL"),
- TALER_JSON_pack_amount ("credit_amount",
- &acc->ts.details.ch_qr_bill.credit_amount),
- GNUNET_JSON_pack_string ("qr_reference_number",
- acc->ts.details.ch_qr_bill.qr_reference_number));
- }
- GNUNET_break (0);
- return NULL;
-}
-
-
-/**
- * Generate the final "OK" response for the request in @a kac.
- * Builds the wire_instructions JSON array from the saved exchange accounts
- * and the given transfer subject.
- *
- * @param[in,out] request with all the details ready to build a response
- */
-static void
-generate_ok_response (struct KycAuthContext *kac)
-{
- json_t *arr;
-
- arr = json_array ();
- GNUNET_assert (NULL != arr);
- for (unsigned int i = 0; i < kac->exchange_accounts_len; i++)
- {
- const struct ExchangeAccount *acc = &kac->exchange_accounts[i];
- json_t *subj;
- json_t *entry;
-
- subj = transfer_subject_to_json (acc);
- GNUNET_assert (NULL != subj);
- entry = GNUNET_JSON_PACK (
- TALER_JSON_pack_amount ("amount",
- &kac->tiny_amount),
- TALER_JSON_pack_full_payto ("target_payto",
- acc->payto),
- GNUNET_JSON_pack_object_steal ("subject",
- subj),
- GNUNET_JSON_pack_timestamp ("expiration",
- acc->expiration));
- GNUNET_assert (NULL != entry);
- GNUNET_assert (0 ==
- json_array_append_new (arr,
- entry));
- }
- kac->response_code = MHD_HTTP_OK;
- kac->response = TALER_MHD_MAKE_JSON_PACK (
- GNUNET_JSON_pack_array_steal ("wire_instructions",
- arr));
- kac->phase = KP_RETURN_RESPONSE;
- kac_resume (kac);
-}
-
-
-/**
- * Callback invoked with the result of the bank gateway /registration request.
- *
- * @param cls our `struct KycAuthContext`
- * @param rr response details
- */
-static void
-registration_cb (void *cls,
- const struct TALER_BANK_RegistrationResponse *rr)
-{
- struct ExchangeAccount *acc = cls;
- struct KycAuthContext *kac = acc->kac;
-
- acc->brh = NULL;
- kac->active_registrations--;
- if (MHD_HTTP_OK != rr->http_status)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Bank gateway registration failed with HTTP status %u\n",
- rr->http_status);
- kac->response_code = MHD_HTTP_BAD_GATEWAY;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_BANK_GATEWAY_UNREACHABLE,
- "bank gateway /registration failed");
- }
- TALER_BANK_transfer_subject_copy (&acc->ts,
- &rr->details.ok.subject);
- acc->expiration = rr->details.ok.expiration;
- if (0 != kac->active_registrations)
- return; /* wait for more replies */
- generate_ok_response (kac);
-}
-
-
-/**
- * Callback invoked with the /keys of the selected exchange.
- *
- * Saves tiny_amount and exchange account payto URIs from the keys, then
- * either calls the bank gateway /registration (if wire_transfer_gateway is
- * present) or directly constructs the SIMPLE-format response.
- *
- * @param cls our `struct KycAuthContext`
- * @param keys the exchange's key material, or NULL on failure
- * @param exchange internal exchange handle (unused)
- */
-static void
-process_keys_cb (void *cls,
- struct TALER_EXCHANGE_Keys *keys,
- struct TMH_Exchange *exchange)
-{
- struct KycAuthContext *kac = cls;
- struct TMH_MerchantInstance *mi = kac->hc->instance;
-
- (void) exchange;
- kac->fo = NULL;
- if (NULL == keys)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Could not get /keys from exchange `%s'\n",
- kac->exchange_url);
- kac->response_code = MHD_HTTP_BAD_GATEWAY;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_POST_ACCOUNTS_KYCAUTH_EXCHANGE_UNREACHABLE,
- kac->exchange_url);
- kac->phase = KP_RETURN_RESPONSE;
- kac_resume (kac);
- return;
- }
- if (! keys->tiny_amount_available)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Exchange `%s' did not provide tiny_amount in /keys\n",
- kac->exchange_url);
- kac->response_code = MHD_HTTP_BAD_GATEWAY;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_POST_ACCOUNTS_EXCHANGE_TOO_OLD,
- "exchange /keys missing tiny_amount");
- kac->phase = KP_RETURN_RESPONSE;
- kac_resume (kac);
- return;
- }
-
- /* Save tiny_amount and exchange wire accounts from keys */
- kac->tiny_amount = keys->tiny_amount;
- kac->exchange_accounts_len = keys->accounts_len;
- if (0 == keys->accounts_len)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Exchange `%s' did not provide any wire accounts in /keys\n",
- kac->exchange_url);
- kac->response_code = MHD_HTTP_BAD_GATEWAY;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS,
- "exchange /keys missing wire accounts");
- kac->phase = KP_RETURN_RESPONSE;
- kac_resume (kac);
- return;
- }
-
- kac->exchange_accounts
- = GNUNET_new_array (keys->accounts_len,
- struct ExchangeAccount);
- for (unsigned int i = 0; i < keys->accounts_len; i++)
- {
- struct ExchangeAccount *acc = &kac->exchange_accounts[i];
- struct TALER_ReserveMapAuthorizationPrivateKeyP apk;
-
- GNUNET_CRYPTO_eddsa_key_create (&apk.eddsa_priv);
- acc->kac = kac;
- acc->payto.full_payto
- = GNUNET_strdup (keys->accounts[i].fpayto_uri.full_payto);
- acc->ts.format = TALER_BANK_SUBJECT_FORMAT_SIMPLE;
- acc->expiration = GNUNET_TIME_UNIT_FOREVER_TS;
- if (NULL == keys->accounts[i].wire_transfer_gateway)
- continue;
- acc->brh = TALER_BANK_registration (
- TMH_curl_ctx,
- keys->accounts[i].wire_transfer_gateway,
- TALER_BANK_SUBJECT_FORMAT_SIMPLE,
- &kac->tiny_amount,
- TALER_BANK_REGISTRATION_TYPE_KYC,
- (const union TALER_AccountPublicKeyP *) &mi->merchant_pub,
- // FIXME: turn this into an option when modernizing the BANK API...
- &apk,
- false, /* recurrent */
- ®istration_cb,
- acc);
- if (NULL == acc->brh)
- {
- GNUNET_break (0);
- kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- kac->response = TALER_MHD_make_error (
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "could not start bank gateway registration");
- kac->phase = KP_RETURN_RESPONSE;
- kac_resume (kac);
- return;
- }
- kac->active_registrations++;
- }
- if (0 != kac->active_registrations)
- return;
- generate_ok_response (kac);
-}
-
-
-/**
- * Process the PARSE phase: validate the URL path, parse the request body,
- * and look up the merchant account in the database.
- *
- * @param[in,out] kac context to process
- */
-static void
-phase_parse (struct KycAuthContext *kac)
-{
- const char *h_wire_s = kac->hc->infix;
- struct TALER_MerchantWireHashP h_wire;
- const char *exchange_url;
- enum GNUNET_GenericReturnValue res;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("exchange_url",
- &exchange_url),
- GNUNET_JSON_spec_end ()
- };
-
- GNUNET_assert (NULL != h_wire_s);
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (h_wire_s,
- strlen (h_wire_s),
- &h_wire,
- sizeof (h_wire)))
- {
- GNUNET_break_op (0);
- kac->response_code = MHD_HTTP_BAD_REQUEST;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED,
- h_wire_s);
- kac->phase = KP_RETURN_RESPONSE;
- return;
- }
-
- res = TALER_MHD_parse_json_data (kac->connection,
- kac->hc->request_body,
- spec);
- if (GNUNET_OK != res)
- {
- kac->phase = (GNUNET_NO == res) ? KP_END_YES : KP_END_NO;
- return;
- }
-
- /* Look up the merchant account in the database */
- {
- struct TALER_MERCHANTDB_AccountDetails ad = { 0 };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TMH_db->select_account (TMH_db->cls,
- kac->hc->instance->settings.id,
- &h_wire,
- &ad);
- if (0 > qs)
- {
- GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- kac->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- kac->response = TALER_MHD_make_error (
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "select_account");
- kac->phase = KP_RETURN_RESPONSE;
- return;
- }
- if (0 == qs)
- {
- GNUNET_JSON_parse_free (spec);
- kac->response_code = MHD_HTTP_NOT_FOUND;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN,
- h_wire_s);
- kac->phase = KP_RETURN_RESPONSE;
- return;
- }
- kac->payto_uri.full_payto = GNUNET_strdup (ad.payto_uri.full_payto);
- /* Free fields we do not need */
- GNUNET_free (ad.payto_uri.full_payto);
- GNUNET_free (ad.credit_facade_url);
- GNUNET_free (ad.extra_wire_subject_metadata);
- json_decref (ad.credit_facade_credentials);
- }
-
- kac->exchange_url = GNUNET_strdup (exchange_url);
- GNUNET_JSON_parse_free (spec);
- kac->phase = KP_CHECK_EXCHANGES;
-}
-
-
-/**
- * Process the CHECK_EXCHANGES phase: start an async /keys fetch from
- * the requested exchange and suspend the connection.
- *
- * @param[in,out] kac context to process
- */
-static void
-phase_check_exchanges (struct KycAuthContext *kac)
-{
- kac->fo = TMH_EXCHANGES_keys4exchange (kac->exchange_url,
- false,
- &process_keys_cb,
- kac);
- if (NULL == kac->fo)
- {
- GNUNET_break_op (0);
- kac->response_code = MHD_HTTP_BAD_REQUEST;
- kac->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
- kac->exchange_url);
- kac->phase = KP_RETURN_RESPONSE;
- return;
- }
- MHD_suspend_connection (kac->connection);
- kac->suspended = GNUNET_YES;
-}
-
-
-/**
- * Process the RETURN_RESPONSE phase: queue the prepared response into MHD.
- *
- * @param[in,out] kac context to process
- */
-static void
-phase_return_response (struct KycAuthContext *kac)
-{
- MHD_RESULT ret;
-
- if (UINT_MAX == kac->response_code)
- {
- GNUNET_break (0);
- kac->phase = KP_END_NO;
- return;
- }
- GNUNET_assert (0 != kac->response_code);
- ret = MHD_queue_response (kac->connection,
- kac->response_code,
- kac->response);
- kac->phase = (MHD_YES == ret) ? KP_END_YES : KP_END_NO;
-}
-
-
-MHD_RESULT
-TMH_private_post_accounts_H_WIRE_kycauth (
- const struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc)
-{
- struct KycAuthContext *kac = hc->ctx;
-
- (void) rh;
- GNUNET_assert (NULL != hc->infix);
- if (NULL == kac)
- {
- kac = GNUNET_new (struct KycAuthContext);
- kac->connection = connection;
- kac->hc = hc;
- hc->ctx = kac;
- hc->cc = &kac_cleanup;
- GNUNET_CONTAINER_DLL_insert (kac_head,
- kac_tail,
- kac);
- }
-
- while (1)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Processing POST /private/accounts/$H_WIRE/kycauth"
- " in phase %d\n",
- (int) kac->phase);
- switch (kac->phase)
- {
- case KP_PARSE:
- phase_parse (kac);
- break;
- case KP_CHECK_EXCHANGES:
- phase_check_exchanges (kac);
- break;
- case KP_RETURN_RESPONSE:
- phase_return_response (kac);
- break;
- case KP_END_YES:
- return MHD_YES;
- case KP_END_NO:
- return MHD_NO;
- default:
- GNUNET_assert (0);
- return MHD_NO;
- }
-
- switch (kac->suspended)
- {
- case GNUNET_SYSERR:
- /* Shutdown in progress */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Processing POST /private/accounts/$H_WIRE/kycauth"
- " ends due to shutdown in phase %d\n",
- (int) kac->phase);
- return MHD_NO;
- case GNUNET_NO:
- /* Not suspended, continue with next phase */
- break;
- case GNUNET_YES:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Processing POST /private/accounts/$H_WIRE/kycauth"
- " suspended in phase %d\n",
- (int) kac->phase);
- return MHD_YES;
- }
- }
- GNUNET_assert (0);
- return MHD_YES;
-}
-
-
-/* end of taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h b/src/backend/taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h
@@ -1,54 +0,0 @@
-/*
- 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 Affero General Public License
- along with TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h
- * @brief Handle POST /private/accounts/$H_WIRE/kycauth requests
- * @author Christian Grothoff
- */
-#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_ACCOUNTS_H_WIRE_KYCAUTH_H
-#define TALER_MERCHANT_HTTPD_PRIVATE_POST_ACCOUNTS_H_WIRE_KYCAUTH_H
-
-#include "taler-merchant-httpd.h"
-
-
-/**
- * Force all suspended kycauth handler connections to resume (during shutdown).
- */
-void
-TMH_force_kac_resume (void);
-
-
-/**
- * Handle a POST /private/accounts/$H_WIRE/kycauth request.
- *
- * Returns wire transfer instructions for performing a KYC authentication
- * transfer to a specific exchange on behalf of the merchant account
- * identified by @e H_WIRE.
- *
- * @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_post_accounts_H_WIRE_kycauth (
- const struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc);
-
-
-/* end of taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h */
-#endif