merchant

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

commit 287d4ce69a87b92f7b4ca4e4e668cb97c641b236
parent 57b697a4d6b110eb7afb89e0a25873c3aec06e72
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  2 Mar 2026 21:37:08 +0100

protocol breaking change for b11169

Diffstat:
Msrc/backend/Makefile.am | 2++
Msrc/backend/taler-merchant-httpd.c | 3++-
Msrc/backend/taler-merchant-httpd_dispatcher.c | 12++++++++++++
Asrc/backend/taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.c | 725+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backend/taler-merchant-httpd_private-post-accounts-H_WIRE-kycauth.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 795 insertions(+), 1 deletion(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -229,6 +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-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" /** * Backlog for listen operation on unix-domain sockets. @@ -221,6 +221,7 @@ do_shutdown (void *cls) (void) cls; TALER_MHD_daemons_halt (); TMH_handler_statistic_report_transactions_cleanup (); + TMH_force_kac_resume (); TMH_force_orders_resume (); TMH_force_get_sessions_ID_resume (); TMH_force_get_orders_resume_typst (); diff --git a/src/backend/taler-merchant-httpd_dispatcher.c b/src/backend/taler-merchant-httpd_dispatcher.c @@ -93,6 +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-challenge-ID.h" #include "taler-merchant-httpd_post-challenge-ID-confirm.h" #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h" @@ -805,6 +806,17 @@ determine_handler_group (const char **urlp, /* allow account details of up to 8 kb, that should be plenty */ .max_upload = 1024 * 8 }, + /* POST /accounts/H_WIRE/kycauth: */ + { + .url_prefix = "/accounts/", + .url_suffix = "kycauth", + .method = MHD_HTTP_METHOD_POST, + .have_id_segment = true, + .permission = "accounts-read", + .handler = &TMH_private_post_account, + /* allow exchange URL up to 4 kb, that should be plenty */ + .max_upload = 1024 * 4 + }, /* PATCH /accounts/$H_WIRE: */ { .url_prefix = "/accounts/", 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 @@ -0,0 +1,725 @@ +/* + 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 */ + &registration_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 @@ -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_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