summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2023-05-01 00:09:55 +0200
committerChristian Grothoff <christian@grothoff.org>2023-05-01 00:09:55 +0200
commit583c01c6224dd317f9665e623b02a32b74fdf74a (patch)
tree0b12e40c9dafde2b235c3c405a1ee032ff0e70d4
parenta7fe1d1b77ce1d7959522262f85788807d56316b (diff)
downloadmerchant-583c01c6224dd317f9665e623b02a32b74fdf74a.tar.gz
merchant-583c01c6224dd317f9665e623b02a32b74fdf74a.tar.bz2
merchant-583c01c6224dd317f9665e623b02a32b74fdf74a.zip
first rough cut at merchant update for #7810 (still with known bugs)
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c541
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.h34
-rw-r--r--src/backend/taler-merchant-httpd_helper.c96
-rw-r--r--src/backend/taler-merchant-httpd_helper.h16
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-abort.c26
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c54
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-refund.c26
-rw-r--r--src/backend/taler-merchant-httpd_post-tips-ID-pickup.c25
-rw-r--r--src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c21
-rw-r--r--src/backend/taler-merchant-httpd_private-get-orders-ID.c11
-rw-r--r--src/backend/taler-merchant-httpd_private-get-reserves-ID.c11
-rw-r--r--src/backend/taler-merchant-httpd_private-post-reserves.c84
-rw-r--r--src/backend/taler-merchant-httpd_private-post-transfers.c19
-rw-r--r--src/backend/taler-merchant-httpd_reserves.c7
-rw-r--r--src/backenddb/Makefile.am3
-rw-r--r--src/backenddb/merchant-0005.sql30
-rw-r--r--src/backenddb/pg_delete_exchange_accounts.c48
-rw-r--r--src/backenddb/pg_delete_exchange_accounts.h42
-rw-r--r--src/backenddb/pg_insert_exchange_account.c66
-rw-r--r--src/backenddb/pg_insert_exchange_account.h51
-rw-r--r--src/backenddb/pg_select_accounts_by_exchange.c146
-rw-r--r--src/backenddb/pg_select_accounts_by_exchange.h45
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c41
-rw-r--r--src/include/taler_merchant_service.h133
-rw-r--r--src/include/taler_merchantdb_plugin.h91
-rw-r--r--src/lib/merchant_api_get_reserve.c164
-rw-r--r--src/lib/merchant_api_post_order_pay.c29
-rw-r--r--src/lib/merchant_api_post_reserves.c70
-rw-r--r--src/lib/merchant_api_tip_pickup.c2
-rw-r--r--src/merchant-tools/taler-merchant-setup-reserve.c91
-rw-r--r--src/testing/testing_api_cmd_get_reserve.c129
-rw-r--r--src/testing/testing_api_cmd_post_reserves.c21
-rw-r--r--src/testing/testing_api_cmd_post_transfers.c4
33 files changed, 1495 insertions, 682 deletions
diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c
index f0324c47..90875836 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014-2021 Taler Systems SA
+ (C) 2014-2023 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
@@ -24,6 +24,10 @@
#include "taler-merchant-httpd_exchanges.h"
#include "taler-merchant-httpd.h"
+/**
+ * How often do we retry DB transactions with soft errors?
+ */
+#define MAX_RETRIES 3
/**
* Delay after which we'll re-fetch key information from the exchange.
@@ -130,11 +134,6 @@ struct FeesByWireMethod
char *wire_method;
/**
- * Full payto URI of the exchange.
- */
- char *payto_uri;
-
- /**
* Applicable fees, NULL if unknown/error.
*/
struct TALER_EXCHANGE_WireAggregateFees *af;
@@ -285,16 +284,11 @@ json_t *TMH_trusted_exchanges;
* this callback is called. Thus, once 'pending' turns 'false',
* it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
* in order to get the "good" keys.
- * @param hr http response details
- * @param keys information about the various keys used
- * by the exchange
- * @param compat version compatibility data
+ * @param kr response details
*/
static void
keys_mgmt_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat);
+ const struct TALER_EXCHANGE_KeysResponse *kr);
/**
@@ -347,134 +341,189 @@ retry_exchange (void *cls)
*
* @param exchange connection to the exchange
* @param master_pub public key of the exchange
- * @param wire_method name of the wire method (i.e. "iban")
- * @param payto_uri full payto URI of the exchange
- * @param fees fee structure for this method
+ * @param num_methods number of wire methods supported
+ * @param fbm wire fees by method
* @return #TALER_EC_NONE on success
*/
static enum TALER_ErrorCode
process_wire_fees (struct Exchange *exchange,
const struct TALER_MasterPublicKeyP *master_pub,
- const char *wire_method,
- const char *payto_uri,
- const struct TALER_EXCHANGE_WireAggregateFees *fees)
+ unsigned int num_methods,
+ const struct TALER_EXCHANGE_WireFeesByMethod *fbm)
{
- struct FeesByWireMethod *f;
- struct TALER_EXCHANGE_WireAggregateFees *endp;
- struct TALER_EXCHANGE_WireAggregateFees *af;
-
- for (f = exchange->wire_fees_head; NULL != f; f = f->next)
- if (0 == strcasecmp (wire_method,
- f->wire_method))
- break;
- if (NULL == f)
+ for (unsigned int i = 0; i<num_methods; i++)
{
- f = GNUNET_new (struct FeesByWireMethod);
- f->wire_method = GNUNET_strdup (wire_method);
- f->payto_uri = GNUNET_strdup (payto_uri);
- GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
- exchange->wire_fees_tail,
- f);
- }
- endp = f->af;
- while ( (NULL != endp) &&
- (NULL != endp->next) )
- endp = endp->next;
- while ( (NULL != endp) &&
- (NULL != fees) &&
- (GNUNET_TIME_timestamp_cmp (fees->start_date,
- <,
- endp->end_date)) )
- fees = fees->next;
- if ( (NULL != endp) &&
- (NULL != fees) &&
- (GNUNET_TIME_timestamp_cmp (fees->start_date,
- !=,
- endp->end_date)) )
- {
- /* Hole in the fee structure, not allowed! */
- GNUNET_break_op (0);
- return TALER_EC_MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE;
- }
- while (NULL != fees)
- {
- struct GNUNET_HashCode h_wire_method;
- enum GNUNET_DB_QueryStatus qs;
+ const char *wire_method = fbm[i].method;
+ const struct TALER_EXCHANGE_WireAggregateFees *fees = fbm[i].fees_head;
+ struct FeesByWireMethod *f;
+ struct TALER_EXCHANGE_WireAggregateFees *endp;
+ struct TALER_EXCHANGE_WireAggregateFees *af;
- af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
- *af = *fees;
- GNUNET_CRYPTO_hash (wire_method,
- strlen (wire_method) + 1,
- &h_wire_method);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Storing wire fee for `%s' and method `%s' at %s in DB; the fee is %s\n",
- TALER_B2S (master_pub),
- wire_method,
- GNUNET_TIME_timestamp2s (af->start_date),
- TALER_amount2s (&af->fees.wire));
- TMH_db->preflight (TMH_db->cls);
- if (GNUNET_OK !=
- TMH_db->start (TMH_db->cls,
- "store wire fee"))
+ for (f = exchange->wire_fees_head; NULL != f; f = f->next)
+ if (0 == strcasecmp (wire_method,
+ f->wire_method))
+ break;
+ if (NULL == f)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to start database transaction to store exchange wire fees (will try to continue anyway)!\n");
- GNUNET_free (af);
- fees = fees->next;
- continue;
+ f = GNUNET_new (struct FeesByWireMethod);
+ f->wire_method = GNUNET_strdup (wire_method);
+ GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
+ exchange->wire_fees_tail,
+ f);
}
- qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
- master_pub,
- &h_wire_method,
- &af->fees,
- af->start_date,
- af->end_date,
- &af->master_sig);
- if (0 > qs)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
- GNUNET_free (af);
+ endp = f->af;
+ while ( (NULL != endp) &&
+ (NULL != endp->next) )
+ endp = endp->next;
+ while ( (NULL != endp) &&
+ (NULL != fees) &&
+ (GNUNET_TIME_timestamp_cmp (fees->start_date,
+ <,
+ endp->end_date)) )
fees = fees->next;
- TMH_db->rollback (TMH_db->cls);
- continue;
- }
- if (0 == qs)
+ if ( (NULL != endp) &&
+ (NULL != fees) &&
+ (GNUNET_TIME_timestamp_cmp (fees->start_date,
+ !=,
+ endp->end_date)) )
{
- /* Entry was already in DB, fine, continue as if we had succeeded */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Fees already in DB, rolling back transaction attempt!\n");
- TMH_db->rollback (TMH_db->cls);
+ /* Hole in the fee structure, not allowed! */
+ GNUNET_break_op (0);
+ return TALER_EC_MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE;
}
- if (0 < qs)
+ while (NULL != fees)
{
- /* Inserted into DB, make sure transaction completes */
- qs = TMH_db->commit (TMH_db->cls);
+ struct GNUNET_HashCode h_wire_method;
+ enum GNUNET_DB_QueryStatus qs;
+
+ af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
+ *af = *fees;
+ GNUNET_CRYPTO_hash (wire_method,
+ strlen (wire_method) + 1,
+ &h_wire_method);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Storing wire fee for `%s' and method `%s' at %s in DB; the fee is %s\n",
+ TALER_B2S (master_pub),
+ wire_method,
+ GNUNET_TIME_timestamp2s (af->start_date),
+ TALER_amount2s (&af->fees.wire));
+ TMH_db->preflight (TMH_db->cls);
+ if (GNUNET_OK !=
+ TMH_db->start (TMH_db->cls,
+ "store wire fee"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to start database transaction to store exchange wire fees (will try to continue anyway)!\n");
+ GNUNET_free (af);
+ fees = fees->next;
+ continue;
+ }
+ qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
+ master_pub,
+ &h_wire_method,
+ &af->fees,
+ af->start_date,
+ af->end_date,
+ &af->master_sig);
if (0 > qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
GNUNET_free (af);
fees = fees->next;
+ TMH_db->rollback (TMH_db->cls);
continue;
}
- }
- af->next = NULL;
- if (NULL == endp)
- f->af = af;
- else
- endp->next = af;
- endp = af;
- fees = fees->next;
- }
+ if (0 == qs)
+ {
+ /* Entry was already in DB, fine, continue as if we had succeeded */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Fees already in DB, rolling back transaction attempt!\n");
+ TMH_db->rollback (TMH_db->cls);
+ }
+ if (0 < qs)
+ {
+ /* Inserted into DB, make sure transaction completes */
+ qs = TMH_db->commit (TMH_db->cls);
+ if (0 > qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
+ GNUNET_free (af);
+ fees = fees->next;
+ continue;
+ }
+ }
+ af->next = NULL;
+ if (NULL == endp)
+ f->af = af;
+ else
+ endp->next = af;
+ endp = af;
+ fees = fees->next;
+ } /* all fees for this method */
+ } /* for all methods (i) */
return TALER_EC_NONE;
}
/**
- * Function called with information about the wire accounts
- * of the exchange. Stores the wire fees with the
- * exchange for laster use.
+ * Add account restriction @a a to array of @a restrictions.
+ *
+ * @param[in,out] restrictions JSON array to build
+ * @param r restriction to add to @a restrictions
+ * @return #GNUNET_SYSERR if @a r is malformed
+ */
+static enum GNUNET_GenericReturnValue
+add_restriction (json_t *restrictions,
+ const struct TALER_EXCHANGE_AccountRestriction *r)
+{
+ json_t *jr;
+
+ jr = NULL;
+ switch (r->type)
+ {
+ case TALER_EXCHANGE_AR_INVALID:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ case TALER_EXCHANGE_AR_DENY:
+ jr = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "deny")
+ );
+ break;
+ case TALER_EXCHANGE_AR_REGEX:
+ jr = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "type",
+ "regex"),
+ GNUNET_JSON_pack_string (
+ "regex",
+ r->details.regex.posix_egrep),
+ GNUNET_JSON_pack_string (
+ "human_hint",
+ r->details.regex.human_hint),
+ GNUNET_JSON_pack_object_incref (
+ "human_hint_i18n",
+ (json_t *) r->details.regex.human_hint_i18n)
+ );
+ break;
+ }
+ if (NULL == jr)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (0 ==
+ json_array_append_new (restrictions,
+ jr));
+ return GNUNET_OK;
+
+}
+
+
+/**
+ * Function called with information about the wire accounts of the exchange.
*
* @param exchange the exchange
* @param master_pub public key of the exchange
@@ -488,28 +537,89 @@ process_wire_accounts (struct Exchange *exchange,
unsigned int accounts_len,
const struct TALER_EXCHANGE_WireAccount *accounts)
{
- for (unsigned int i = 0; i<accounts_len; i++)
+ for (unsigned int r = 0; r<MAX_RETRIES; r++)
{
- enum TALER_ErrorCode ec;
- char *method;
+ enum GNUNET_DB_QueryStatus qs;
- method = TALER_payto_get_method (accounts[i].payto_uri);
- if (NULL == method)
+ if (GNUNET_OK !=
+ TMH_db->start (TMH_db->cls,
+ "update_exchange_accounts"))
{
- /* malformed payto:// URI returned by exchange */
- GNUNET_break_op (0);
- return TALER_EC_GENERIC_PAYTO_URI_MALFORMED;
+ TMH_db->rollback (TMH_db->cls);
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_DB_START_FAILED;
}
- ec = process_wire_fees (exchange,
- master_pub,
- method,
- accounts[i].payto_uri,
- accounts[i].fees);
- GNUNET_free (method);
- if (TALER_EC_NONE != ec)
- return ec;
+ qs = TMH_db->delete_exchange_accounts (TMH_db->cls,
+ master_pub);
+ if (qs < 0)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ continue;
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_DB_STORE_FAILED;
+ }
+ for (unsigned int i = 0; i<accounts_len; i++)
+ {
+ const struct TALER_EXCHANGE_WireAccount *account = &accounts[i];
+ json_t *debit_restrictions;
+ json_t *credit_restrictions;
+
+ debit_restrictions = json_array ();
+ GNUNET_assert (NULL != debit_restrictions);
+ credit_restrictions = json_array ();
+ GNUNET_assert (NULL != credit_restrictions);
+ for (unsigned int j = 0; j<account->debit_restrictions_length; j++)
+ {
+ if (GNUNET_OK !=
+ add_restriction (debit_restrictions,
+ &account->debit_restrictions[j]))
+ {
+ TMH_db->rollback (TMH_db->cls);
+ GNUNET_break (0);
+ return TALER_EC_MERCHANT_GENERIC_EXCHANGE_WIRE_REQUEST_FAILED;
+ }
+ }
+ for (unsigned int j = 0; j<account->credit_restrictions_length; j++)
+ {
+ if (GNUNET_OK !=
+ add_restriction (credit_restrictions,
+ &account->credit_restrictions[j]))
+ {
+ TMH_db->rollback (TMH_db->cls);
+ GNUNET_break (0);
+ return TALER_EC_MERCHANT_GENERIC_EXCHANGE_WIRE_REQUEST_FAILED;
+ }
+ }
+ qs = TMH_db->insert_exchange_account (TMH_db->cls,
+ master_pub,
+ account->payto_uri,
+ account->conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ &account->master_sig);
+ if (qs < 0)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto outer;
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_DB_STORE_FAILED;
+ }
+ }
+ qs = TMH_db->commit (TMH_db->cls);
+ if (qs < 0)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ continue;
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_DB_COMMIT_FAILED;
+ }
+ return TALER_EC_NONE;
+outer:;
}
- return TALER_EC_NONE;
+ return TALER_EC_GENERIC_DB_SOFT_FAILURE;
}
@@ -643,8 +753,6 @@ process_find_operations (struct Exchange *exchange)
fo->fc (fo->fc_cls,
&hr,
exchange->conn,
- (NULL != fbw) ? fbw->payto_uri : NULL,
- (NULL != fbw) ? &fbw->af->fees.wire : NULL,
exchange->trusted);
}
TMH_EXCHANGES_find_exchange_cancel (fo);
@@ -668,15 +776,11 @@ wire_task_cb (void *cls);
* that is #TALER_EXCHANGE_get_keys() will succeed.
*
* @param cls closure, a `struct Exchange`
- * @param hr HTTP response details
- * @param accounts_len length of the @a accounts array
- * @param accounts list of wire accounts of the exchange, NULL on error
+ * @param wr response details
*/
static void
handle_wire_data (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- unsigned int accounts_len,
- const struct TALER_EXCHANGE_WireAccount *accounts)
+ const struct TALER_EXCHANGE_WireResponse *wr)
{
struct Exchange *exchange = cls;
const struct TALER_EXCHANGE_Keys *keys;
@@ -685,7 +789,7 @@ handle_wire_data (void *cls,
exchange->wire_request = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Received /wire response\n");
- if (MHD_HTTP_OK != hr->http_status)
+ if (MHD_HTTP_OK != wr->hr.http_status)
{
struct TMH_EXCHANGES_FindOperation *fo;
@@ -693,16 +797,14 @@ handle_wire_data (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to obtain /wire details from `%s': %u/%d\n",
exchange->url,
- hr->http_status,
- hr->ec);
+ wr->hr.http_status,
+ wr->hr.ec);
while (NULL != (fo = exchange->fo_head))
{
fo->fc (fo->fc_cls,
- hr,
+ &wr->hr,
exchange->conn,
- NULL,
- NULL,
- GNUNET_NO);
+ false);
TMH_EXCHANGES_find_exchange_cancel (fo);
}
return;
@@ -711,8 +813,13 @@ handle_wire_data (void *cls,
GNUNET_assert (NULL != keys);
ecx = process_wire_accounts (exchange,
&keys->master_pub,
- accounts_len,
- accounts);
+ wr->details.ok.accounts_len,
+ wr->details.ok.accounts);
+ if (TALER_EC_NONE == ecx)
+ ecx = process_wire_fees (exchange,
+ &keys->master_pub,
+ wr->details.ok.fees_len,
+ wr->details.ok.fees);
if (TALER_EC_NONE != ecx)
{
/* Report hard failure to all callbacks! */
@@ -720,7 +827,7 @@ handle_wire_data (void *cls,
struct TALER_EXCHANGE_HttpResponse hrx = {
.ec = ecx,
.http_status = 0,
- .reply = hr->reply
+ .reply = wr->hr.reply
};
GNUNET_break_op (0);
@@ -730,9 +837,7 @@ handle_wire_data (void *cls,
fo->fc (fo->fc_cls,
&hrx,
NULL,
- NULL,
- NULL,
- GNUNET_NO);
+ false);
TMH_EXCHANGES_find_exchange_cancel (fo);
}
return;
@@ -754,7 +859,7 @@ handle_wire_data (void *cls,
GNUNET_STRINGS_relative_time_to_string (
exchange->wire_retry_delay,
- GNUNET_YES));
+ true));
exchange->wire_task
= GNUNET_SCHEDULER_add_delayed (exchange->wire_retry_delay,
&wire_task_cb,
@@ -818,7 +923,6 @@ free_exchange_entry (struct Exchange *exchange)
GNUNET_free (af);
}
GNUNET_free (f->wire_method);
- GNUNET_free (f->payto_uri);
GNUNET_free (f);
}
if (NULL != exchange->wire_request)
@@ -854,12 +958,10 @@ free_exchange_entry (struct Exchange *exchange)
*
* @param exchange exchange that failed
* @param hr details about the HTTP reply
- * @param compat version compatibility data
*/
static void
fail_and_retry (struct Exchange *exchange,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- enum TALER_EXCHANGE_VersionCompatibility compat)
+ const struct TALER_EXCHANGE_HttpResponse *hr)
{
struct TMH_EXCHANGES_FindOperation *fo;
@@ -879,23 +981,9 @@ fail_and_retry (struct Exchange *exchange,
fo->fc (fo->fc_cls,
hr,
NULL,
- NULL,
- NULL,
- GNUNET_NO);
+ false);
TMH_EXCHANGES_find_exchange_cancel (fo);
}
- if (TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER == compat)
- {
- /* Log hard error: we likely need admin help! */
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Exchange `%s' runs an incompatible more recent version of the Taler protocol. Will not retry. This client may need to be updated.\n",
- exchange->url);
- /* Theoretically, the exchange could downgrade,
- but let's not be too aggressive about retries
- on this one. */
- exchange->retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS,
- exchange->retry_delay);
- }
if ( (NULL == exchange->fo_head) &&
(TALER_EC_GENERIC_CONFIGURATION_INVALID == hr->ec) )
{
@@ -921,40 +1009,22 @@ fail_and_retry (struct Exchange *exchange,
}
-/**
- * Function called with information about who is auditing
- * a particular exchange and what key the exchange is using.
- *
- * @param cls closure, will be `struct Exchange` so that
- * when this function gets called, it will change the flag 'pending'
- * to 'false'. Note: 'keys' is automatically saved inside the exchange's
- * handle, which is contained inside 'struct Exchange', when
- * this callback is called. Thus, once 'pending' turns 'false',
- * it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
- * in order to get the "good" keys.
- * @param hr http response details
- * @param keys information about the various keys used
- * by the exchange
- * @param compat version compatibility data
- */
static void
keys_mgmt_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat)
+ const struct TALER_EXCHANGE_KeysResponse *kr)
{
struct Exchange *exchange = cls;
struct GNUNET_TIME_Timestamp expire;
struct GNUNET_TIME_Relative delay;
+ const struct TALER_EXCHANGE_Keys *keys;
- if ( (MHD_HTTP_OK != hr->http_status) ||
- (NULL == keys) )
+ if (MHD_HTTP_OK != kr->hr.http_status)
{
fail_and_retry (exchange,
- hr,
- compat);
+ &kr->hr);
return;
}
+ keys = kr->details.ok.keys;
if ( (exchange->trusted) &&
(0 != GNUNET_memcmp (&exchange->master_pub,
&keys->master_pub)) )
@@ -982,19 +1052,6 @@ keys_mgmt_cb (void *cls,
exchange->trusted = true; /* same exchange, different URL => trust applies */
}
}
- if (0 != (TALER_EXCHANGE_VC_NEWER & compat))
- {
- /* Warn user exactly once about need to upgrade */
- static int once;
-
- if (0 == once)
- {
- once = 1;
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Exchange `%s' runs a more recent version of the Taler protocol. You may want to update this client.\n",
- exchange->url);
- }
- }
/* store exchange online signing keys in our DB */
for (unsigned int i = 0; i<keys->num_sign_keys; i++)
@@ -1015,8 +1072,7 @@ keys_mgmt_cb (void *cls,
{
GNUNET_break (0);
fail_and_retry (exchange,
- hr,
- compat);
+ &kr->hr);
return;
}
}
@@ -1089,16 +1145,35 @@ return_result (void *cls)
}
+/**
+ * Lookup exchange by @a exchange_url.
+ *
+ * @param exchange_url base URL to match against
+ * @return NULL if exchange is not yet known
+ */
+static struct Exchange *
+lookup_exchange (const char *exchange_url)
+{
+ for (struct Exchange *exchange = exchange_head;
+ NULL != exchange;
+ exchange = exchange->next)
+ if (0 == strcmp (exchange->url,
+ exchange_url))
+ return exchange;
+ return NULL;
+}
+
+
struct TMH_EXCHANGES_FindOperation *
TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
- const char *wire_method,
- int force_reload,
+ bool force_reload,
TMH_EXCHANGES_FindContinuation fc,
void *fc_cls)
{
struct Exchange *exchange;
struct TMH_EXCHANGES_FindOperation *fo;
struct GNUNET_TIME_Timestamp now;
+ const char *wire_method = NULL;
if (NULL == merchant_curl_ctx)
{
@@ -1109,22 +1184,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
"Trying to find chosen exchange `%s'\n",
chosen_exchange);
/* Check if the exchange is known */
- for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
- {
- /* test it by checking URL */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Comparing chosen exchange url '%s' with known url '%s'.\n",
- chosen_exchange,
- exchange->url);
- if (0 == strcmp (exchange->url,
- chosen_exchange))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "The exchange `%s' is already known (good)\n",
- chosen_exchange);
- break;
- }
- }
+ exchange = lookup_exchange (chosen_exchange);
if (NULL == exchange)
{
/* This is a new exchange */
@@ -1326,6 +1386,31 @@ accept_exchanges (void *cls,
enum GNUNET_GenericReturnValue
+TMH_EXCHANGES_lookup_wire_fee (const char *exchange_url,
+ const char *wire_method,
+ struct TALER_Amount *wire_fee)
+{
+ struct Exchange *exchange;
+ const struct FeesByWireMethod *fbm;
+ const struct TALER_EXCHANGE_WireAggregateFees *af;
+
+ exchange = lookup_exchange (exchange_url);
+ if (NULL == exchange)
+ return GNUNET_SYSERR;
+ if (! exchange->have_wire)
+ return GNUNET_SYSERR;
+ fbm = get_wire_fees (exchange,
+ GNUNET_TIME_timestamp_get (),
+ wire_method);
+ if (NULL == fbm)
+ return GNUNET_NO;
+ af = fbm->af;
+ *wire_fee = af->fees.wire;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
merchant_curl_ctx
diff --git a/src/backend/taler-merchant-httpd_exchanges.h b/src/backend/taler-merchant-httpd_exchanges.h
index df68b9a5..2f4e69fe 100644
--- a/src/backend/taler-merchant-httpd_exchanges.h
+++ b/src/backend/taler-merchant-httpd_exchanges.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014-2020 Taler Systems SA
+ (C) 2014-2023 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
@@ -61,16 +61,12 @@ TMH_EXCHANGES_done (void);
* @param cls closure
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
typedef void
(*TMH_EXCHANGES_FindContinuation)(void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted);
@@ -86,24 +82,36 @@ struct TMH_EXCHANGES_FindOperation;
* NULL for the exchange.
*
* @param chosen_exchange URL of the exchange we would like to talk to
- * @param wire_method the wire method we will use with @a chosen_exchange, NULL for none
- * @param force_reload try to force reloading /keys from the exchange ASAP; note
- * that IF the forced reload fails, it is possible @a fc won't be called at all
- * until a /keys download succeeds; only use #GNUNET_YES if a new /keys request
- * is mandatory. If the force reload request is not allowed due to our rate limiting,
- * then @a fc will be called immediately with the existing /keys data
+ * @param force_reload set to true to download /wire again even if we already have
+ * an answer (used if the answer might be stale).
* @param fc function to call with the handles for the exchange
* @param fc_cls closure for @a fc
*/
struct TMH_EXCHANGES_FindOperation *
TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
- const char *wire_method,
- int force_reload,
+ bool force_reload,
TMH_EXCHANGES_FindContinuation fc,
void *fc_cls);
/**
+ * Lookup current wire fee by @a exchange_url and
+ * @a wire_method.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param wire_method wire method to lookup fee by
+ * @param[out] wire_fee set to the wire fee
+ * @return #GNUNET_OK on success
+ * #GNUNET_NO if @a wire_method is not supported
+ * #GNUNET_SYSERR if @a exchange_url did not yet respond properly to our /wire request
+ */
+enum GNUNET_GenericReturnValue
+TMH_EXCHANGES_lookup_wire_fee (const char *exchange_url,
+ const char *wire_method,
+ struct TALER_Amount *wire_fee);
+
+
+/**
* Abort pending find operation.
*
* @param fo handle to operation to abort
diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c
index 981f5937..b93cdb12 100644
--- a/src/backend/taler-merchant-httpd_helper.c
+++ b/src/backend/taler-merchant-httpd_helper.c
@@ -755,3 +755,99 @@ TMH_trigger_webhook (const char *instance,
return qs;
return t.rv;
}
+
+
+/**
+ * Closure for #add_matching_account().
+ */
+struct ExchangeMatchContext
+{
+ /**
+ * Wire method to match, NULL for all.
+ */
+ const char *wire_method;
+
+ /**
+ * Array of accounts to return.
+ */
+ json_t *accounts;
+};
+
+
+/**
+ * If the given account is feasible, add it to the array
+ * of accounts we return.
+ *
+ * @param cls a `struct PostReserveContext`
+ * @param payto_uri URI of the account
+ * @param conversion_url URL of a conversion service
+ * @param debit_restrictions restrictions for debits from account
+ * @param credit_restrictions restrictions for credits to account
+ * @param master_sig signature affirming the account
+ */
+static void
+add_matching_account (void *cls,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct ExchangeMatchContext *rc = cls;
+ char *method;
+
+ method = TALER_payto_get_method (payto_uri);
+ if ( (NULL == rc->wire_method) ||
+ (0 == strcmp (method,
+ rc->wire_method)) )
+ {
+ json_t *acc;
+
+ acc = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("payto_uri",
+ payto_uri),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
+ GNUNET_JSON_pack_array_incref ("credit_restrictions",
+ (json_t *) credit_restrictions)
+ );
+ GNUNET_assert (0 ==
+ json_array_append_new (rc->accounts,
+ acc));
+ }
+ GNUNET_free (method);
+}
+
+
+/**
+ * Return JSON array with all of the exchange accounts
+ * that support the given @a wire_method.
+ *
+ * @param master_pub master public key to match exchange by
+ * @param wire_method NULL for any
+ * @return JSON array with information about all matching accounts
+ */
+json_t *
+TMH_exchange_accounts_by_method (
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *wire_method)
+{
+ struct ExchangeMatchContext emc = {
+ .wire_method = wire_method,
+ .accounts = json_array ()
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ GNUNET_assert (NULL != emc.accounts);
+ qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
+ master_pub,
+ &add_matching_account,
+ &emc);
+ if (qs < 0)
+ {
+ json_decref (emc.accounts);
+ return NULL;
+ }
+ return emc.accounts;
+}
diff --git a/src/backend/taler-merchant-httpd_helper.h b/src/backend/taler-merchant-httpd_helper.h
index 930186d3..3b64c04b 100644
--- a/src/backend/taler-merchant-httpd_helper.h
+++ b/src/backend/taler-merchant-httpd_helper.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2021 Taler Systems SA
+ Copyright (C) 2021-2023 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
@@ -187,4 +187,18 @@ TMH_trigger_webhook (const char *instance,
const json_t *args);
+/**
+ * Return JSON array with all of the exchange accounts
+ * that support the given @a wire_method.
+ *
+ * @param master_pub master public key to match exchange by
+ * @param wire_method NULL for any
+ * @return JSON array with information about all matching accounts
+ */
+json_t *
+TMH_exchange_accounts_by_method (
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *wire_method);
+
+
#endif
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
index d0fcfbc0..9ab10939 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
@@ -468,30 +468,23 @@ find_next_exchange (struct AbortContext *ac);
* passed back to the wallet).
*
* @param cls closure
- * @param hr HTTP response data
- * @param sign_key exchange key used to sign @a obj, or NULL
- * @param signature the actual signature, or NULL on error
+ * @param rr response data
*/
static void
refund_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_ExchangePublicKeyP *sign_key,
- const struct TALER_ExchangeSignatureP *signature)
+ const struct TALER_EXCHANGE_RefundResponse *rr)
{
struct RefundDetails *rd = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
struct AbortContext *ac = rd->ac;
- (void) sign_key;
- (void) signature;
rd->rh = NULL;
rd->http_status = hr->http_status;
rd->exchange_reply = json_incref ((json_t*) hr->reply);
if (MHD_HTTP_OK == hr->http_status)
{
- GNUNET_assert (NULL != sign_key);
- GNUNET_assert (NULL != signature);
- rd->exchange_pub = *sign_key;
- rd->exchange_sig = *signature;
+ rd->exchange_pub = rr->details.ok.exchange_pub;
+ rd->exchange_sig = rr->details.ok.exchange_sig;
}
ac->pending_at_ce--;
if (0 == ac->pending_at_ce)
@@ -504,10 +497,7 @@ refund_cb (void *cls,
*
* @param cls the `struct AbortContext`
* @param hr HTTP response details
- * @param payto_uri payto://-URI of the exchange
* @param exchange_handle NULL if exchange was not found to be acceptable
- * @param wire_fee current applicable fee for dealing with @a exchange_handle,
- * NULL if not available
* @param exchange_trusted true if this exchange is
* trusted by config
*/
@@ -515,13 +505,10 @@ static void
process_abort_with_exchange (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *exchange_handle,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct AbortContext *ac = cls;
- (void) payto_uri;
(void) exchange_trusted;
ac->fo = NULL;
GNUNET_assert (GNUNET_YES == ac->suspended);
@@ -613,8 +600,7 @@ find_next_exchange (struct AbortContext *ac)
{
ac->current_exchange = rdi->exchange_url;
ac->fo = TMH_EXCHANGES_find_exchange (ac->current_exchange,
- NULL,
- GNUNET_NO,
+ false,
&process_abort_with_exchange,
ac);
if (NULL == ac->fo)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index df9bd21e..55e345e6 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -805,9 +805,6 @@ deposit_get_callback (
* @param cls the `struct KycContext`
* @param hr HTTP response details
* @param exchange_handle NULL if exchange was not found to be acceptable
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable fee for dealing with @a exchange_handle,
- * NULL if not available
* @param exchange_trusted true if this exchange is
* trusted by config
*/
@@ -816,8 +813,6 @@ process_kyc_with_exchange (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *exchange_handle,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct KycContext *kc = cls;
@@ -948,8 +943,7 @@ check_kyc (struct PayContext *pc,
kc_tail,
kc);
kc->fo = TMH_EXCHANGES_find_exchange (kc->exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&process_kyc_with_exchange,
kc);
if (NULL == kc->fo)
@@ -1013,11 +1007,11 @@ handle_batch_deposit_ok (struct ExchangeGroup *eg,
it is possible to over-pay if two wallets literally make a concurrent
payment, as the earlier check for 'paid' is not in the same transaction
scope as this 'insert' operation. */
- GNUNET_assert (j < dr->details.success.num_signatures);
+ GNUNET_assert (j < dr->details.ok.num_signatures);
qs = TMH_db->insert_deposit (
TMH_db->cls,
pc->hc->instance->settings.id,
- dr->details.success.deposit_timestamp,
+ dr->details.ok.deposit_timestamp,
&pc->h_contract_terms,
&dc->cdd.coin_pub,
dc->exchange_url,
@@ -1026,8 +1020,8 @@ handle_batch_deposit_ok (struct ExchangeGroup *eg,
&dc->refund_fee,
&dc->wire_fee,
&pc->wm->h_wire,
- &dr->details.success.exchange_sigs[j++],
- dr->details.success.exchange_pub);
+ &dr->details.ok.exchange_sigs[j++],
+ dr->details.ok.exchange_pub);
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
TMH_db->rollback (TMH_db->cls);
@@ -1181,9 +1175,6 @@ batch_deposit_cb (
* @param cls the `struct ExchangeGroup`
* @param hr HTTP response details
* @param exchange_handle NULL if exchange was not found to be acceptable
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable fee for dealing with @a exchange_handle,
- * NULL if not available
* @param exchange_trusted true if this exchange is
* trusted by config
*/
@@ -1192,8 +1183,6 @@ process_pay_with_exchange (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *exchange_handle,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct ExchangeGroup *eg = cls;
@@ -1202,7 +1191,6 @@ process_pay_with_exchange (
const struct TALER_EXCHANGE_Keys *keys;
unsigned int group_size;
- (void) payto_uri;
eg->fo = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Processing payment with exchange %s\n",
@@ -1275,8 +1263,7 @@ process_pay_with_exchange (
Maybe the wallet has seen /keys that we missed. */
eg->tried_force_keys = true;
eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
- pc->wm->wire_method,
- GNUNET_YES,
+ true,
&process_pay_with_exchange,
eg);
if (NULL != eg->fo)
@@ -1316,8 +1303,7 @@ process_pay_with_exchange (
Maybe the wallet has seen auditors that we missed. */
eg->tried_force_keys = true;
eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
- pc->wm->wire_method,
- GNUNET_YES,
+ true,
&process_pay_with_exchange,
eg);
if (NULL != eg->fo)
@@ -1439,6 +1425,7 @@ AGE_FAIL:
for (unsigned int i = 0; i<pc->coins_cnt; i++)
{
struct DepositConfirmation *dc = &pc->dc[i];
+ enum GNUNET_GenericReturnValue ret;
if (dc->found_in_db)
continue;
@@ -1446,7 +1433,27 @@ AGE_FAIL:
eg->exchange_url))
continue;
cdds[i] = dc->cdd;
- dc->wire_fee = *wire_fee;
+ ret = TMH_EXCHANGES_lookup_wire_fee (dc->exchange_url,
+ pc->wm->wire_method,
+ &dc->wire_fee);
+ if (GNUNET_OK != ret)
+ {
+ enum TALER_ErrorCode ec;
+
+ ec = (GNUNET_NO == ret)
+ ? TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED
+ : TALER_EC_MERCHANT_GENERIC_EXCHANGE_WIRE_REQUEST_FAILED;
+ pc->pending_at_eg--;
+ GNUNET_break_op (0);
+ resume_pay_with_response (
+ pc,
+ TALER_ErrorCode_get_http_status_safe (ec),
+ TALER_MHD_MAKE_JSON_PACK (
+ TALER_JSON_pack_ec (ec),
+ GNUNET_JSON_pack_string ("wire_method",
+ pc->wm->wire_method)));
+ return;
+ }
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Initiating batch deposit with %u coins\n",
@@ -1509,8 +1516,7 @@ start_batch_deposits (struct PayContext *pc)
if (! have_coins)
continue; /* no coins left to deposit at this exchange */
eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
- pc->wm->wire_method,
- GNUNET_NO,
+ false,
&process_pay_with_exchange,
eg);
if (NULL == eg->fo)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
index 40c89712..766c8814 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
@@ -386,17 +386,14 @@ notify_refund_obtained (struct PostRefundData *prd)
* refund request to an exchange.
*
* @param cls a `struct CoinRefund`
- * @param hr HTTP response data
- * @param exchange_pub exchange key used to sign refund confirmation
- * @param exchange_sig exchange's signature over refund
+ * @param rr response data
*/
static void
refund_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_ExchangePublicKeyP *exchange_pub,
- const struct TALER_ExchangeSignatureP *exchange_sig)
+ const struct TALER_EXCHANGE_RefundResponse *rr)
{
struct CoinRefund *cr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
cr->rh = NULL;
cr->exchange_status = hr->http_status;
@@ -413,12 +410,12 @@ refund_cb (void *cls,
{
enum GNUNET_DB_QueryStatus qs;
- cr->exchange_pub = *exchange_pub;
- cr->exchange_sig = *exchange_sig;
+ cr->exchange_pub = rr->details.ok.exchange_pub;
+ cr->exchange_sig = rr->details.ok.exchange_sig;
qs = TMH_db->insert_refund_proof (TMH_db->cls,
cr->refund_serial,
- exchange_sig,
- exchange_pub);
+ &rr->details.ok.exchange_sig,
+ &rr->details.ok.exchange_pub);
if (0 >= qs)
{
/* generally, this is relatively harmless for the merchant, but let's at
@@ -443,23 +440,17 @@ refund_cb (void *cls,
* @param cls a `struct CoinRefund *`
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
exchange_found_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct CoinRefund *cr = cls;
struct PostRefundData *prd = cr->prd;
- (void) payto_uri;
- (void) wire_fee;
(void) exchange_trusted;
cr->fo = NULL;
if (NULL == hr)
@@ -725,8 +716,7 @@ TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh,
{
/* We need to talk to the exchange */
cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&exchange_found_cb,
cr);
}
diff --git a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
index 24bbba35..fb560de6 100644
--- a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
+++ b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
@@ -309,15 +309,14 @@ TMH_force_tip_pickup_resume ()
* planchet operation, resume HTTP processing.
*
* @param cls closure with a `struct PlanchetOperation *`
- * @param hr HTTP response data
- * @param blind_sig blind signature over the coin, NULL on error
+ * @param w2r response data
*/
static void
withdraw_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_BlindedDenominationSignature *blind_sig)
+ const struct TALER_EXCHANGE_Withdraw2Response *w2r)
{
struct PlanchetOperation *po = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &w2r->hr;
struct PickupContext *pc = po->pc;
enum GNUNET_DB_QueryStatus qs;
@@ -325,7 +324,7 @@ withdraw_cb (void *cls,
pc->po_tail,
po);
TMH_db->preflight (TMH_db->cls);
- if (NULL == blind_sig)
+ if (MHD_HTTP_OK != hr->http_status)
{
GNUNET_free (po);
stop_operations (pc);
@@ -344,7 +343,7 @@ withdraw_cb (void *cls,
qs = TMH_db->insert_pickup_blind_signature (TMH_db->cls,
&pc->pickup_id,
po->offset,
- blind_sig);
+ &w2r->details.ok.blind_sig);
GNUNET_free (po);
if (qs < 0)
{
@@ -380,16 +379,12 @@ withdraw_cb (void *cls,
* @param cls closure, with our `struct PlanchetOperation *`
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
do_withdraw (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct PlanchetOperation *po = cls;
@@ -463,8 +458,7 @@ try_withdraw (struct PickupContext *pc,
po->pd = *planchet;
po->offset = offset;
po->fo = TMH_EXCHANGES_find_exchange (exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&do_withdraw,
po);
GNUNET_assert (NULL != po->fo);
@@ -507,16 +501,12 @@ do_timeout (void *cls)
* @param cls closure, with our `struct PickupContext *`
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
compute_total_requested (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct PickupContext *pc = cls;
@@ -796,8 +786,7 @@ TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh,
&do_timeout,
pc);
pc->fo = TMH_EXCHANGES_find_exchange (exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&compute_total_requested,
pc);
GNUNET_free (exchange_url);
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
index e7ab0468..773415f8 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
@@ -494,7 +494,7 @@ exchange_check_cb (void *cls,
{
enum GNUNET_DB_QueryStatus qs;
- if (TALER_AML_NORMAL != ks->details.success.aml_status)
+ if (TALER_AML_NORMAL != ks->details.ok.aml_status)
{
GNUNET_assert (
0 ==
@@ -503,7 +503,7 @@ exchange_check_cb (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 (
"aml_status",
- ks->details.success.aml_status),
+ ks->details.ok.aml_status),
GNUNET_JSON_pack_string ("exchange_url",
ekr->exchange_url),
GNUNET_JSON_pack_string ("payto_uri",
@@ -514,11 +514,11 @@ exchange_check_cb (void *cls,
&ekr->h_wire,
ekr->exchange_url,
ekr->exchange_kyc_serial,
- &ks->details.success.exchange_sig,
- &ks->details.success.exchange_pub,
- ks->details.success.timestamp,
+ &ks->details.ok.exchange_sig,
+ &ks->details.ok.exchange_pub,
+ ks->details.ok.timestamp,
true, /* KYC OK */
- ks->details.success.aml_status);
+ ks->details.ok.aml_status);
if (qs < 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -651,24 +651,18 @@ exchange_check_cb (void *cls,
* @param cls closure with our `struct ExchangeKycRequest *`
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
kyc_with_exchange (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct ExchangeKycRequest *ekr = cls;
struct KycContext *kc = ekr->kc;
struct TALER_PaytoHashP h_payto;
- (void) payto_uri;
- (void) wire_fee;
(void) exchange_trusted;
ekr->fo = NULL;
if (MHD_HTTP_OK != hr->http_status)
@@ -755,8 +749,7 @@ kyc_status_cb (void *cls,
ekr->last_check = last_check;
ekr->kc = kc;
ekr->fo = TMH_EXCHANGES_find_exchange (exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&kyc_with_exchange,
ekr);
}
diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
index 87ebc44e..cff03c7e 100644
--- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
@@ -468,7 +468,7 @@ deposit_get_cb (void *cls,
qs = TMH_db->insert_deposit_to_transfer (TMH_db->cls,
tq->deposit_serial,
- &dr->details.success);
+ &dr->details.ok);
if (qs < 0)
{
gorc_report (gorc,
@@ -487,7 +487,7 @@ deposit_get_cb (void *cls,
if (0 >
TALER_amount_add (&gorc->deposits_total,
&gorc->deposits_total,
- &dr->details.success.coin_contribution))
+ &dr->details.ok.coin_contribution))
{
gorc_report (gorc,
TALER_EC_MERCHANT_PRIVATE_GET_ORDERS_ID_AMOUNT_ARITHMETIC_FAILURE,
@@ -592,16 +592,12 @@ deposit_get_cb (void *cls,
* @param cls closure with a `struct GetOrderRequestContext *`
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
exchange_found_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct TransferQuery *tq = cls;
@@ -697,8 +693,7 @@ deposit_cb (void *cls,
tq->amount_with_fee = *amount_with_fee;
tq->deposit_fee = *deposit_fee;
tq->fo = TMH_EXCHANGES_find_exchange (exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&exchange_found_cb,
tq);
if (NULL == tq->fo)
diff --git a/src/backend/taler-merchant-httpd_private-get-reserves-ID.c b/src/backend/taler-merchant-httpd_private-get-reserves-ID.c
index 5545b02f..80d52399 100644
--- a/src/backend/taler-merchant-httpd_private-get-reserves-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-reserves-ID.c
@@ -25,6 +25,7 @@
#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_mhd.h"
#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_helper.h"
#include "taler-merchant-httpd_private-get-reserves-ID.h"
@@ -64,7 +65,6 @@ struct GetReserveContext
* picked up yet
* @param active true if the reserve is still active (we have the private key)
* @param exchange_url URL of the exchange, NULL if not active
- * @param payto_uri payto:// URI to fill the reserve, NULL if not active or already paid
* @param tips_length length of the @a tips array
* @param tips information about the tips created by this reserve
*/
@@ -77,13 +77,14 @@ handle_reserve_details (void *cls,
const struct TALER_Amount *picked_up_amount,
const struct TALER_Amount *committed_amount,
bool active,
+ const struct TALER_MasterPublicKeyP *master_pub,
const char *exchange_url,
- const char *payto_uri,
unsigned int tips_length,
const struct TALER_MERCHANTDB_TipDetails *tips)
{
struct GetReserveContext *ctx = cls;
json_t *tips_json;
+ json_t *accounts;
if (NULL != tips)
{
@@ -107,6 +108,8 @@ handle_reserve_details (void *cls,
{
tips_json = NULL;
}
+ accounts = TMH_exchange_accounts_by_method (master_pub,
+ NULL);
ctx->res = TALER_MHD_REPLY_JSON_PACK (
ctx->connection,
MHD_HTTP_OK,
@@ -131,8 +134,8 @@ handle_reserve_details (void *cls,
GNUNET_JSON_pack_string ("exchange_url",
exchange_url)),
GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("payto_uri",
- payto_uri)));
+ GNUNET_JSON_pack_array_steal ("accounts",
+ accounts)));
}
diff --git a/src/backend/taler-merchant-httpd_private-post-reserves.c b/src/backend/taler-merchant-httpd_private-post-reserves.c
index 82fc865f..a6008f32 100644
--- a/src/backend/taler-merchant-httpd_private-post-reserves.c
+++ b/src/backend/taler-merchant-httpd_private-post-reserves.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2021, 2022 Taler Systems SA
+ (C) 2021-2023 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
@@ -16,7 +16,6 @@
License along with TALER; see the file COPYING. If not,
see <http://www.gnu.org/licenses/>
*/
-
/**
* @file taler-merchant-httpd_private-post-reserves.c
* @brief implementing POST /reserves request handling
@@ -25,6 +24,7 @@
#include "platform.h"
#include "taler-merchant-httpd_exchanges.h"
#include "taler-merchant-httpd_private-post-reserves.h"
+#include "taler-merchant-httpd_helper.h"
#include "taler-merchant-httpd_reserves.h"
#include <taler/taler_json_lib.h>
@@ -74,9 +74,14 @@ struct PostReserveContext
const char *exchange_url;
/**
- * URI of the exchange where the payment needs to be made to.
+ * Wire method the client wants to use for the payment.
+ */
+ const char *wire_method;
+
+ /**
+ * Array of accounts that could be used.
*/
- char *payto_uri;
+ json_t *accounts;
/**
* Handle for contacting the exchange.
@@ -89,6 +94,12 @@ struct PostReserveContext
struct GNUNET_SCHEDULER_Task *timeout_task;
/**
+ * Master public key of the exchange matching
+ * @e exchange_url.
+ */
+ struct TALER_MasterPublicKeyP master_pub;
+
+ /**
* Initial balance of the reserve.
*/
struct TALER_Amount initial_balance;
@@ -187,7 +198,7 @@ reserve_context_cleanup (void *cls)
rc->timeout_task = NULL;
}
GNUNET_assert (GNUNET_YES != rc->suspended);
- GNUNET_free (rc->payto_uri);
+ json_decref (rc->accounts);
GNUNET_free (rc);
}
@@ -198,17 +209,13 @@ reserve_context_cleanup (void *cls)
*
* @param cls closure with our `struct PostReserveContext *`
* @param hr HTTP response details
- * @param payto_uri URI of the exchange for the wire transfer, NULL on errors
* @param eh handle to the exchange context
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
handle_exchange (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct PostReserveContext *rc = cls;
@@ -222,6 +229,7 @@ handle_exchange (void *cls,
}
rc->suspended = GNUNET_NO;
MHD_resume_connection (rc->connection);
+ TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
GNUNET_CONTAINER_DLL_remove (rc_head,
rc_tail,
rc);
@@ -229,42 +237,47 @@ handle_exchange (void *cls,
{
rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT;
rc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
return;
}
if (NULL == eh)
{
rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE;
rc->http_status = MHD_HTTP_BAD_GATEWAY;
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- keys = TALER_EXCHANGE_get_keys (eh);
- if (NULL == keys)
- {
- rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE;
- rc->http_status = MHD_HTTP_BAD_GATEWAY;
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
return;
}
if (MHD_HTTP_OK != hr->http_status)
{
rc->ec = hr->ec;
rc->http_status = hr->http_status;
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
return;
}
- if (NULL == payto_uri)
+ keys = TALER_EXCHANGE_get_keys (eh);
+ if (NULL == keys)
{
- rc->ec = TALER_EC_MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD;
- rc->http_status = MHD_HTTP_CONFLICT;
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
+ rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE;
+ rc->http_status = MHD_HTTP_BAD_GATEWAY;
return;
}
+ rc->master_pub = keys->master_pub;
+ {
+ rc->accounts = TMH_exchange_accounts_by_method (
+ &keys->master_pub,
+ rc->wire_method);
+ if (NULL == rc->accounts)
+ {
+ rc->ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
+ rc->http_status = MHD_HTTP_CONFLICT;
+ return;
+ }
+ if (0 == json_array_size (rc->accounts))
+ {
+ rc->ec = TALER_EC_MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD;
+ rc->http_status = MHD_HTTP_CONFLICT;
+ return;
+ }
+ }
rc->reserve_expiration
= GNUNET_TIME_relative_to_timestamp (keys->reserve_closing_delay);
- rc->payto_uri = GNUNET_strdup (payto_uri);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
}
@@ -308,11 +321,12 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
GNUNET_assert (NULL != mi);
if (NULL == rc)
{
- const char *wire_method;
rc = GNUNET_new (struct PostReserveContext);
rc->connection = connection;
rc->hc = hc;
+ rc->accounts = json_array ();
+ GNUNET_assert (NULL != rc->accounts);
hc->ctx = rc;
hc->cc = &reserve_context_cleanup;
@@ -322,7 +336,7 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
GNUNET_JSON_spec_string ("exchange_url",
&rc->exchange_url),
GNUNET_JSON_spec_string ("wire_method",
- &wire_method),
+ &rc->wire_method),
TALER_JSON_spec_amount ("initial_balance",
TMH_currency,
&rc->initial_balance),
@@ -337,8 +351,7 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
: MHD_NO;
}
rc->fo = TMH_EXCHANGES_find_exchange (rc->exchange_url,
- wire_method,
- GNUNET_NO,
+ false,
&handle_exchange,
rc);
rc->timeout_task
@@ -354,15 +367,14 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
}
if (GNUNET_SYSERR == rc->suspended)
return MHD_NO; /* we are in shutdown */
-
- GNUNET_assert (GNUNET_NO == rc->suspended);
- if (NULL == rc->payto_uri)
+ if (TALER_EC_NONE != rc->ec)
{
return TALER_MHD_reply_with_error (connection,
rc->http_status,
rc->ec,
NULL);
}
+ GNUNET_assert (GNUNET_NO == rc->suspended);
{
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_ReservePrivateKeyP reserve_priv;
@@ -375,8 +387,8 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
mi->settings.id,
&reserve_priv,
&reserve_pub,
+ &rc->master_pub,
rc->exchange_url,
- rc->payto_uri,
&rc->initial_balance,
rc->reserve_expiration);
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
@@ -394,8 +406,8 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
MHD_HTTP_OK,
GNUNET_JSON_pack_data_auto ("reserve_pub",
&reserve_pub),
- GNUNET_JSON_pack_string ("payto_uri",
- rc->payto_uri));
+ GNUNET_JSON_pack_array_steal ("accounts",
+ rc->accounts));
}
}
diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c
index aa21c747..a57c5e3b 100644
--- a/src/backend/taler-merchant-httpd_private-post-transfers.c
+++ b/src/backend/taler-merchant-httpd_private-post-transfers.c
@@ -457,15 +457,14 @@ check_wire_fee (struct PostTransfersContext *ptc,
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
- * @param hr HTTP response details
- * @param td transfer data
+ * @param tgr response details
*/
static void
wire_transfer_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_EXCHANGE_TransferData *td)
+ const struct TALER_EXCHANGE_TransfersGetResponse *tgr)
{
struct PostTransfersContext *ptc = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &tgr->hr;
const char *instance_id = ptc->hc->instance->settings.id;
enum GNUNET_DB_QueryStatus qs;
@@ -503,7 +502,7 @@ wire_transfer_cb (void *cls,
ptc->exchange_url,
ptc->payto_uri,
&ptc->wtid,
- td);
+ &tgr->details.ok.td);
if (0 > qs)
{
/* Always report on DB error as well to enable diagnostics */
@@ -528,7 +527,7 @@ wire_transfer_cb (void *cls,
return;
}
if (0 !=
- TALER_amount_cmp (&td->total_amount,
+ TALER_amount_cmp (&tgr->details.ok.td.total_amount,
&ptc->amount))
{
resume_transfer_with_error (
@@ -553,21 +552,16 @@ wire_transfer_cb (void *cls,
* @param cls the `struct PostTransfersContext`
* @param hr HTTP response details
* @param eh NULL if exchange was not found to be acceptable
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee NULL (we did not specify a wire method)
* @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
*/
static void
process_transfer_with_exchange (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct PostTransfersContext *ptc = cls;
- (void) payto_uri;
(void) exchange_trusted;
ptc->fo = NULL;
if (NULL == hr)
@@ -899,8 +893,7 @@ download (struct PostTransfersContext *ptc)
ptc_tail,
ptc);
ptc->fo = TMH_EXCHANGES_find_exchange (ptc->exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&process_transfer_with_exchange,
ptc);
ptc->timeout_task
diff --git a/src/backend/taler-merchant-httpd_reserves.c b/src/backend/taler-merchant-httpd_reserves.c
index 50af145f..fec18440 100644
--- a/src/backend/taler-merchant-httpd_reserves.c
+++ b/src/backend/taler-merchant-httpd_reserves.c
@@ -232,16 +232,12 @@ reserve_cb (void *cls,
* @param cls closure
* @param hr HTTP response details
* @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
* @param exchange_trusted true if this exchange is trusted by config
*/
static void
find_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
bool exchange_trusted)
{
struct Reserve *r = cls;
@@ -277,8 +273,7 @@ try_now (void *cls)
r->tt = NULL;
r->fo = TMH_EXCHANGES_find_exchange (r->exchange_url,
- NULL,
- GNUNET_NO,
+ false,
&find_cb,
r);
if (NULL == r->fo)
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index c72ddd6f..5a3611e7 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -59,6 +59,9 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
pg_select_open_transfers.h pg_select_open_transfers.c \
pg_lookup_instances.h pg_lookup_instances.c \
pg_lookup_transfers.h pg_lookup_transfers.c \
+ pg_delete_exchange_accounts.h pg_delete_exchange_accounts.c \
+ pg_select_accounts_by_exchange.h pg_select_accounts_by_exchange.c \
+ pg_insert_exchange_account.h pg_insert_exchange_account.c \
plugin_merchantdb_postgres.c pg_helper.h
libtaler_plugin_merchantdb_postgres_la_LIBADD = \
$(LTLIBINTL)
diff --git a/src/backenddb/merchant-0005.sql b/src/backenddb/merchant-0005.sql
index 8124341b..5c01e55b 100644
--- a/src/backenddb/merchant-0005.sql
+++ b/src/backenddb/merchant-0005.sql
@@ -27,6 +27,12 @@ ALTER TABLE merchant_instances
COMMENT ON COLUMN merchant_instances.user_type
IS 'what type of user is this (individual or business)';
+-- Column makes no sense for multi-account exchanges. Instead, we should
+-- lookup the various accounts of the exchange (by the master_pub) and return
+-- all of them (with constraints).
+ALTER TABLE merchant_tip_reserve_keys
+ DROP COLUMN payto_uri,
+ ADD COLUMN master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32);
ALTER TABLE merchant_transfers
ADD COLUMN failed BOOLEAN NOT NULL DEFAULT FALSE,
@@ -55,5 +61,29 @@ COMMENT ON COLUMN merchant_accounts.credit_facade_credentials
COMMENT ON COLUMN merchant_accounts.last_bank_serial
IS 'Serial number of the bank of the last transaction we successfully imported';
+
+CREATE TABLE IF NOT EXISTS merchant_exchange_accounts
+ (mea_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
+ ,master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)
+ ,payto_uri VARCHAR NOT NULL
+ ,conversion_url VARCHAR
+ ,debit_restrictions VARCHAR NOT NULL
+ ,credit_restrictions VARCHAR NOT NULL
+ ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
+ );
+COMMENT ON TABLE merchant_exchange_accounts
+ IS 'Here we store which bank accounts the exchange uses and with which constraints';
+COMMENT ON COLUMN merchant_exchange_accounts.master_pub
+ IS 'Master public key of the exchange with these accounts';
+COMMENT ON COLUMN merchant_exchange_accounts.payto_uri
+ IS 'RFC 8905 URI of the exchange bank account';
+COMMENT ON COLUMN merchant_exchange_accounts.conversion_url
+ IS 'NULL if this account does not require currency conversion';
+COMMENT ON COLUMN merchant_exchange_accounts.debit_restrictions
+ IS 'JSON array with account restrictions';
+COMMENT ON COLUMN merchant_exchange_accounts.credit_restrictions
+ IS 'JSON array with account restrictions';
+
+
-- Complete transaction
COMMIT;
diff --git a/src/backenddb/pg_delete_exchange_accounts.c b/src/backenddb/pg_delete_exchange_accounts.c
new file mode 100644
index 00000000..7d8a5e48
--- /dev/null
+++ b/src/backenddb/pg_delete_exchange_accounts.c
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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 backenddb/pg_delete_exchange_accounts.c
+ * @brief Implementation of the delete_exchange_accounts function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_delete_exchange_accounts.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_delete_exchange_accounts (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ PREPARE (pg,
+ "delete_exchange_accounts",
+ "DELETE FROM merchant_exchange_accounts"
+ " WHERE master_pub=$1;");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "delete_exchange_accounts",
+ params);
+}
diff --git a/src/backenddb/pg_delete_exchange_accounts.h b/src/backenddb/pg_delete_exchange_accounts.h
new file mode 100644
index 00000000..da9013d3
--- /dev/null
+++ b/src/backenddb/pg_delete_exchange_accounts.h
@@ -0,0 +1,42 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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 backenddb/pg_delete_exchange_accounts.h
+ * @brief implementation of the delete_exchange_accounts function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_DELETE_EXCHANGE_ACCOUNTS_H
+#define PG_DELETE_EXCHANGE_ACCOUNTS_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Delete information about wire accounts of an exchange. (Used when we got new account data.)
+ *
+ * @param cls closure
+ * @param master_pub public key of the exchange
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_delete_exchange_accounts (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub);
+
+
+#endif
diff --git a/src/backenddb/pg_insert_exchange_account.c b/src/backenddb/pg_insert_exchange_account.c
new file mode 100644
index 00000000..4495ccc0
--- /dev/null
+++ b/src/backenddb/pg_insert_exchange_account.c
@@ -0,0 +1,66 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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 backenddb/pg_insert_exchange_account.c
+ * @brief Implementation of the insert_exchange_account function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_exchange_account.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_exchange_account (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_string (payto_uri),
+ NULL == conversion_url
+ ? GNUNET_PQ_query_param_null ()
+ : GNUNET_PQ_query_param_string (conversion_url),
+ TALER_PQ_query_param_json (debit_restrictions),
+ TALER_PQ_query_param_json (credit_restrictions),
+ GNUNET_PQ_query_param_auto_from_type (master_sig),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ PREPARE (pg,
+ "insert_exchange_account",
+ "INSERT INTO merchant_exchange_accounts"
+ "(master_pub"
+ ",payto_uri"
+ ",conversion_url"
+ ",debit_restrictions"
+ ",credit_restrictions"
+ ",master_sig)"
+ " VALUES ($1, $2, $3, $4, $5, $6);");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_exchange_account",
+ params);
+}
diff --git a/src/backenddb/pg_insert_exchange_account.h b/src/backenddb/pg_insert_exchange_account.h
new file mode 100644
index 00000000..acfcdba2
--- /dev/null
+++ b/src/backenddb/pg_insert_exchange_account.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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 backenddb/pg_insert_exchange_account.h
+ * @brief implementation of the insert_exchange_account function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_EXCHANGE_ACCOUNT_H
+#define PG_INSERT_EXCHANGE_ACCOUNT_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Insert information about a wire account of an exchange.
+ *
+ * @param cls closure
+ * @param master_pub public key of the exchange
+ * @param payto_uri URI of the bank account
+ * @param conversion_url conversion service, NULL if there is no conversion required
+ * @param debit_restrictions JSON array of debit restrictions on the account
+ * @param credit_restrictions JSON array of debit restrictions on the account
+ * @param master_sig signature affirming the account of the exchange
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_exchange_account (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig);
+
+
+#endif
diff --git a/src/backenddb/pg_select_accounts_by_exchange.c b/src/backenddb/pg_select_accounts_by_exchange.c
new file mode 100644
index 00000000..c7637031
--- /dev/null
+++ b/src/backenddb/pg_select_accounts_by_exchange.c
@@ -0,0 +1,146 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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 backenddb/pg_select_accounts_by_exchange.c
+ * @brief Implementation of the select_accounts_by_exchange function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_select_accounts_by_exchange.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #parse_accounts.
+ */
+struct SelectAccountContext
+{
+ /**
+ * Function to call on each result.
+ */
+ TALER_MERCHANTDB_ExchangeAccountCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Set to true on failure.
+ */
+ bool failed;
+};
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results about accounts.
+ *
+ * @param cls of type `struct SelectAccountContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+parse_accounts (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct SelectAccountContext *ctx = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ char *payto_uri;
+ char *conversion_url = NULL;
+ json_t *debit_restrictions;
+ json_t *credit_restrictions;
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+ &master_sig),
+ GNUNET_PQ_result_spec_string ("payto_uri",
+ &payto_uri),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("conversion_url",
+ &conversion_url),
+ NULL),
+ TALER_PQ_result_spec_json ("debit_restrictions",
+ &debit_restrictions),
+ TALER_PQ_result_spec_json ("credit_restrictions",
+ &credit_restrictions),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->failed = true;
+ return;
+ }
+ ctx->cb (ctx->cb_cls,
+ payto_uri,
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ &master_sig);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_accounts_by_exchange (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ TALER_MERCHANTDB_ExchangeAccountCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct SelectAccountContext ctx = {
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ check_connection (pg);
+ PREPARE (pg,
+ "select_exchange_accounts",
+ "SELECT"
+ " payto_uri"
+ ",conversion_url"
+ ",debit_restrictions"
+ ",credit_restrictions"
+ ",master_sig"
+ " FROM merchant_exchange_accounts"
+ " WHERE master_pub=$1;");
+ qs = GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "select_exchange_accounts",
+ params,
+ &parse_accounts,
+ &ctx);
+ if (ctx.failed)
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ return qs;
+}
diff --git a/src/backenddb/pg_select_accounts_by_exchange.h b/src/backenddb/pg_select_accounts_by_exchange.h
new file mode 100644
index 00000000..e22c1601
--- /dev/null
+++ b/src/backenddb/pg_select_accounts_by_exchange.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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 backenddb/pg_select_accounts_by_exchange.h
+ * @brief implementation of the select_accounts_by_exchange function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_ACCOUNTS_BY_EXCHANGE_H
+#define PG_SELECT_ACCOUNTS_BY_EXCHANGE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Return information about wire accounts of an exchange.
+ *
+ * @param cls closure
+ * @param master_pub public key of the exchange
+ * @param cb function to call on each account
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_accounts_by_exchange (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ TALER_MERCHANTDB_ExchangeAccountCallback cb,
+ void *cb_cls);
+
+#endif
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index 6040c9af..baaad568 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -37,6 +37,9 @@
#include "pg_update_wirewatch_progress.h"
#include "pg_select_wirewatch_accounts.h"
#include "pg_select_open_transfers.h"
+#include "pg_delete_exchange_accounts.h"
+#include "pg_select_accounts_by_exchange.h"
+#include "pg_insert_exchange_account.h"
/**
@@ -4553,8 +4556,8 @@ postgres_store_wire_fee_by_exchange (
* @param instance_id which instance is the reserve tied to
* @param reserve_priv which reserve is topped up or created
* @param reserve_pub which reserve is topped up or created
+ * @param master_pub master public key of the exchange
* @param exchange_url what URL is the exchange reachable at where the reserve is located
- * @param payto_uri URI to use to fund the reserve
* @param initial_balance how much money will be added to the reserve
* @param expiration when does the reserve expire?
* @return transaction status, usually
@@ -4565,8 +4568,8 @@ postgres_insert_reserve (void *cls,
const char *instance_id,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_MasterPublicKeyP *master_pub,
const char *exchange_url,
- const char *payto_uri,
const struct TALER_Amount *initial_balance,
struct GNUNET_TIME_Timestamp expiration)
{
@@ -4618,7 +4621,7 @@ RETRY:
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_auto_from_type (reserve_priv),
GNUNET_PQ_query_param_string (exchange_url),
- GNUNET_PQ_query_param_string (payto_uri),
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_end
};
@@ -5084,9 +5087,9 @@ postgres_lookup_reserve (void *cls,
struct TALER_Amount exchange_initial_balance;
struct TALER_Amount pickup_amount;
struct TALER_Amount committed_amount;
- uint8_t active;
+ struct TALER_MasterPublicKeyP master_pub;
+ bool active;
char *exchange_url = NULL;
- char *payto_uri = NULL;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_timestamp ("creation_time",
&creation_time),
@@ -5100,16 +5103,14 @@ postgres_lookup_reserve (void *cls,
&pickup_amount),
TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
&committed_amount),
- GNUNET_PQ_result_spec_auto_from_type ("active",
- &active),
+ GNUNET_PQ_result_spec_auto_from_type ("master_pub",
+ &master_pub),
+ GNUNET_PQ_result_spec_bool ("active",
+ &active),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("exchange_url",
&exchange_url),
NULL),
- GNUNET_PQ_result_spec_allow_null (
- GNUNET_PQ_result_spec_string ("payto_uri",
- &payto_uri),
- NULL),
GNUNET_PQ_result_spec_end
};
enum GNUNET_DB_QueryStatus qs;
@@ -5130,9 +5131,9 @@ postgres_lookup_reserve (void *cls,
&exchange_initial_balance,
&pickup_amount,
&committed_amount,
- (0 != active),
+ active,
+ &master_pub,
exchange_url,
- payto_uri,
0,
NULL);
GNUNET_PQ_cleanup_result (rs);
@@ -5155,9 +5156,9 @@ postgres_lookup_reserve (void *cls,
&exchange_initial_balance,
&pickup_amount,
&committed_amount,
- 0 != active,
+ active,
+ &master_pub,
exchange_url,
- payto_uri,
ltc.tips_length,
ltc.tips);
}
@@ -9064,7 +9065,7 @@ postgres_connect (void *cls)
"(reserve_serial"
",reserve_priv"
",exchange_url"
- ",payto_uri"
+ ",master_pub"
")"
"SELECT reserve_serial, $3, $4, $5"
" FROM merchant_tip_reserves"
@@ -9123,7 +9124,7 @@ postgres_connect (void *cls)
",tips_picked_up_frac"
",reserve_priv IS NOT NULL AS active"
",exchange_url"
- ",payto_uri"
+ ",master_pub"
" FROM merchant_tip_reserves"
" FULL OUTER JOIN merchant_tip_reserve_keys USING (reserve_serial)"
" WHERE reserve_pub = $2"
@@ -9804,6 +9805,12 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
plugin->delete_pending_webhook = &postgres_delete_pending_webhook;
plugin->insert_pending_webhook = &postgres_insert_pending_webhook;
plugin->update_pending_webhook = &postgres_update_pending_webhook;
+ plugin->delete_exchange_accounts
+ = &TMH_PG_delete_exchange_accounts;
+ plugin->select_accounts_by_exchange
+ = &TMH_PG_select_accounts_by_exchange;
+ plugin->insert_exchange_account
+ = &TMH_PG_insert_exchange_account;
return plugin;
}
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
index 30f65a0f..1135cb38 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -3268,22 +3268,58 @@ TALER_MERCHANT_transfers_get_cancel (
struct TALER_MERCHANT_PostReservesHandle;
-// FIXME: change signature!
+/**
+ * Response to a POST /reserves request.
+ */
+struct TALER_MERCHANT_PostReservesResponse
+{
+ /**
+ * HTTP response details.
+ */
+ struct TALER_MERCHANT_HttpResponse hr;
+
+ /**
+ * Details depending on HTTP status.
+ */
+ union
+ {
+ /**
+ * Response on #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * Public key of the created reserve.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Accounts to credit to for filling the reserve.
+ * Array of accounts of the exchange.
+ */
+ const struct TALER_EXCHANGE_WireAccount *accounts;
+
+ /**
+ * Length of @e accounts array.
+ */
+ unsigned int accounts_len;
+
+ } ok;
+ } details;
+};
+
+
/**
* Callbacks of this type are used to work the result of submitting a
* POST /reserves request to a merchant
*
* @param cls closure
- * @param hr HTTP response details
- * @param reserve_pub public key of the created reserve, NULL on error
- * @param payto_uri where to make the payment to for filling the reserve, NULL on error
+ * @param prr response details
*/
typedef void
(*TALER_MERCHANT_PostReservesCallback) (
void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const char *payto_uri);
+ const struct TALER_MERCHANT_PostReservesResponse *prr);
/**
@@ -3455,29 +3491,82 @@ struct TALER_MERCHANT_TipDetails
};
-// FIXME: change signature!
+/**
+ * Response to a GET /reserves/$ID request.
+ */
+struct TALER_MERCHANT_ReserveGetResponse
+{
+ /**
+ * HTTP response.
+ */
+ struct TALER_MERCHANT_HttpResponse hr;
+
+ /**
+ * Details depending on HTTP status.
+ */
+ union
+ {
+
+ /**
+ * Details on #MHD_HTTP_OK.
+ */
+ struct
+ {
+
+ /**
+ * reserve summary for the reserve
+ */
+ struct TALER_MERCHANT_ReserveSummary rs;
+
+ /**
+ * URL of the exchange hosting the reserve, NULL if not @a active
+ */
+ const char *exchange_url;
+
+ /**
+ * Accounts to credit to for filling the reserve.
+ * Array of accounts of the exchange. Empty if
+ * already filled.
+ */
+ const struct TALER_EXCHANGE_WireAccount *accounts;
+
+ /**
+ * Length of @e accounts array.
+ */
+ unsigned int accounts_len;
+
+ /**
+ * Array with details about the tips granted.
+ */
+ const struct TALER_MERCHANT_TipDetails *tips;
+
+ /**
+ * Length of the @e tips array
+ */
+ unsigned int tips_length;
+
+ /**
+ * Is this reserve active (false if it was deleted but not purged)
+ */
+ bool active;
+
+ } ok;
+
+ } details;
+
+};
+
+
/**
* Callback to process a GET /reserve/$RESERVE_PUB request
*
* @param cls closure
- * @param hr HTTP response details
- * @param rs reserve summary for the reserve, NULL on error
- * @param active is this reserve active (false if it was deleted but not purged)
- * @param exchange_url URL of the exchange hosting the reserve, NULL if not @a active
- * @param payto_uri URI to fill the reserve, NULL if not @a active or already filled
- * @param tips_length length of the @a reserves array
- * @param tips array with details about the tips granted, NULL on error
+ * @param rgr response details
*/
typedef void
(*TALER_MERCHANT_ReserveGetCallback) (
void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_MERCHANT_ReserveSummary *rs,
- bool active,
- const char *exchange_url,
- const char *payto_uri,
- unsigned int tips_length,
- const struct TALER_MERCHANT_TipDetails tips[]);
+ const struct TALER_MERCHANT_ReserveGetResponse *rgr);
/**
diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h
index 0d191878..30609bae 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -802,6 +802,27 @@ typedef void
/**
+ * If the given account is feasible, add it to the array
+ * of accounts we return.
+ *
+ * @param cls closure
+ * @param payto_uri URI of the account
+ * @param conversion_url URL of a conversion service
+ * @param debit_restrictions restrictions for debits from account
+ * @param credit_restrictions restrictions for credits to account
+ * @param master_sig signature affirming the account
+ */
+typedef void
+(*TALER_MERCHANTDB_ExchangeAccountCallback) (
+ void *cls,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig);
+
+
+/**
* Callback with reserve details.
*
* @param cls closure
@@ -904,8 +925,8 @@ typedef void
* @param committed_amount total of tips that the merchant committed to, but that were not
* picked up yet
* @param active true if the reserve is still active (we have the private key)
+ * @param master_pub master public key of the exchange
* @param exchange_url base URL of the exchange hosting the reserve, NULL if not @a active
- * @param payto_uri URI to use to fund the reserve, NULL if not @a active
* @param tips_length length of the @a tips array
* @param tips information about the tips created by this reserve
*/
@@ -919,8 +940,8 @@ typedef void
const struct TALER_Amount *picked_up_amount,
const struct TALER_Amount *committed_amount,
bool active,
+ const struct TALER_MasterPublicKeyP *master_pub,
const char *exchange_url,
- const char *payto_uri,
unsigned int tips_length,
const struct TALER_MERCHANTDB_TipDetails *tips);
@@ -1122,7 +1143,6 @@ struct TALER_MERCHANTDB_Plugin
* Roll back the current transaction of a database connection.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
- * @return #GNUNET_OK on success
*/
void
(*rollback) (void *cls);
@@ -2391,23 +2411,76 @@ struct TALER_MERCHANTDB_Plugin
* including signature (so we have proof).
*
* @param cls closure
- * @param exchange_pub public key of the exchange
+ * @param master_pub master public key of the exchange
* @param h_wire_method hash of wire method
* @param fees wire fees charged
* @param start_date start of fee being used
* @param end_date end of fee being used
- * @param exchange_sig signature of exchange over fee structure
+ * @param master_sig signature of exchange over fee structure
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
(*store_wire_fee_by_exchange)(
void *cls,
- const struct TALER_MasterPublicKeyP *exchange_pub,
+ const struct TALER_MasterPublicKeyP *master_pub,
const struct GNUNET_HashCode *h_wire_method,
const struct TALER_WireFeeSet *fees,
struct GNUNET_TIME_Timestamp start_date,
struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_MasterSignatureP *exchange_sig);
+ const struct TALER_MasterSignatureP *master_sig);
+
+
+ /**
+ * Delete information about wire accounts of an exchange. (Used when we got new account data.)
+ *
+ * @param cls closure
+ * @param master_pub public key of the exchange
+ * @return transaction status code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*delete_exchange_accounts)(
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub);
+
+
+ /**
+ * Return information about wire accounts of an exchange.
+ *
+ * @param cls closure
+ * @param master_pub public key of the exchange
+ * @param cb function to call on each account
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*select_accounts_by_exchange)(
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ TALER_MERCHANTDB_ExchangeAccountCallback cb,
+ void *cb_cls);
+
+
+ /**
+ * Insert information about a wire account of an exchange.
+ *
+ * @param cls closure
+ * @param master_pub public key of the exchange
+ * @param payto_uri URI of the bank account
+ * @param conversion_url conversion service, NULL if there is no conversion required
+ * @param debit_restrictions JSON array of debit restrictions on the account
+ * @param credit_restrictions JSON array of debit restrictions on the account
+ * @param master_sig signature affirming the account of the exchange
+ * @return transaction status code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*insert_exchange_account)(
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig);
/**
@@ -2421,8 +2494,8 @@ struct TALER_MERCHANTDB_Plugin
* @param instance_id which instance is the reserve tied to
* @param reserve_priv which reserve is topped up or created
* @param reserve_pub which reserve is topped up or created
+ * @param master_pub master public key of the exchange
* @param exchange_url what URL is the exchange reachable at where the reserve is located
- * @param payto_uri URI to fund the reserve
* @param initial_balance how much money will be added to the reserve
* @param expiration when does the reserve expire?
* @return transaction status, usually
@@ -2433,8 +2506,8 @@ struct TALER_MERCHANTDB_Plugin
const char *instance_id,
const struct TALER_ReservePrivateKeyP *reserve_priv,
const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_MasterPublicKeyP *master_pub,
const char *exchange_url,
- const char *payto_uri,
const struct TALER_Amount *initial_balance,
struct GNUNET_TIME_Timestamp expiration);
diff --git a/src/lib/merchant_api_get_reserve.c b/src/lib/merchant_api_get_reserve.c
index 6c970db8..3df1dc75 100644
--- a/src/lib/merchant_api_get_reserve.c
+++ b/src/lib/merchant_api_get_reserve.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@@ -81,49 +81,59 @@ handle_reserve_get_finished (void *cls,
{
struct TALER_MERCHANT_ReserveGetHandle *rgh = cls;
const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_MERCHANT_ReserveGetResponse rgr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
- bool active;
rgh->job = NULL;
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
- struct TALER_MERCHANT_ReserveSummary rs;
- const json_t *tips;
- const char *exchange_url = NULL;
- const char *payto_uri = NULL;
+ struct TALER_MERCHANT_ReserveSummary *rs
+ = &rgr.details.ok.rs;
+ const json_t *tips = NULL;
+ const json_t *accounts = NULL;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_timestamp ("creation_time",
- &rs.creation_time),
+ &rs->creation_time),
GNUNET_JSON_spec_timestamp ("expiration_time",
- &rs.expiration_time),
+ &rs->expiration_time),
GNUNET_JSON_spec_bool ("active",
- &active),
+ &rgr.details.ok.active),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("exchange_url",
- &exchange_url),
+ GNUNET_JSON_spec_array_const ("tips",
+ &tips),
NULL),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("payto_uri",
- &payto_uri),
+ GNUNET_JSON_spec_array_const ("accounts",
+ &accounts),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string (
+ "exchange_url",
+ &rgr.details.ok.exchange_url),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("accounts",
+ &accounts),
NULL),
TALER_JSON_spec_amount_any ("merchant_initial_amount",
- &rs.merchant_initial_amount),
+ &rs->merchant_initial_amount),
TALER_JSON_spec_amount_any ("exchange_initial_amount",
- &rs.exchange_initial_amount),
+ &rs->exchange_initial_amount),
TALER_JSON_spec_amount_any ("pickup_amount",
- &rs.pickup_amount),
+ &rs->pickup_amount),
TALER_JSON_spec_amount_any ("committed_amount",
- &rs.committed_amount),
+ &rs->committed_amount),
GNUNET_JSON_spec_end ()
};
+ struct TALER_EXCHANGE_WireAccount *was = NULL;
+ struct TALER_MERCHANT_TipDetails *tds = NULL;
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
@@ -131,45 +141,43 @@ handle_reserve_get_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ rgr.hr.http_status = 0;
+ rgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- tips = json_object_get (json,
- "tips");
- if ((NULL == tips) ||
- json_is_null (tips))
- {
- rgh->cb (rgh->cb_cls,
- &hr,
- &rs,
- false,
- NULL,
- NULL,
- 0,
- NULL);
- TALER_MERCHANT_reserve_get_cancel (rgh);
- return;
- }
- if (! json_is_array (tips))
+ if (NULL != accounts)
{
- GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
- }
+ unsigned int accounts_len = json_array_size (accounts);
+
+ was = GNUNET_new_array (accounts_len,
+ struct TALER_EXCHANGE_WireAccount);
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_parse_accounts (NULL,
+ accounts,
+ was,
+ accounts_len))
+ {
+ GNUNET_break_op (0);
+ rgr.hr.http_status = 0;
+ rgr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
+ else
+ {
+ rgr.details.ok.accounts = was;
+ rgr.details.ok.accounts_len = accounts_len;
+ }
+ } /* end 'have accounts' */
+
+ if (NULL != tips)
{
- size_t tds_length;
+ size_t tds_length = json_array_size (tips);
+ bool ok = true;
json_t *tip;
- struct TALER_MERCHANT_TipDetails *tds;
unsigned int i;
- bool ok;
- tds_length = json_array_size (tips);
tds = GNUNET_new_array (tds_length,
struct TALER_MERCHANT_TipDetails);
- ok = true;
json_array_foreach (tips, i, tip) {
struct TALER_MERCHANT_TipDetails *td = &tds[i];
struct GNUNET_JSON_Specification ispec[] = {
@@ -192,60 +200,56 @@ handle_reserve_get_finished (void *cls,
break;
}
}
-
if (! ok)
{
GNUNET_break_op (0);
- GNUNET_free (tds);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- break;
+ rgr.hr.http_status = 0;
+ rgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ }
+ else
+ {
+ rgr.details.ok.tips = tds;
+ rgr.details.ok.tips_length = tds_length;
}
- rgh->cb (rgh->cb_cls,
- &hr,
- &rs,
- active,
- exchange_url,
- payto_uri,
- tds_length,
- tds);
- GNUNET_free (tds);
- TALER_MERCHANT_reserve_get_cancel (rgh);
- return;
+ } /* end 'have tips' */
+
+ rgh->cb (rgh->cb_cls,
+ &rgr);
+ if (NULL != accounts)
+ {
+ TALER_EXCHANGE_free_accounts (was,
+ json_array_size (accounts));
+ GNUNET_free (was);
}
+ GNUNET_free (tds);
+ TALER_MERCHANT_reserve_get_cancel (rgh);
+ return;
}
case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ rgr.hr.ec = TALER_JSON_get_error_code (json);
+ rgr.hr.hint = TALER_JSON_get_error_hint (json);
/* Nothing really to verify, merchant says we need to authenticate. */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ rgr.hr.ec = TALER_JSON_get_error_code (json);
+ rgr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
TALER_MERCHANT_parse_error_details_ (json,
response_code,
- &hr);
+ &rgr.hr);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d\n",
(unsigned int) response_code,
- (int) hr.ec);
- response_code = 0;
+ (int) rgr.hr.ec);
break;
}
rgh->cb (rgh->cb_cls,
- &hr,
- NULL,
- false,
- NULL,
- NULL,
- 0,
- NULL);
+ &rgr);
TALER_MERCHANT_reserve_get_cancel (rgh);
}
diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c
index 3923db21..c22e9ba5 100644
--- a/src/lib/merchant_api_post_order_pay.c
+++ b/src/lib/merchant_api_post_order_pay.c
@@ -190,27 +190,25 @@ check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
* validate the conflict error.
*
* @param cls a `struct TALER_MERCHANT_OrderPayHandle`
- * @param ehr reply from the exchange
- * @param keys the key structure
- * @param compat protocol compatibility indication
+ * @param kr reply from the exchange
*/
static void
cert_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *ehr,
- const struct TALER_EXCHANGE_Keys *keys,
- enum TALER_EXCHANGE_VersionCompatibility compat)
+ const struct TALER_EXCHANGE_KeysResponse *kr)
{
struct TALER_MERCHANT_OrderPayHandle *oph = cls;
+ const struct TALER_EXCHANGE_HttpResponse *ehr = &kr->hr;
- if (TALER_EXCHANGE_VC_INCOMPATIBLE & compat)
+ if ( (MHD_HTTP_OK != ehr->http_status) ||
+ (NULL == kr->details.ok.keys) )
{
struct TALER_MERCHANT_PayResponse pr = {
.hr.http_status = MHD_HTTP_CONFLICT,
- .hr.exchange_http_status = 0,
- .hr.ec = TALER_EC_WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
+ .hr.exchange_http_status = ehr->http_status,
+ .hr.ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
.hr.reply = oph->full_reply,
.hr.exchange_reply = ehr->reply,
- .hr.hint = "could not check error: incompatible exchange version"
+ .hr.hint = "failed to download /keys from the exchange"
};
oph->pay_cb (oph->pay_cb_cls,
@@ -218,16 +216,15 @@ cert_cb (void *cls,
TALER_MERCHANT_order_pay_cancel (oph);
return;
}
- if ( (MHD_HTTP_OK != ehr->http_status) ||
- (NULL == keys) )
+ if (TALER_EXCHANGE_VC_INCOMPATIBLE & kr->details.ok.compat)
{
struct TALER_MERCHANT_PayResponse pr = {
.hr.http_status = MHD_HTTP_CONFLICT,
- .hr.exchange_http_status = ehr->http_status,
- .hr.ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ .hr.exchange_http_status = 0,
+ .hr.ec = TALER_EC_WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
.hr.reply = oph->full_reply,
.hr.exchange_reply = ehr->reply,
- .hr.hint = "failed to download /keys from the exchange"
+ .hr.hint = "could not check error: incompatible exchange version"
};
oph->pay_cb (oph->pay_cb_cls,
@@ -238,7 +235,7 @@ cert_cb (void *cls,
if (GNUNET_OK !=
check_conflict (oph,
- keys))
+ kr->details.ok.keys))
{
struct TALER_MERCHANT_PayResponse pr = {
.hr.http_status = 0,
diff --git a/src/lib/merchant_api_post_reserves.c b/src/lib/merchant_api_post_reserves.c
index 65239dfb..a2822c26 100644
--- a/src/lib/merchant_api_post_reserves.c
+++ b/src/lib/merchant_api_post_reserves.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016, 2020 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@@ -86,26 +86,27 @@ handle_post_reserves_finished (void *cls,
{
struct TALER_MERCHANT_PostReservesHandle *prh = cls;
const json_t *json = response;
- struct TALER_MERCHANT_HttpResponse hr = {
- .http_status = (unsigned int) response_code,
- .reply = json
+ struct TALER_MERCHANT_PostReservesResponse prr = {
+ .hr.http_status = (unsigned int) response_code,
+ .hr.reply = json
};
prh->job = NULL;
switch (response_code)
{
case 0:
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ prr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
- const char *payto_uri;
- struct TALER_ReservePublicKeyP reserve_pub;
+ const json_t *accounts;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &reserve_pub),
- GNUNET_JSON_spec_string ("payto_uri",
- &payto_uri),
+ GNUNET_JSON_spec_fixed_auto (
+ "reserve_pub",
+ &prr.details.ok.reserve_pub),
+ GNUNET_JSON_spec_array_const (
+ "accounts",
+ &accounts),
GNUNET_JSON_spec_end ()
};
@@ -115,16 +116,31 @@ handle_post_reserves_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ prr.hr.http_status = 0;
+ prr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- else
+
{
+ unsigned int accounts_len = json_array_size (accounts);
+ struct TALER_EXCHANGE_WireAccount was[GNUNET_NZL (accounts_len)];
+
+ prr.details.ok.accounts = was;
+ prr.details.ok.accounts_len = accounts_len;
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_parse_accounts (NULL,
+ accounts,
+ was,
+ accounts_len))
+ {
+ GNUNET_break_op (0);
+ prr.hr.http_status = 0;
+ prr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+ }
prh->cb (prh->cb_cls,
- &hr,
- &reserve_pub,
- payto_uri);
+ &prr);
+ TALER_EXCHANGE_free_accounts (was,
+ accounts_len);
GNUNET_JSON_parse_free (spec);
TALER_MERCHANT_reserves_post_cancel (prh);
return;
@@ -133,8 +149,8 @@ handle_post_reserves_finished (void *cls,
case MHD_HTTP_ACCEPTED:
break;
case MHD_HTTP_UNAUTHORIZED:
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ prr.hr.ec = TALER_JSON_get_error_code (json);
+ prr.hr.hint = TALER_JSON_get_error_hint (json);
/* Nothing really to verify, merchant says we need to authenticate. */
break;
case MHD_HTTP_NOT_FOUND:
@@ -142,31 +158,29 @@ handle_post_reserves_finished (void *cls,
happen, we should pass the JSON reply to the application */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Did not find any data\n");
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ prr.hr.ec = TALER_JSON_get_error_code (json);
+ prr.hr.hint = TALER_JSON_get_error_hint (json);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- hr.ec = TALER_JSON_get_error_code (json);
- hr.hint = TALER_JSON_get_error_hint (json);
+ prr.hr.ec = TALER_JSON_get_error_code (json);
+ prr.hr.hint = TALER_JSON_get_error_hint (json);
break;
default:
/* unexpected response code */
GNUNET_break_op (0);
TALER_MERCHANT_parse_error_details_ (json,
response_code,
- &hr);
+ &prr.hr);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) prr.hr.ec);
break;
}
prh->cb (prh->cb_cls,
- &hr,
- NULL,
- NULL);
+ &prr);
TALER_MERCHANT_reserves_post_cancel (prh);
}
diff --git a/src/lib/merchant_api_tip_pickup.c b/src/lib/merchant_api_tip_pickup.c
index 593efa43..ba1256b1 100644
--- a/src/lib/merchant_api_tip_pickup.c
+++ b/src/lib/merchant_api_tip_pickup.c
@@ -312,7 +312,7 @@ csr_cb (void *cls,
{
struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[pd->off];
- pcd->exchange_vals = csrr->details.success.alg_values;
+ pcd->exchange_vals = csrr->details.ok.alg_values;
}
if (0 != tp->csr_active)
return;
diff --git a/src/merchant-tools/taler-merchant-setup-reserve.c b/src/merchant-tools/taler-merchant-setup-reserve.c
index 1ed50530..46888171 100644
--- a/src/merchant-tools/taler-merchant-setup-reserve.c
+++ b/src/merchant-tools/taler-merchant-setup-reserve.c
@@ -156,46 +156,81 @@ do_request (void *cls);
* POST /reserves request to a merchant
*
* @param cls closure
- * @param hr HTTP response details
- * @param reserve_pub public key of the created reserve, NULL on error
- * @param payto_uri where to make the payment to for filling the reserve, NULL on error
+ * @param prr response details
*/
static void
result_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const char *payto_uri)
+ const struct TALER_MERCHANT_PostReservesResponse *prr)
{
(void) cls;
prh = NULL;
- switch (hr->http_status)
+ switch (prr->hr.http_status)
{
case MHD_HTTP_OK:
{
- char res_str[sizeof (*reserve_pub) * 2 + 1];
+ char res_str[sizeof (prr->details.ok.reserve_pub) * 2 + 1];
- GNUNET_STRINGS_data_to_string (reserve_pub,
- sizeof (*reserve_pub),
+ GNUNET_STRINGS_data_to_string (&prr->details.ok.reserve_pub,
+ sizeof (prr->details.ok.reserve_pub),
res_str,
sizeof (res_str));
- if (NULL != strchr (payto_uri, '?'))
- fprintf (stdout,
- "%s&message=%s\n",
- payto_uri,
- res_str);
- else
- fprintf (stdout,
- "%s?message=%s\n",
- payto_uri,
- res_str);
+ for (unsigned int i = 0; i<prr->details.ok.accounts_len; i++)
+ {
+ const struct TALER_EXCHANGE_WireAccount *wa
+ = &prr->details.ok.accounts[i];
+ const char *payto_uri = wa->payto_uri;
+ bool skip = false;
+
+ for (unsigned int j = 0; j<wa->credit_restrictions_length; j++)
+ if (TALER_EXCHANGE_AR_DENY ==
+ wa->credit_restrictions[j].type)
+ skip = true;
+ if (skip)
+ continue;
+ if (NULL != strchr (payto_uri, '?'))
+ fprintf (stdout,
+ "%s&message=%s\n",
+ payto_uri,
+ res_str);
+ else
+ fprintf (stdout,
+ "%s?message=%s\n",
+ payto_uri,
+ res_str);
+ if (NULL != wa->conversion_url)
+ fprintf (stdout,
+ "\tConversion needed: %s\n",
+ wa->conversion_url);
+ for (unsigned int j = 0; j<wa->credit_restrictions_length; j++)
+ {
+ const struct TALER_EXCHANGE_AccountRestriction *cr
+ = &wa->credit_restrictions[j];
+
+ switch (cr->type)
+ {
+ case TALER_EXCHANGE_AR_INVALID:
+ GNUNET_assert (0);
+ break;
+ case TALER_EXCHANGE_AR_DENY:
+ GNUNET_assert (0);
+ break;
+ case TALER_EXCHANGE_AR_REGEX:
+ fprintf (stdout,
+ "\tCredit restriction: %s (%s)\n",
+ cr->details.regex.human_hint,
+ cr->details.regex.posix_egrep);
+ break;
+ }
+ }
+ }
}
break;
case MHD_HTTP_CONFLICT:
fprintf (stderr,
"Conflict trying to setup reserve: %u/%d\nHint: %s\n",
- hr->http_status,
- (int) hr->ec,
- hr->hint);
+ prr->hr.http_status,
+ (int) prr->hr.ec,
+ prr->hr.hint);
global_ret = 1;
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
@@ -212,16 +247,16 @@ result_cb (void *cls,
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Merchant failed too often (%u/%d), giving up\n",
- hr->http_status,
- hr->ec);
+ prr->hr.http_status,
+ prr->hr.ec);
global_ret = 1;
break;
default:
fprintf (stderr,
"Unexpected backend failure: %u/%d\nHint: %s\n",
- hr->http_status,
- (int) hr->ec,
- hr->hint);
+ prr->hr.http_status,
+ (int) prr->hr.ec,
+ prr->hr.hint);
global_ret = 1;
break;
}
diff --git a/src/testing/testing_api_cmd_get_reserve.c b/src/testing/testing_api_cmd_get_reserve.c
index ef7f67e0..db6f2562 100644
--- a/src/testing/testing_api_cmd_get_reserve.c
+++ b/src/testing/testing_api_cmd_get_reserve.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2020 Taler Systems SA
+ Copyright (C) 2020-2023 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
@@ -75,15 +75,10 @@ struct GetReserveState
static void
get_reserve_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_MERCHANT_ReserveSummary *rs,
- bool active,
- const char *exchange_url,
- const char *payto_uri,
- unsigned int tips_length,
- const struct TALER_MERCHANT_TipDetails tips[])
+ const struct TALER_MERCHANT_ReserveGetResponse *rgr)
{
struct GetReserveState *grs = cls;
+ const struct TALER_MERCHANT_HttpResponse *hr = &rgr->hr;
const struct TALER_TESTING_Command *reserve_cmd;
reserve_cmd = TALER_TESTING_interpreter_lookup_command (
@@ -105,6 +100,7 @@ get_reserve_cb (void *cls,
{
case MHD_HTTP_OK:
{
+ const struct TALER_MERCHANT_ReserveSummary *rs = &rgr->details.ok.rs;
const struct TALER_Amount *initial_amount;
if (GNUNET_OK !=
TALER_TESTING_get_trait_amount (reserve_cmd,
@@ -122,71 +118,76 @@ get_reserve_cb (void *cls,
return;
}
}
- if (tips_length != grs->tips_length)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Number of tips authorized does not match\n");
- TALER_TESTING_interpreter_fail (grs->is);
- return;
- }
- for (unsigned int i = 0; i < tips_length; ++i)
- {
- const struct TALER_TESTING_Command *tip_cmd;
+ unsigned int tips_length = rgr->details.ok.tips_length;
+ const struct TALER_MERCHANT_TipDetails *tips = rgr->details.ok.tips;
- tip_cmd = TALER_TESTING_interpreter_lookup_command (grs->is,
- grs->tips[i]);
+ if (tips_length != grs->tips_length)
{
- const struct TALER_TipIdentifierP *tip_id;
-
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_tip_id (tip_cmd,
- &tip_id))
- TALER_TESTING_interpreter_fail (grs->is);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Number of tips authorized does not match\n");
+ TALER_TESTING_interpreter_fail (grs->is);
+ return;
+ }
+ for (unsigned int i = 0; i < tips_length; ++i)
+ {
+ const struct TALER_TESTING_Command *tip_cmd;
- if (0 != GNUNET_memcmp (&tips[i].tip_id,
- tip_id))
+ tip_cmd = TALER_TESTING_interpreter_lookup_command (grs->is,
+ grs->tips[i]);
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Reserve tip id does not match\n");
- TALER_TESTING_interpreter_fail (grs->is);
- return;
+ const struct TALER_TipIdentifierP *tip_id;
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_tip_id (tip_cmd,
+ &tip_id))
+ TALER_TESTING_interpreter_fail (grs->is);
+
+ if (0 != GNUNET_memcmp (&tips[i].tip_id,
+ tip_id))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Reserve tip id does not match\n");
+ TALER_TESTING_interpreter_fail (grs->is);
+ return;
+ }
}
- }
- {
- const struct TALER_Amount *total_amount;
-
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_amount (tip_cmd,
- &total_amount))
- TALER_TESTING_interpreter_fail (grs->is);
-
- if ((GNUNET_OK !=
- TALER_amount_cmp_currency (&tips[i].amount,
- total_amount)) ||
- (0 != TALER_amount_cmp (&tips[i].amount,
- total_amount)))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Reserve tip amount does not match\n");
- TALER_TESTING_interpreter_fail (grs->is);
- return;
+ const struct TALER_Amount *total_amount;
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_amount (tip_cmd,
+ &total_amount))
+ TALER_TESTING_interpreter_fail (grs->is);
+
+ if ((GNUNET_OK !=
+ TALER_amount_cmp_currency (&tips[i].amount,
+ total_amount)) ||
+ (0 != TALER_amount_cmp (&tips[i].amount,
+ total_amount)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Reserve tip amount does not match\n");
+ TALER_TESTING_interpreter_fail (grs->is);
+ return;
+ }
}
- }
- {
- const char **reason;
-
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_reason (tip_cmd,
- &reason))
- TALER_TESTING_interpreter_fail (grs->is);
-
- if (0 != strcmp (tips[i].reason,
- *reason))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Reserve tip reason does not match\n");
- TALER_TESTING_interpreter_fail (grs->is);
- return;
+ const char **reason;
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_reason (tip_cmd,
+ &reason))
+ TALER_TESTING_interpreter_fail (grs->is);
+
+ if (0 != strcmp (tips[i].reason,
+ *reason))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Reserve tip reason does not match\n");
+ TALER_TESTING_interpreter_fail (grs->is);
+ return;
+ }
}
}
}
diff --git a/src/testing/testing_api_cmd_post_reserves.c b/src/testing/testing_api_cmd_post_reserves.c
index b2167534..fe3de9ed 100644
--- a/src/testing/testing_api_cmd_post_reserves.c
+++ b/src/testing/testing_api_cmd_post_reserves.c
@@ -79,32 +79,29 @@ struct PostReservesState
* POST /reserves request to a merchant
*
* @param cls closure
- * @param hr HTTP response details
- * @param reserve_pub public key of the created reserve, NULL on error
- * @param payto_uri where to make the payment to for filling the reserve, NULL on error
+ * @param prr response details
*/
static void
post_reserves_cb (void *cls,
- const struct TALER_MERCHANT_HttpResponse *hr,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const char *payto_uri)
+ const struct TALER_MERCHANT_PostReservesResponse *prr)
{
struct PostReservesState *prs = cls;
prs->prh = NULL;
- if (prs->http_status != hr->http_status)
+ if (prs->http_status != prr->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
- hr->http_status,
- (int) hr->ec,
+ prr->hr.http_status,
+ (int) prr->hr.ec,
TALER_TESTING_interpreter_get_current_label (prs->is));
TALER_TESTING_interpreter_fail (prs->is);
return;
}
- switch (hr->http_status)
+ switch (prr->hr.http_status)
{
case MHD_HTTP_OK:
+ prs->reserve_pub = prr->details.ok.reserve_pub;
break;
case MHD_HTTP_ACCEPTED:
break;
@@ -116,9 +113,9 @@ post_reserves_cb (void *cls,
GNUNET_break (0);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Unhandled HTTP status %u for POST /reserves.\n",
- hr->http_status);
+ prr->hr.http_status);
+ break;
}
- prs->reserve_pub = *reserve_pub;
TALER_TESTING_interpreter_next (prs->is);
}
diff --git a/src/testing/testing_api_cmd_post_transfers.c b/src/testing/testing_api_cmd_post_transfers.c
index 9a01d2d5..b73c6b15 100644
--- a/src/testing/testing_api_cmd_post_transfers.c
+++ b/src/testing/testing_api_cmd_post_transfers.c
@@ -404,10 +404,10 @@ debit_cb (
TALER_TESTING_interpreter_fail (pts->is);
return;
}
- for (unsigned int i = 0; i<reply->details.success.details_length; i++)
+ for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
const struct TALER_BANK_DebitDetails *details
- = &reply->details.success.details[i];
+ = &reply->details.ok.details[i];
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Bank reports transfer of %s to %s\n",