commit 0dc132ded682646339917a6a39455cbdf7e9630c
parent afb5d95964bfb6a662c3b1c6a6098cfdb2104dc7
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date: Fri, 11 Jul 2025 09:58:25 +0200
some progress for the donau pay test
Diffstat:
8 files changed, 360 insertions(+), 45 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -2682,11 +2682,20 @@ phase_execute_pay_transaction (struct PayContext *pc)
/* Store signed output tokens in database. */
for (size_t i = 0; i<pc->validate_tokens.output_tokens_len; i++)
{
+ const struct TALER_MERCHANT_ContractChoice *choice =
+ &pc->check_contract.contract_terms->details.v1
+ .choices[pc->parse_wallet_data.choice_index];
+
+ /* If the matching contract-output is a donation-receipt, we skip it. */
+ if (i < choice->outputs_len &&
+ TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT ==
+ choice->outputs[i].type)
+ continue;
+
struct SignedOutputToken *output = &pc->validate_tokens.output_tokens[i];
enum GNUNET_DB_QueryStatus qs;
- //FIXME: Fails for donau output...
qs = TMH_db->insert_issued_token (TMH_db->cls,
&pc->check_contract.h_contract_terms,
&output->h_issue,
@@ -2735,6 +2744,8 @@ phase_execute_pay_transaction (struct PayContext *pc)
}
}
+ pc->charity.charity_priv = GNUNET_new (struct DONAU_CharityPrivateKeyP);
+
/* Part where we fetch info about the charity*/
qs = TMH_db->lookup_order_charity(
TMH_db->cls,
@@ -2742,7 +2753,7 @@ phase_execute_pay_transaction (struct PayContext *pc)
&pc->charity.charity_id,
pc->charity.charity_priv);
- if (0 >= qs)
+ if (0 > qs)
{
TMH_db->rollback (TMH_db->cls);
diff --git a/src/backenddb/pg_lookup_order_charity.c b/src/backenddb/pg_lookup_order_charity.c
@@ -50,17 +50,14 @@ TMH_PG_lookup_order_charity (
{
struct PostgresClosure *pg = cls;
- uint64_t cid;
- struct DONAU_CharityPrivateKeyP cpriv;
-
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (donau_url),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("charity_id", &cid),
- GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", &cpriv),
+ GNUNET_PQ_result_spec_uint64 ("charity_id", charity_id),
+ GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", charity_priv),
GNUNET_PQ_result_spec_end
};
diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h
@@ -30,6 +30,9 @@
#include <gnunet/gnunet_time_lib.h>
#include <taler/taler_testing_lib.h>
#include "taler_merchant_service.h"
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
+#include "donau/donau_service.h"
+#endif // HAVE_DONAU_DONAU_SERVICE_H
/* ********************* Helper functions ********************* */
@@ -1904,6 +1907,35 @@ TALER_TESTING_cmd_checkserver2 (const char *label,
const char *expected_header,
const char *expected_body);
+
+#ifdef HAVE_DONAU_DONAU_SERVICE_H
+/**
+ * This function is the similar to the TALER_TESSTING_cmd_charity_post from DONAU
+ * with only difference that it re-uses the merchant public key, from the merchant test-suite.
+ *
+ * @param label command label
+ * @param name name of the charity
+ * @param url url of the charity
+ * @param max_per_year maximum amount of donations per year
+ * @param receipts_to_date amount of receipts to date
+ * @param current_year year
+ * @param bearer
+ * @param merchant_reference reference to fetch the merchant public key
+ * @param expected_response_code expected HTTP response code
+ * @return
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_charity_post_merchant (const char *label,
+ const char *name,
+ const char *url,
+ const char *max_per_year,
+ const char *receipts_to_date,
+ uint64_t current_year,
+ const struct DONAU_BearerToken *bearer,
+ const char *merchant_reference,
+ unsigned int expected_response_code);
+#endif /* HAVE_DONAU_DONAU_SERVICE_H */
+
/**
* Define a "GET /donau" CMD.
*
@@ -1939,6 +1971,7 @@ TALER_TESTING_cmd_merchant_get_donau_instances(const char *label,
struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_post_donau_instance(const char *label,
const char *url,
+ const char *merchant_reference,
unsigned int expected_http_status,
...);
diff --git a/src/lib/taler_merchant_pay_service.c b/src/lib/taler_merchant_pay_service.c
@@ -113,6 +113,18 @@ struct TALER_MERCHANT_OrderPayHandle
bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH];
bool am_wallet;
+
+ /* --- Donau (optional) ------------------------------------------------- */
+ #ifdef HAVE_DONAU_DONAU_SERVICE_H
+ bool has_donau_url;
+ char *donau_url;
+
+ bool has_donau_year;
+ uint64_t donau_year;
+
+ bool has_donau_budis;
+ json_t *donau_budikeypairs; /* array we’ll own */
+ #endif
};
/**
@@ -446,6 +458,12 @@ TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph)
json_decref (ph->tokens_evs);
ph->tokens_evs = NULL;
+ #ifdef HAVE_DONAU_DONAU_SERVICE_H
+ GNUNET_free (ph->donau_url);
+ if (ph->donau_budikeypairs)
+ json_decref (ph->donau_budikeypairs);
+ #endif
+
GNUNET_free (ph->url);
GNUNET_free (ph->merchant_url);
@@ -512,13 +530,7 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph,
case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX:
ph->choice_index = o->details.choice_index;
ph->has_choice_index = true;
- {
- json_t *js = GNUNET_JSON_PACK(GNUNET_JSON_pack_int64("choice_index", o->details.choice_index));
- enum TALER_MERCHANT_OrderPayOptionErrorCode ec = store_json_option(ph, o->ot, js);
- if (TALER_MERCHANT_OPOEC_OK != ec)
- return ec;
- break;
- }
+ break;
case TALER_MERCHANT_OrderPayOptionType_AMOUNT:
{
@@ -605,40 +617,23 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph,
/* ---------- Donau ------------ */
case TALER_MERCHANT_OrderPayOptionType_DONAU_URL:
{
- json_t *js = GNUNET_JSON_PACK(
- GNUNET_JSON_pack_string("donau_url",
- o->details.donau_url)
- );
- enum TALER_MERCHANT_OrderPayOptionErrorCode ec =
- store_json_option (ph, o->ot, js);
- if (ec != TALER_MERCHANT_OPOEC_OK)
- return ec;
+ ph->has_donau_url = true;
+ ph->donau_url = GNUNET_strdup (o->details.donau_url);
break;
}
case TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR:
{
- json_t *js = GNUNET_JSON_PACK(
- GNUNET_JSON_pack_uint64("donau_year",
- o->details.donau_year)
- );
- enum TALER_MERCHANT_OrderPayOptionErrorCode ec =
- store_json_option(ph, o->ot, js);
- if (ec != TALER_MERCHANT_OPOEC_OK)
- return ec;
+ ph->has_donau_year = true;
+ ph->donau_year = o->details.donau_year;
break;
}
case TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS:
{
- json_t *js = GNUNET_JSON_PACK(
- GNUNET_JSON_pack_array_incref("donau_budikeypairs",
- o->details.donau_budis_json)
- );
- enum TALER_MERCHANT_OrderPayOptionErrorCode ec =
- store_json_option(ph, o->ot, js);
- if (ec != TALER_MERCHANT_OPOEC_OK)
- return ec;
+ ph->has_donau_budis = true;
+ ph->donau_budikeypairs = o->details.donau_budis_json;
+ json_incref (ph->donau_budikeypairs);
break;
}
#endif /* HAVE_DONAU_DONAU_SERVICE_H */
@@ -668,11 +663,33 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph)
/* --- build wallet_data hash for signing coins & tokens --- */
if (ph->has_choice_index) {
- ph->wallet_data = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_int64("choice_index", ph->choice_index),
- GNUNET_JSON_pack_allow_null(
- GNUNET_JSON_pack_array_incref("tokens_evs", ph->tokens_evs))
- );
+ /* base fields */
+ json_t *wd = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("choice_index", ph->choice_index),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_incref ("tokens_evs", ph->tokens_evs))
+ );
+
+ #ifdef HAVE_DONAU_DONAU_SERVICE_H
+ /* Donau extras (optional) */
+ if (ph->has_donau_url)
+ GNUNET_assert (0 == json_object_set_new (wd,
+ "donau_url",
+ json_string (ph->donau_url)));
+
+ if (ph->has_donau_year)
+ GNUNET_assert (0 == json_object_set_new (wd,
+ "donau_year",
+ json_integer ((json_int_t) ph->donau_year)));
+
+ if (ph->has_donau_budis)
+ GNUNET_assert (0 == json_object_set_new (wd,
+ "donau_budikeypairs",
+ json_incref (ph->donau_budikeypairs)));
+ #endif
+
+ ph->wallet_data = wd;
+
TALER_json_hash (ph->wallet_data,
&ph->wallet_data_hash);
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
@@ -89,6 +89,7 @@ libtalermerchanttesting_la_SOURCES = \
if HAVE_DONAU
libtalermerchanttesting_la_SOURCES += \
+ testing_api_cmd_post_donau_charity_merchant.c \
testing_api_cmd_post_donau_instances.c \
testing_api_cmd_get_donau_instances.c \
testing_api_cmd_delete_donau_instances.c
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
@@ -1860,17 +1860,19 @@ run (void *cls,
TALER_TESTING_cmd_get_donau ("get-donau",
cred.cfg,
true)),
- TALER_TESTING_cmd_charity_post ("post-charity",
+ TALER_TESTING_cmd_charity_post_merchant ("post-charity",
"example",
"example.com",
"EUR:50", // max_per_year
"EUR:0", // receipts_to_date
2025, // current year
&bearer,
+ "create-another-order-with-input-and-output", //reusing the merhcant_reference for merchant_pub
MHD_HTTP_CREATED),
TALER_TESTING_cmd_merchant_post_donau_instance (
"post-donau-instance",
merchant_url,
+ "create-another-order-with-input-and-output", //reusing the merhcant_reference
MHD_HTTP_NO_CONTENT),
TALER_TESTING_cmd_sleep (
"In this time donaukeyupdate must fetch the keys from the donau", 1),
@@ -2254,7 +2256,7 @@ run (void *cls,
tokens),
#ifdef HAVE_DONAU_DONAU_SERVICE_H
- TALER_TESTING_cmd_sleep("dream", 30),
+ //TALER_TESTING_cmd_sleep("dream", 30),
TALER_TESTING_cmd_batch ("donau",
donau),
#endif
diff --git a/src/testing/testing_api_cmd_post_donau_charity_merchant.c b/src/testing/testing_api_cmd_post_donau_charity_merchant.c
@@ -0,0 +1,229 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing_api_cmd_post_donau_charity_merchant.c
+ * @brief command to test POST /donau charity with merchant_pub
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include <taler/taler_signatures.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+#include "taler_merchant_donau.h"
+#include <donau/donau_service.h>
+#include <donau/donau_testing_lib.h>
+
+
+/**
+ * State for a "status" CMD.
+ */
+struct StatusState
+{
+ /**
+ * Handle to the "charity status" operation.
+ */
+ struct DONAU_CharityPostHandle *cph;
+
+ /**
+ * The charity POST request.
+ */
+ struct DONAU_CharityRequest charity_req;
+
+ /**
+ * The bearer token for authorization.
+ */
+ const struct DONAU_BearerToken *bearer;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * charity id
+ */
+ unsigned long long charity_id;
+
+ /**
+ * Merchant reference to fetch public key.
+ */
+ const char *merchant_reference;
+};
+
+
+/**
+ * Check that the reserve balance and HTTP response code are
+ * both acceptable.
+ *
+ * @param cls closure.
+ * @param gcr HTTP response details
+ */
+static void
+charity_status_cb (void *cls,
+ const struct DONAU_PostCharityResponse *gcr)
+{
+ struct StatusState *ss = cls;
+
+ ss->cph = NULL;
+ if (ss->expected_response_code != gcr->hr.http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected HTTP response code: %d in %s:%u\n",
+ gcr->hr.http_status,
+ __FILE__,
+ __LINE__);
+ json_dumpf (gcr->hr.reply,
+ stderr,
+ 0);
+ TALER_TESTING_interpreter_fail (ss->is);
+ return;
+ }
+ if (ss->expected_response_code == gcr->hr.http_status)
+ ss->charity_id = (unsigned long long) gcr->details.ok.charity_id;
+ TALER_TESTING_interpreter_next (ss->is);
+}
+
+
+/**
+ * Run the command.
+ *
+ * @param cls closure.
+ * @param cmd the command being executed.
+ * @param is the interpreter state.
+ */
+static void
+charity_status_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct StatusState *ss = cls;
+
+ ss->is = is;
+
+ if (NULL != ss->merchant_reference)
+ {
+ const struct TALER_TESTING_Command *mc;
+ const struct TALER_MerchantPublicKeyP *mpub;
+
+ mc = TALER_TESTING_interpreter_lookup_command (is,
+ ss->merchant_reference);
+ GNUNET_assert (NULL != mc);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_merchant_pub (mc,
+ &mpub));
+
+ ss->charity_req.charity_pub.eddsa_pub = mpub->eddsa_pub;
+ }
+
+ ss->cph = DONAU_charity_post (
+ TALER_TESTING_interpreter_get_context (is),
+ TALER_TESTING_get_donau_url (is),
+ &ss->charity_req,
+ ss->bearer,
+ &charity_status_cb,
+ ss);
+}
+
+
+/**
+ * Cleanup the state from a "reserve status" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd the command which is being cleaned up.
+ */
+static void
+cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct StatusState *ss = cls;
+
+ if (NULL != ss->cph)
+ {
+ // log incomplete command
+ TALER_TESTING_command_incomplete (ss->is,
+ cmd->label);
+ DONAU_charity_post_cancel (ss->cph);
+ ss->cph = NULL;
+ }
+ GNUNET_free (ss);
+}
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_charity_post_merchant (const char *label,
+ const char *name,
+ const char *url,
+ const char *max_per_year,
+ const char *receipts_to_date,
+ uint64_t current_year,
+ const struct DONAU_BearerToken *bearer,
+ const char *merchant_reference,
+ unsigned int expected_response_code)
+{
+ struct StatusState *ss;
+
+ ss = GNUNET_new (struct StatusState);
+
+ ss->merchant_reference = merchant_reference;
+ ss->charity_req.name = name;
+ ss->charity_req.charity_url = url;
+ // parse string max_per_year to amount
+ if (GNUNET_OK !=
+ TALER_string_to_amount (max_per_year,
+ &ss->charity_req.max_per_year))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %s\n",
+ max_per_year,
+ label);
+ GNUNET_assert (0);
+ }
+ // parse string receipts_to_date to amount
+ if (GNUNET_OK !=
+ TALER_string_to_amount (receipts_to_date,
+ &ss->charity_req.receipts_to_date))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %s\n",
+ receipts_to_date,
+ label);
+ GNUNET_assert (0);
+ }
+ ss->charity_req.current_year = current_year;
+ ss->expected_response_code = expected_response_code;
+ ss->bearer = bearer;
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = ss,
+ .label = label,
+ .run = &charity_status_run,
+ .cleanup = &cleanup
+ };
+
+ return cmd;
+ }
+}
diff --git a/src/testing/testing_api_cmd_post_donau_instances.c b/src/testing/testing_api_cmd_post_donau_instances.c
@@ -57,6 +57,11 @@ struct PostDonauState
struct TALER_MERCHANT_DONAU_Charity charity;
/**
+ * Merchant reference to fetch public key.
+ */
+ const char *merchant_reference;
+
+ /**
* Authentication token for the request.
*/
const char *auth_token;
@@ -135,6 +140,23 @@ post_donau_run (void *cls,
//Replacing the url of the charity
pds->charity.charity_url = TALER_TESTING_get_donau_url(is);
+ //Fetching the publick key of the merchant
+ if (NULL != pds->merchant_reference)
+ {
+ const struct TALER_TESTING_Command *mc;
+ const struct TALER_MerchantPublicKeyP *merchant_pub;
+
+ mc = TALER_TESTING_interpreter_lookup_command (is,
+ pds->merchant_reference);
+ GNUNET_assert (NULL != mc);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_merchant_pub (mc,
+ &merchant_pub));
+
+ /* shallow copy of the actual EdDSA key */
+ pds->charity.charity_pub.eddsa_pub = merchant_pub->eddsa_pub;
+ }
+
pds->dph = TALER_MERCHANT_donau_instances_post(
TALER_TESTING_interpreter_get_context(is),
pds->merchant_url,
@@ -184,11 +206,13 @@ post_donau_cleanup (void *cls,
struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_post_donau_instance(const char *label,
const char *merchant_url,
+ const char *merchant_reference,
unsigned int http_status,
...)
{
struct PostDonauState *pds = GNUNET_new(struct PostDonauState);
+ //FIXME: I need to steal the charity_pub->eddsa_pub from the merchant_pub->eddsa_pub
struct DONAU_CharityPublicKeyP *charity_pub = GNUNET_new(struct DONAU_CharityPublicKeyP);
struct TALER_Amount max_amount;
@@ -215,6 +239,7 @@ TALER_TESTING_cmd_merchant_post_donau_instance(const char *label,
.current_year = 2025
};
+ pds->merchant_reference = merchant_reference;
pds->merchant_url = merchant_url;
pds->charity = charity;
pds->http_status = http_status;