merchant

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

commit b863e9ef6e2574ad3dfed5d3869230bbdb03e6f2
parent f803e34444cd56bfe99bd4f6075e9980e1b85ff1
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 26 Mar 2026 22:59:52 +0100

misc minor API fixes

Diffstat:
Msrc/backend/Makefile.am | 2++
Msrc/backend/taler-merchant-httpd.c | 2+-
Asrc/backend/taler-merchant-httpd_delete-private-token.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backend/taler-merchant-httpd_delete-private-token.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/backend/taler-merchant-httpd_delete-private-tokenfamilies-TOKEN_FAMILY_SLUG.c | 24++++++++++++++++--------
Msrc/backend/taler-merchant-httpd_delete-private-tokens-SERIAL.c | 79-------------------------------------------------------------------------------
Msrc/backend/taler-merchant-httpd_delete-private-tokens-SERIAL.h | 20++------------------
Msrc/backend/taler-merchant-httpd_dispatcher.c | 1+
Msrc/backend/taler-merchant-httpd_get-private-tokens.c | 13++++++++++++-
Msrc/backend/taler-merchant-httpd_post-management-instances-INSTANCE-auth.c | 21++++++++++-----------
Msrc/backend/taler-merchant-httpd_post-orders-ORDER_ID-refund.c | 13++++++++-----
Msrc/backend/taler-merchant-httpd_post-orders-ORDER_ID-unclaim.c | 4++--
Msrc/backend/taler-merchant-httpd_post-private-accounts.c | 2++
Msrc/include/taler/taler_merchantdb_plugin.h | 2++
Msrc/lib/merchant_api_post-orders-ORDER_ID-abort.c | 8++++----
15 files changed, 214 insertions(+), 129 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am @@ -111,6 +111,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_delete-private-units-UNIT.h \ taler-merchant-httpd_delete-management-instances-INSTANCE.c \ taler-merchant-httpd_delete-management-instances-INSTANCE.h \ + taler-merchant-httpd_delete-private-token.c \ + taler-merchant-httpd_delete-private-token.h \ taler-merchant-httpd_delete-private-tokens-SERIAL.c \ taler-merchant-httpd_delete-private-tokens-SERIAL.h \ taler-merchant-httpd_delete-private-products-PRODUCT_ID.c \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -562,7 +562,7 @@ identify_instance (struct TMH_HandlerContext *hc, const char *rslash = strchr (rstart, '/'); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Client used deprecated '/instances/default/' path. Redirecting to modern path\n"); + "Client used deprecated '/instances/admin/' path. Redirecting to modern path\n"); response = MHD_create_response_from_buffer (0, diff --git a/src/backend/taler-merchant-httpd_delete-private-token.c b/src/backend/taler-merchant-httpd_delete-private-token.c @@ -0,0 +1,107 @@ +/* + This file is part of GNU Taler + (C) 2023 Taler Systems SA + + GNU 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. + + GNU 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_delete-private-token.c + * @brief implementing DELETE /instances/$ID/token request handling + * @author Christian Grothoff + */ +#include "taler/platform.h" +#include "taler-merchant-httpd_delete-private-token.h" +#include "taler-merchant-httpd_helper.h" +#include <taler/taler_json_lib.h> + + +MHD_RESULT +TMH_private_delete_instances_ID_token (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + const char *bearer = "Bearer "; + struct TMH_MerchantInstance *mi = hc->instance; + const char *tok; + struct TALER_MERCHANTDB_LoginTokenP btoken; + enum GNUNET_DB_QueryStatus qs; + + tok = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_AUTHORIZATION); + /* This was presumably checked before... */ + if (0 != + strncmp (tok, + bearer, + strlen (bearer))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_ec (connection, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "login token (in 'Authorization' header)"); + } + tok += strlen (bearer); + while (' ' == *tok) + tok++; + if (0 != strncasecmp (tok, + RFC_8959_PREFIX, + strlen (RFC_8959_PREFIX))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_ec (connection, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "login token (in 'Authorization' header)"); + } + tok += strlen (RFC_8959_PREFIX); + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (tok, + strlen (tok), + &btoken, + sizeof (btoken))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_ec (connection, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "login token (in 'Authorization' header)"); + } + qs = TMH_db->delete_login_token (TMH_db->cls, + mi->settings.id, + &btoken); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_ec (connection, + TALER_EC_GENERIC_DB_STORE_FAILED, + "delete_login_token"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* No 404, as the login token must have existed + when we got the request as it was accepted as + valid. So we can only get here due to concurrent + modification, and then the client should still + simply see the success. Hence, fall-through! */ + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + } + GNUNET_break (0); + return MHD_NO; +} diff --git a/src/backend/taler-merchant-httpd_delete-private-token.h b/src/backend/taler-merchant-httpd_delete-private-token.h @@ -0,0 +1,45 @@ +/* + This file is part of GNU Taler + (C) 2023 Taler Systems SA + + GNU 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. + + GNU 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_delete-private-token.h + * @brief implements DELETE /instances/$ID/token request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_INSTANCES_ID_TOKEN_H +#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_INSTANCES_ID_TOKEN_H +#include "taler-merchant-httpd.h" + + +/** + * Delete login token for an instance. + * + * @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_delete_instances_ID_token ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + + +#endif diff --git a/src/backend/taler-merchant-httpd_delete-private-tokenfamilies-TOKEN_FAMILY_SLUG.c b/src/backend/taler-merchant-httpd_delete-private-tokenfamilies-TOKEN_FAMILY_SLUG.c @@ -49,17 +49,25 @@ TMH_private_delete_token_families_SLUG (const struct TMH_RequestHandler *rh, switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "delete_token_family"); + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "delete_token_family"); case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "delete_token_family (soft)"); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "delete_token_family (soft)"); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_TOKEN_FAMILY_UNKNOWN, + hc->infix); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: return TALER_MHD_reply_static (connection, MHD_HTTP_NO_CONTENT, diff --git a/src/backend/taler-merchant-httpd_delete-private-tokens-SERIAL.c b/src/backend/taler-merchant-httpd_delete-private-tokens-SERIAL.c @@ -83,83 +83,4 @@ TMH_private_delete_instances_ID_token_SERIAL ( } -MHD_RESULT -TMH_private_delete_instances_ID_token (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) -{ - const char *bearer = "Bearer "; - struct TMH_MerchantInstance *mi = hc->instance; - const char *tok; - struct TALER_MERCHANTDB_LoginTokenP btoken; - enum GNUNET_DB_QueryStatus qs; - - tok = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_AUTHORIZATION); - /* This was presumably checked before... */ - if (0 != - strncmp (tok, - bearer, - strlen (bearer))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_ec (connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "login token (in 'Authorization' header)"); - } - tok += strlen (bearer); - while (' ' == *tok) - tok++; - if (0 != strncasecmp (tok, - RFC_8959_PREFIX, - strlen (RFC_8959_PREFIX))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_ec (connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "login token (in 'Authorization' header)"); - } - tok += strlen (RFC_8959_PREFIX); - - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (tok, - strlen (tok), - &btoken, - sizeof (btoken))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_ec (connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "login token (in 'Authorization' header)"); - } - qs = TMH_db->delete_login_token (TMH_db->cls, - mi->settings.id, - &btoken); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_ec (connection, - TALER_EC_GENERIC_DB_STORE_FAILED, - "delete_login_token"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* No 404, as the login token must have existed - when we got the request as it was accepted as - valid. So we can only get here due to concurrent - modification, and then the client should still - simply see the success. Hence, fall-through */ - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - return TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - } - GNUNET_break (0); - return MHD_NO; -} - - /* end of taler-merchant-httpd_delete-private-tokens-SERIAL.c */ diff --git a/src/backend/taler-merchant-httpd_delete-private-tokens-SERIAL.h b/src/backend/taler-merchant-httpd_delete-private-tokens-SERIAL.h @@ -22,8 +22,8 @@ * @brief implements DELETE /instances/$ID/token request handling * @author Christian Grothoff */ -#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_INSTANCES_ID_TOKEN_H -#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_INSTANCES_ID_TOKEN_H +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_INSTANCES_ID_TOKENS_SERIAL_H +#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_INSTANCES_ID_TOKENS_SERIAL_H #include "taler-merchant-httpd.h" /** @@ -40,20 +40,4 @@ TMH_private_delete_instances_ID_token_SERIAL ( struct MHD_Connection *connection, struct TMH_HandlerContext *hc); - -/** - * Delete login token for an instance. - * - * @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_delete_instances_ID_token ( - const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc); - - #endif diff --git a/src/backend/taler-merchant-httpd_dispatcher.c b/src/backend/taler-merchant-httpd_dispatcher.c @@ -31,6 +31,7 @@ #include "taler-merchant-httpd_delete-private-categories-CATEGORY_ID.h" #include "taler-merchant-httpd_delete-private-units-UNIT.h" #include "taler-merchant-httpd_delete-management-instances-INSTANCE.h" +#include "taler-merchant-httpd_delete-private-token.h" #include "taler-merchant-httpd_delete-private-tokens-SERIAL.h" #include "taler-merchant-httpd_delete-private-products-PRODUCT_ID.h" #include "taler-merchant-httpd_delete-private-orders-ORDER_ID.h" diff --git a/src/backend/taler-merchant-httpd_get-private-tokens.c b/src/backend/taler-merchant-httpd_get-private-tokens.c @@ -99,14 +99,25 @@ TMH_private_get_instances_ID_tokens (const struct TMH_RequestHandler *rh, limit, &add_token, ta); - if (0 > qs) + switch (qs) { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); json_decref (ta); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + json_decref (ta); + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + default: + break; } return TALER_MHD_REPLY_JSON_PACK (connection, MHD_HTTP_OK, diff --git a/src/backend/taler-merchant-httpd_post-management-instances-INSTANCE-auth.c b/src/backend/taler-merchant-httpd_post-management-instances-INSTANCE-auth.c @@ -293,6 +293,16 @@ TMH_public_post_instances_ID_auth (const struct TMH_RequestHandler *rh, { struct TMH_MerchantInstance *mi = hc->instance; + if (0 == strcmp ("admin", + mi->settings.id)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_MERCHANT_GENERIC_MFA_MISSING, + "not allowed for 'admin' account"); + } return post_instances_ID_auth (mi, connection, hc, @@ -310,17 +320,6 @@ TMH_private_post_instances_default_ID_auth ( struct TMH_MerchantInstance *mi; MHD_RESULT ret; - if ( (NULL == hc->infix) || - (0 == strcmp ("admin", - hc->infix)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_MERCHANT_GENERIC_MFA_MISSING, - "not allowed for 'admin' account"); - } mi = TMH_lookup_instance (hc->infix); if (NULL == mi) { diff --git a/src/backend/taler-merchant-httpd_post-orders-ORDER_ID-refund.c b/src/backend/taler-merchant-httpd_post-orders-ORDER_ID-refund.c @@ -688,11 +688,14 @@ TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh, } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "no coins found that could be refunded"); + /* We have a contract, which means the order was paid, but there + are no coins (maybe paid with a token?), and thus we cannot + do any refunds */ + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); } /* Now launch exchange interactions, unless we already have the diff --git a/src/backend/taler-merchant-httpd_post-orders-ORDER_ID-unclaim.c b/src/backend/taler-merchant-httpd_post-orders-ORDER_ID-unclaim.c @@ -74,8 +74,8 @@ TMH_post_orders_ID_unclaim (const struct TMH_RequestHandler *rh, GNUNET_break_op (0); return TALER_MHD_reply_with_error ( connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, + MHD_HTTP_FORBIDDEN, + TALER_EC_MERCHANT_POST_ORDERS_UNCLAIM_SIGNATURE_INVALID, "unclaim_sig"); } TMH_db->preflight (TMH_db->cls); diff --git a/src/backend/taler-merchant-httpd_post-private-accounts.c b/src/backend/taler-merchant-httpd_post-private-accounts.c @@ -403,6 +403,7 @@ TMH_private_post_account (const struct TMH_RequestHandler *rh, break; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); TMH_wire_method_free (wm); return TALER_MHD_reply_with_error ( connection, @@ -413,6 +414,7 @@ TMH_private_post_account (const struct TMH_RequestHandler *rh, continue; case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); TMH_wire_method_free (wm); return TALER_MHD_reply_with_error ( connection, diff --git a/src/include/taler/taler_merchantdb_plugin.h b/src/include/taler/taler_merchantdb_plugin.h @@ -34,6 +34,8 @@ #ifdef HAVE_DONAU_DONAU_SERVICE_H #include <donau/donau_service.h> #include "taler_merchant_donau.h" +#else +struct DONAU_CharityPublicKeyP charity_pub_key; #endif /** diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-abort.c b/src/lib/merchant_api_post-orders-ORDER_ID-abort.c @@ -325,10 +325,10 @@ TALER_MERCHANT_post_orders_abort_create ( poah->num_coins = num_coins; poah->coins = GNUNET_new_array (num_coins, struct TALER_MERCHANT_PostOrdersAbortCoin); - GNUNET_memcpy (poah->coins, - coins, - num_coins * sizeof (struct TALER_MERCHANT_PostOrdersAbortCoin)) - ; + GNUNET_memcpy ( + poah->coins, + coins, + num_coins * sizeof (struct TALER_MERCHANT_PostOrdersAbortCoin)); return poah; }