merchant

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

commit 86cab216c4a68dfc5be8dfaea7e1c81d9c110201
parent af85f31619b4be1eed5f0575f13d26d81db37e1f
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Wed, 23 Jul 2025 18:49:56 +0200

update donaukeyupdate to match exchangekeyupdate

Diffstat:
Msrc/backend/taler-merchant-donaukeyupdate.c | 135++++++++++++++++++++++++++++++-------------------------------------------------
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 5+----
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 5++---
Msrc/backenddb/merchant-0022.sql | 3+++
Msrc/backenddb/pg_lookup_donau_keys.c | 6+++++-
Msrc/backenddb/pg_lookup_donau_keys.h | 1+
Msrc/backenddb/pg_upsert_donau_keys.c | 13++++++++-----
Msrc/backenddb/pg_upsert_donau_keys.h | 3++-
Msrc/include/taler_merchantdb_plugin.h | 4+++-
9 files changed, 76 insertions(+), 99 deletions(-)

diff --git a/src/backend/taler-merchant-donaukeyupdate.c b/src/backend/taler-merchant-donaukeyupdate.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024 Taler Systems SA + Copyright (C) 2024, 2025 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 @@ -19,6 +19,7 @@ * @author Bohdan Potuzhnyi */ #include "platform.h" +#include "microhttpd.h" #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> @@ -107,6 +108,12 @@ struct Donau * If true, the instance is temporarily paused from making further requests due to reaching a limit. */ bool limited; + + /** + * Are we force-retrying a /keys download because some keys + * were missing? + */ + bool force_retry; }; @@ -220,14 +227,19 @@ end_inquiry (void) * Update Donau keys in the database. * * @param keys Donau keys to persist + * @param first_retry earliest we may retry fetching the keys * @return transaction status */ static enum GNUNET_DB_QueryStatus -insert_donau_keys_data (const struct DONAU_Keys *keys) +insert_donau_keys_data (const struct DONAU_Keys *keys, + struct GNUNET_TIME_Absolute first_retry) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Inserting Donau keys into the database %s\n", keys->donau_url); - return db_plugin->upsert_donau_keys (db_plugin->cls, keys); + "Inserting Donau keys into the database %s\n", + keys->donau_url); + return db_plugin->upsert_donau_keys (db_plugin->cls, + keys, + first_retry); } @@ -235,10 +247,12 @@ insert_donau_keys_data (const struct DONAU_Keys *keys) * Store Donau keys in the database and handle retries. * * @param keys the keys to store + * @param first_retry earliest time we may retry fetching the keys * @return true on success */ static bool -store_donau_keys (struct DONAU_Keys *keys) +store_donau_keys (struct DONAU_Keys *keys, + struct GNUNET_TIME_Absolute first_retry) { enum GNUNET_DB_QueryStatus qs; @@ -254,8 +268,9 @@ store_donau_keys (struct DONAU_Keys *keys) return false; } - qs = insert_donau_keys_data (keys); - if (qs < 0) + qs = insert_donau_keys_data (keys, + first_retry); + if (0 > qs) { db_plugin->rollback (db_plugin->cls); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -268,7 +283,7 @@ store_donau_keys (struct DONAU_Keys *keys) } qs = db_plugin->commit (db_plugin->cls); - if (qs < 0) + if (0 > qs) { db_plugin->rollback (db_plugin->cls); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -278,6 +293,7 @@ store_donau_keys (struct DONAU_Keys *keys) GNUNET_break (0); return false; } + break; } if (qs < 0) { @@ -303,6 +319,7 @@ donau_cert_cb ( { struct Donau *d = cls; struct GNUNET_TIME_Absolute n; + struct GNUNET_TIME_Absolute first_retry; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting donau cert with object \n"); @@ -311,23 +328,27 @@ donau_cert_cb ( switch (kr->hr.http_status) { case MHD_HTTP_OK: - if (! store_donau_keys (keys)) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got new keys for %s, updating database\n", + d->donau_url); + first_retry = GNUNET_TIME_relative_to_absolute (DONAU_MAXFREQ); + if (! store_donau_keys (keys, + first_retry)) + { + GNUNET_break (0); + DONAU_keys_decref (keys); break; + } - d->keys = DONAU_keys_incref (keys); + d->keys = keys; /* Reset back-off */ d->retry_delay = DONAU_MAXFREQ; /* limit retry */ - d->first_retry = - GNUNET_TIME_relative_to_absolute ( - DONAU_MAXFREQ); + d->first_retry = first_retry; - // Fixme: hardcoded retry delay + /* Might be good to reference some key_data_expiration and not first sign_key*/ n = GNUNET_TIME_absolute_max (d->first_retry, - GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_MONTHS, - 3))); + keys->sign_keys[0].expire_sign.abs_time); if (NULL != d->retry_task) GNUNET_SCHEDULER_cancel (d->retry_task); @@ -338,6 +359,7 @@ donau_cert_cb ( end_inquiry (); return; default: + GNUNET_break (NULL == keys); break; } @@ -380,11 +402,15 @@ download_keys (void *cls) } d->retry_delay = GNUNET_TIME_STD_BACKOFF (d->retry_delay); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Downloading keys from %s (%s)\n", + d->donau_url, + d->force_retry ? "forced" : "regular"); d->conn = DONAU_get_keys (ctx, d->donau_url, &donau_cert_cb, d); - + d->force_retry = false; if (NULL != d->conn) { active_inquiries++; @@ -465,22 +491,15 @@ force_donau_keys (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Donau instance `%s' not found. Creating new instance.\n", url); - // Create a new Donau instance dynamically d = GNUNET_new (struct Donau); d->donau_url = GNUNET_strdup (url); - d->keys = NULL; - d->conn = NULL; - d->retry_task = NULL; d->retry_delay = DONAU_MAXFREQ; d->first_retry = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_ZERO); - d->limited = false; - // Insert the new instance into the linked list GNUNET_CONTAINER_DLL_insert (d_head, d_tail, d); - - // Schedule a keys download for the newly created instance download_keys (d); } + if (NULL != d->conn) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -497,7 +516,7 @@ force_donau_keys (void *cls, true)); if (NULL != d->retry_task) GNUNET_SCHEDULER_cancel (d->retry_task); - + d->force_retry = true; d->retry_task = GNUNET_SCHEDULER_add_at (d->first_retry, &download_keys, @@ -518,8 +537,6 @@ accept_donau (void *cls, { char *url; char *currency; - // char *mks; - if (0 != strncasecmp (section, "merchant-donau-", @@ -576,57 +593,21 @@ accept_donau (void *cls, GNUNET_SCHEDULER_shutdown (); return; } -// if (GNUNET_OK != -// GNUNET_CONFIGURATION_get_value_string (cfg, -// section, -// "MASTER_KEY", -// &mks)) -// { -// GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, -// section, -// "MASTER_KEY"); -// global_ret = EXIT_NOTCONFIGURED; -// GNUNET_SCHEDULER_shutdown (); -// GNUNET_free (currency); -// GNUNET_free (url); -// return; -// } - - { struct Donau *d; - d = GNUNET_new (struct Donau); d->donau_url = url; GNUNET_CONTAINER_DLL_insert (d_head, d_tail, d); -// if(GNUNET_OK != -// GNUNET_CRYPTO_eddsa_public_key_from_string ( -// mks, -// strlen(mks), -// &d->master_pub.eddsa_pub)) -// { -// GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, -// section, -// "MASTER_KEY", -// "malformed EdDSA key"); -// global_ret = EXIT_NOTCONFIGURED; -// GNUNET_SCHEDULER_shutdown (); -// GNUNET_free (mks); -// return; -// } -// GNUNET_free (mks); - { enum GNUNET_DB_QueryStatus qs; struct DONAU_Keys *keys; - qs = db_plugin->lookup_donau_keys (db_plugin->cls, url, + &d->first_retry, &keys); - - if (qs < 0) + if (0 > qs) { GNUNET_break (0); global_ret = EXIT_FAILURE; @@ -645,32 +626,18 @@ accept_donau (void *cls, DONAU_keys_decref (keys); keys = NULL; } -// if ( (NULL != keys) && -// (0 != GNUNET_memcmp (&d->master_pub, -// &keys->master_pub)) ) -// { -// /* master pub differs => fetch keys again */ -// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -// "Master public key of donau `%s' differs from our configuration. Fetching /keys again.\n", -// d->donau_url); -// DONAU_keys_decref (keys); -// keys = NULL; -// } + d->keys = keys; if (NULL == keys) { /* done synchronously so that the active_inquiries is updated immediately */ - download_keys (d); } else { d->retry_task - = GNUNET_SCHEDULER_add_at (GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_MONTHS, - 3)), + = GNUNET_SCHEDULER_add_at (keys->sign_keys[0].expire_sign.abs_time, &download_keys, d); } diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -734,7 +734,7 @@ struct PayContext #ifdef HAVE_DONAU_DONAU_SERVICE_H /** - * Struct for #phase_generate_donation_receipt + * Struct for #phase_generate_donation_receipt() */ struct { @@ -1918,9 +1918,6 @@ merchant_donau_issue_receipt_cb (void *cls, /* Donau replies asynchronously, so we expect the PayContext * to be suspended. */ GNUNET_assert (GNUNET_YES == pc->suspended); - - /* REVIEW NULL == resp in donau testing*/ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Donau responded with status=%u, ec=%u\n", resp->hr.http_status, diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -3221,6 +3221,8 @@ phase_merge_inventory (struct OrderContext *oc) } +/* ***************** ORDER_PHASE_PARSE_CHOICES **************** */ + #ifdef HAVE_DONAU_DONAU_SERVICE_H /** * Callback function that is called for each donau instance. @@ -3272,9 +3274,6 @@ add_donau_output (struct OrderContext *oc, #endif - -/* ***************** ORDER_PHASE_PARSE_CHOICES **************** */ - /** * Parse contract choices. Upon success, continue * processing with merge_inventory(). diff --git a/src/backenddb/merchant-0022.sql b/src/backenddb/merchant-0022.sql @@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS merchant_donau_keys (donau_keys_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE ,donau_url TEXT PRIMARY KEY ,keys_json TEXT NOT NULL + ,first_retry INT8 NOT NULL DEFAULT (0) ); COMMENT ON TABLE merchant_donau_keys @@ -40,6 +41,8 @@ COMMENT ON COLUMN merchant_donau_keys.donau_url IS 'Base URL of Donau associated with these keys'; COMMENT ON COLUMN merchant_donau_keys.keys_json IS 'JSON string of the /keys as generated by Donau'; +COMMENT ON COLUMN merchant_donau_keys.first_retry + IS 'Absolute time when this merchant may retry to fetch the keys from this donau at the earliest'; CREATE TABLE IF NOT EXISTS merchant_donau_instances (donau_instances_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY diff --git a/src/backenddb/pg_lookup_donau_keys.c b/src/backenddb/pg_lookup_donau_keys.c @@ -30,6 +30,7 @@ enum GNUNET_DB_QueryStatus TMH_PG_lookup_donau_keys (void *cls, const char *donau_url, + struct GNUNET_TIME_Absolute *first_retry, struct DONAU_Keys **keys) { struct PostgresClosure *pg = cls; @@ -39,6 +40,8 @@ TMH_PG_lookup_donau_keys (void *cls, }; json_t *jkeys; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_absolute_time ("first_retry", + first_retry), TALER_PQ_result_spec_json ("keys_json", &jkeys), GNUNET_PQ_result_spec_end @@ -49,7 +52,8 @@ TMH_PG_lookup_donau_keys (void *cls, PREPARE (pg, "lookup_donau_keys", "SELECT" - " keys_json" + " first_retry" + ",keys_json" " FROM merchant_donau_keys" " WHERE donau_url=$1;"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, diff --git a/src/backenddb/pg_lookup_donau_keys.h b/src/backenddb/pg_lookup_donau_keys.h @@ -38,6 +38,7 @@ enum GNUNET_DB_QueryStatus TMH_PG_lookup_donau_keys (void *cls, const char *donau_url, + struct GNUNET_TIME_Absolute *first_retry, struct DONAU_Keys **keys); diff --git a/src/backenddb/pg_upsert_donau_keys.c b/src/backenddb/pg_upsert_donau_keys.c @@ -30,7 +30,8 @@ enum GNUNET_DB_QueryStatus TMH_PG_upsert_donau_keys ( void *cls, - const struct DONAU_Keys *keys) + const struct DONAU_Keys *keys, + struct GNUNET_TIME_Absolute first_retry) { enum GNUNET_DB_QueryStatus qs; struct PostgresClosure *pg = cls; @@ -42,6 +43,7 @@ TMH_PG_upsert_donau_keys ( { struct GNUNET_PQ_QueryParam params[] = { TALER_PQ_query_param_json (jkeys), + GNUNET_PQ_query_param_absolute_time (&first_retry), GNUNET_PQ_query_param_string (keys->donau_url), GNUNET_PQ_query_param_end }; @@ -51,15 +53,16 @@ TMH_PG_upsert_donau_keys ( "insert_donau_keys", "INSERT INTO merchant_donau_keys" "(keys_json" + ",first_retry" ",donau_url" - ") VALUES ($1, $2);"); + ") VALUES ($1, $2, $3);"); PREPARE (pg, "update_donau_keys", "UPDATE merchant_donau_keys SET" - " keys_json=$1" + " keys_json=$1," + " first_retry=$2" " WHERE" - " donau_url=$2;"); - + " donau_url=$3;"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, "update_donau_keys", params); diff --git a/src/backenddb/pg_upsert_donau_keys.h b/src/backenddb/pg_upsert_donau_keys.h @@ -36,7 +36,8 @@ */ enum GNUNET_DB_QueryStatus TMH_PG_upsert_donau_keys (void *cls, - const struct DONAU_Keys *keys); + const struct DONAU_Keys *keys, + struct GNUNET_TIME_Absolute first_retry); #endif diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -4104,7 +4104,8 @@ struct TALER_MERCHANTDB_Plugin enum GNUNET_DB_QueryStatus (*upsert_donau_keys)( void *cls, - const struct DONAU_Keys *keys + const struct DONAU_Keys *keys, + struct GNUNET_TIME_Absolute first_retry ); /** @@ -4118,6 +4119,7 @@ struct TALER_MERCHANTDB_Plugin (*lookup_donau_keys)( void *cls, const char *donau_url, + struct GNUNET_TIME_Absolute *first_retry, struct DONAU_Keys **keys );