[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant] branch master updated (84443a13 -> f0d362ff)
From: |
gnunet |
Subject: |
[taler-merchant] branch master updated (84443a13 -> f0d362ff) |
Date: |
Thu, 12 Sep 2024 15:37:32 +0200 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a change to branch master
in repository merchant.
from 84443a13 handle zero-amount orders better
new 90e34353 remove unused CONVERTER option (fixes #9126)
new f0d362ff enforce refund limits (#9061)
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
src/backend/taler-merchant-httpd_exchanges.c | 33 ++
src/backend/taler-merchant-httpd_exchanges.h | 16 +
...-merchant-httpd_private-post-orders-ID-refund.c | 42 +-
src/backenddb/pg_increase_refund.c | 423 +++++++++++++++------
src/backenddb/pg_increase_refund.h | 16 +-
src/backenddb/test_merchantdb.c | 22 +-
src/include/taler_merchantdb_plugin.h | 135 ++++---
src/testing/test_kyc_api.conf | 1 -
8 files changed, 509 insertions(+), 179 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_exchanges.c
b/src/backend/taler-merchant-httpd_exchanges.c
index 90649a7e..26c2f5cd 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -1100,6 +1100,39 @@ TMH_EXCHANGES_is_below_limit (
}
+void
+TMH_EXCHANGES_get_limit (
+ const char *exchange_url,
+ enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
+ struct TALER_Amount *amount)
+{
+ struct TMH_Exchange *exchange;
+ const struct TALER_EXCHANGE_Keys *keys;
+
+ exchange = lookup_exchange (exchange_url);
+ if ( (NULL == exchange) ||
+ (NULL == (keys = exchange->keys)) )
+ {
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (
+ amount->currency,
+ amount));
+ return;
+ }
+ for (unsigned int i = 0; i<keys->hard_limits_length; i++)
+ {
+ const struct TALER_EXCHANGE_AccountLimit *al
+ = &keys->hard_limits[i];
+
+ if (operation_type != al->operation_type)
+ continue;
+ TALER_amount_min (amount,
+ amount,
+ &al->threshold);
+ }
+}
+
+
enum GNUNET_GenericReturnValue
TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
diff --git a/src/backend/taler-merchant-httpd_exchanges.h
b/src/backend/taler-merchant-httpd_exchanges.h
index 5c70494c..7c11df61 100644
--- a/src/backend/taler-merchant-httpd_exchanges.h
+++ b/src/backend/taler-merchant-httpd_exchanges.h
@@ -220,4 +220,20 @@ TMH_test_exchange_configured_for_currency (
const char *currency);
+/**
+ * Determines the legal limit for a given @a operation_type
+ * at a given @a exchange_url.
+ *
+ * @param exchange_url base URL of the exchange to get
+ * the refund limit for
+ * @param operation_type type of operation
+ * @param[in,out] amount lowered to the maximum
+ * allowed at the exchange
+ */
+void
+TMH_EXCHANGES_get_limit (
+ const char *exchange_url,
+ enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
+ struct TALER_Amount *amount);
+
#endif
diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
index 1a7ffe37..a9f15405 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014-2023 Taler Systems SA
+ (C) 2014-2024 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
@@ -27,6 +27,7 @@
#include "taler-merchant-httpd_private-post-orders-ID-refund.h"
#include "taler-merchant-httpd_private-get-orders.h"
#include "taler-merchant-httpd_helper.h"
+#include "taler-merchant-httpd_exchanges.h"
/**
@@ -35,9 +36,6 @@
*/
#define MAX_RETRIES 5
-/* FIXME-9061: check exchange refund limits and return 403/
- MERCHANT_POST_ORDERS_ID_REFUND_EXCHANGE_TRANSACTION_LIMIT_VIOLATION
- if they are violated! */
/**
* Use database to notify other clients about the
@@ -108,6 +106,28 @@ make_taler_refund_uri (struct MHD_Connection *connection,
}
+/**
+ * Wrapper around #TMH_EXCHANGES_get_limit() that
+ * determines the refund limit for a given @a exchange_url
+ *
+ * @param cls unused
+ * @param exchange_url base URL of the exchange to get
+ * the refund limit for
+ * @param[in,out] amount lowered to the maximum refund
+ * allowed at the exchange
+ */
+static void
+get_refund_limit (void *cls,
+ const char *exchange_url,
+ struct TALER_Amount *amount)
+{
+ (void) cls;
+ TMH_EXCHANGES_get_limit (exchange_url,
+ TALER_KYCLOGIC_KYC_TRIGGER_REFUND,
+ amount);
+}
+
+
/**
* Handle request for increasing the refund associated with
* a contract.
@@ -252,6 +272,8 @@ TMH_private_post_orders_ID_refund (
hc->instance->settings.id,
hc->infix,
&refund,
+ &get_refund_limit,
+ NULL,
reason);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"increase refund returned %d\n",
@@ -315,6 +337,15 @@ TMH_private_post_orders_ID_refund (
switch (rs)
{
+ case TALER_MERCHANTDB_RS_LEGAL_FAILURE:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Refund amount %s exceeded legal limits of the exchanges
involved\n",
+ TALER_amount2s (&refund));
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
+
TALER_EC_MERCHANT_POST_ORDERS_ID_REFUND_EXCHANGE_TRANSACTION_LIMIT_VIOLATION,
+ NULL);
case TALER_MERCHANTDB_RS_BAD_CURRENCY:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Refund amount %s is not in the currency of the original
payment\n",
@@ -323,8 +354,7 @@ TMH_private_post_orders_ID_refund (
connection,
MHD_HTTP_CONFLICT,
TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- "Order was paid in a different currency")
- ;
+ "Order was paid in a different currency");
case TALER_MERCHANTDB_RS_TOO_HIGH:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Refusing refund amount %s that is larger than original
payment\n",
diff --git a/src/backenddb/pg_increase_refund.c
b/src/backenddb/pg_increase_refund.c
index eef7adc6..ed0b752e 100644
--- a/src/backenddb/pg_increase_refund.c
+++ b/src/backenddb/pg_increase_refund.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -26,6 +26,34 @@
#include "pg_helper.h"
+/**
+ * Information about refund limits per exchange.
+ */
+struct ExchangeLimit
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct ExchangeLimit *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct ExchangeLimit *prev;
+
+ /**
+ * Exchange the limit is about.
+ */
+ char *exchange_url;
+
+ /**
+ * Refund amount remaining at this exchange.
+ */
+ struct TALER_Amount remaining_refund_limit;
+
+};
+
+
/**
* Closure for #process_refund_cb().
*/
@@ -54,6 +82,176 @@ struct FindRefundContext
};
+/**
+ * Closure for #process_deposits_for_refund_cb().
+ */
+struct InsertRefundContext
+{
+ /**
+ * Used to provide a connection to the db
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Head of DLL of per-exchange refund limits.
+ */
+ struct ExchangeLimit *el_head;
+
+ /**
+ * Tail of DLL of per-exchange refund limits.
+ */
+ struct ExchangeLimit *el_tail;
+
+ /**
+ * Amount to which increase the refund for this contract
+ */
+ const struct TALER_Amount *refund;
+
+ /**
+ * Human-readable reason behind this refund
+ */
+ const char *reason;
+
+ /**
+ * Function to call to determine per-exchange limits.
+ * NULL for no limits.
+ */
+ TALER_MERCHANTDB_OperationLimitCallback olc;
+
+ /**
+ * Closure for @e olc.
+ */
+ void *olc_cls;
+
+ /**
+ * Transaction status code.
+ */
+ enum TALER_MERCHANTDB_RefundStatus rs;
+
+ /**
+ * Did we have to cap refunds of any coin
+ * due to legal limits?
+ */
+ bool legal_capped;
+};
+
+
+/**
+ * Data extracted per coin.
+ */
+struct RefundCoinData
+{
+
+ /**
+ * Public key of a coin.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Amount deposited for this coin.
+ */
+ struct TALER_Amount deposited_with_fee;
+
+ /**
+ * Amount refunded already for this coin.
+ */
+ struct TALER_Amount refund_amount;
+
+ /**
+ * Order serial (actually not really per-coin).
+ */
+ uint64_t order_serial;
+
+ /**
+ * Maximum rtransaction_id for this coin so far.
+ */
+ uint64_t max_rtransaction_id;
+
+ /**
+ * Exchange this coin was issued by.
+ */
+ char *exchange_url;
+
+};
+
+
+/**
+ * Find an exchange record for the refund limit enforcement.
+ *
+ * @param irc refund context
+ * @param exchange_url base URL of the exchange
+ */
+static struct ExchangeLimit *
+find_exchange (struct InsertRefundContext *irc,
+ const char *exchange_url)
+{
+ if (NULL == irc->olc)
+ return NULL; /* no limits */
+ /* Check if entry exists, if so, do nothing */
+ for (struct ExchangeLimit *el = irc->el_head;
+ NULL != el;
+ el = el->next)
+ if (0 == strcmp (exchange_url,
+ el->exchange_url))
+ return el;
+ return NULL;
+}
+
+
+/**
+ * Setup an exchange for the refund limit enforcement and initialize the
+ * original refund limit for the exchange.
+ *
+ * @param irc refund context
+ * @param exchange_url base URL of the exchange
+ * @return limiting data structure
+ */
+static struct ExchangeLimit *
+setup_exchange (struct InsertRefundContext *irc,
+ const char *exchange_url)
+{
+ struct ExchangeLimit *el;
+
+ if (NULL == irc->olc)
+ return NULL; /* no limits */
+ /* Check if entry exists, if so, do nothing */
+ if (NULL !=
+ (el = find_exchange (irc,
+ exchange_url)))
+ return el;
+ el = GNUNET_new (struct ExchangeLimit);
+ el->exchange_url = GNUNET_strdup (exchange_url);
+ /* olc only lowers, so set to the maximum amount we care about */
+ el->remaining_refund_limit = *irc->refund;
+ irc->olc (irc->olc_cls,
+ exchange_url,
+ &el->remaining_refund_limit);
+ GNUNET_CONTAINER_DLL_insert (irc->el_head,
+ irc->el_tail,
+ el);
+ return el;
+}
+
+
+/**
+ * Lower the remaining refund limit in @a el by @a val.
+ *
+ * @param[in,out] el exchange limit to lower
+ * @param val amount to lower limit by
+ * @return true on success, false on failure
+ */
+static bool
+lower_balance (struct ExchangeLimit *el,
+ const struct TALER_Amount *val)
+{
+ if (NULL == el)
+ return true;
+ return 0 <= TALER_amount_subtract (&el->remaining_refund_limit,
+ &el->remaining_refund_limit,
+ val);
+}
+
+
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results.
@@ -117,67 +315,6 @@ process_refund_cb (void *cls,
}
-/**
- * Closure for #process_deposits_for_refund_cb().
- */
-struct InsertRefundContext
-{
- /**
- * Used to provide a connection to the db
- */
- struct PostgresClosure *pg;
-
- /**
- * Amount to which increase the refund for this contract
- */
- const struct TALER_Amount *refund;
-
- /**
- * Human-readable reason behind this refund
- */
- const char *reason;
-
- /**
- * Transaction status code.
- */
- enum TALER_MERCHANTDB_RefundStatus rs;
-};
-
-
-/**
- * Data extracted per coin.
- */
-struct RefundCoinData
-{
-
- /**
- * Public key of a coin.
- */
- struct TALER_CoinSpendPublicKeyP coin_pub;
-
- /**
- * Amount deposited for this coin.
- */
- struct TALER_Amount deposited_with_fee;
-
- /**
- * Amount refunded already for this coin.
- */
- struct TALER_Amount refund_amount;
-
- /**
- * Order serial (actually not really per-coin).
- */
- uint64_t order_serial;
-
- /**
- * Maximum rtransaction_id for this coin so far.
- */
- uint64_t max_rtransaction_id;
-
-};
-
-
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results.
@@ -202,23 +339,29 @@ process_deposits_for_refund_cb (
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (ctx->refund->currency,
¤t_refund));
- memset (rcd, 0, sizeof (rcd));
+ memset (rcd,
+ 0,
+ sizeof (rcd));
/* Pass 1: Collect amount of existing refunds into current_refund.
* Also store existing refunded amount for each deposit in deposit_refund. */
for (unsigned int i = 0; i<num_results; i++)
{
+ struct RefundCoinData *rcdi = &rcd[i];
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &rcd[i].coin_pub),
+ &rcdi->coin_pub),
GNUNET_PQ_result_spec_uint64 ("order_serial",
- &rcd[i].order_serial),
+ &rcdi->order_serial),
+ GNUNET_PQ_result_spec_string ("exchange_url",
+ &rcdi->exchange_url),
TALER_PQ_result_spec_amount_with_currency ("amount_with_fee",
- &rcd[i].deposited_with_fee),
+ &rcdi->deposited_with_fee),
GNUNET_PQ_result_spec_end
};
struct FindRefundContext ictx = {
.pg = pg
};
+ struct ExchangeLimit *el;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
@@ -227,44 +370,47 @@ process_deposits_for_refund_cb (
{
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
}
-
- if (0 != strcmp (rcd[i].deposited_with_fee.currency,
+ el = setup_exchange (ctx,
+ rcdi->exchange_url);
+ if (0 != strcmp (rcdi->deposited_with_fee.currency,
ctx->refund->currency))
{
GNUNET_break_op (0);
ctx->rs = TALER_MERCHANTDB_RS_BAD_CURRENCY;
- return;
+ goto cleanup;
}
{
enum GNUNET_DB_QueryStatus ires;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
- GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
+ GNUNET_PQ_query_param_auto_from_type (&rcdi->coin_pub),
+ GNUNET_PQ_query_param_uint64 (&rcdi->order_serial),
GNUNET_PQ_query_param_end
};
GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (ctx->refund->currency,
- &ictx.refunded_amount));
- ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
- "find_refunds_by_coin",
- params,
- &process_refund_cb,
- &ictx);
+ TALER_amount_set_zero (
+ ctx->refund->currency,
+ &ictx.refunded_amount));
+ ires = GNUNET_PQ_eval_prepared_multi_select (
+ ctx->pg->conn,
+ "find_refunds_by_coin",
+ params,
+ &process_refund_cb,
+ &ictx);
if ( (ictx.err) ||
(GNUNET_DB_STATUS_HARD_ERROR == ires) )
{
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
}
if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
{
ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
- return;
+ goto cleanup;
}
}
if (0 >
@@ -274,15 +420,17 @@ process_deposits_for_refund_cb (
{
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
}
- rcd[i].refund_amount = ictx.refunded_amount;
- rcd[i].max_rtransaction_id = ictx.max_rtransaction_id;
+ rcdi->refund_amount = ictx.refunded_amount;
+ rcdi->max_rtransaction_id = ictx.max_rtransaction_id;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Existing refund for coin %s is %s\n",
- TALER_B2S (&rcd[i].coin_pub),
+ TALER_B2S (&rcdi->coin_pub),
TALER_amount2s (&ictx.refunded_amount));
- }
+ GNUNET_break (lower_balance (el,
+ &ictx.refunded_amount));
+ } /* end for all deposited coins */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Total existing refund is %s\n",
@@ -297,38 +445,50 @@ process_deposits_for_refund_cb (
"Existing refund of %s at or above requested refund. Finished
early.\n",
TALER_amount2s (¤t_refund));
ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
- return;
+ goto cleanup;
}
/* Phase 2: Try to increase current refund until it matches desired refund
*/
for (unsigned int i = 0; i<num_results; i++)
{
+ struct RefundCoinData *rcdi = &rcd[i];
const struct TALER_Amount *increment;
struct TALER_Amount left;
struct TALER_Amount remaining_refund;
+ struct ExchangeLimit *el;
/* How much of the coin is left after the existing refunds? */
if (0 >
TALER_amount_subtract (&left,
- &rcd[i].deposited_with_fee,
- &rcd[i].refund_amount))
+ &rcdi->deposited_with_fee,
+ &rcdi->refund_amount))
{
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
}
- if ( (0 == left.value) &&
- (0 == left.fraction) )
+ if (TALER_amount_is_zero (&left))
{
/* coin was fully refunded, move to next coin */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Coin %s fully refunded, moving to next coin\n",
- TALER_B2S (&rcd[i].coin_pub));
+ TALER_B2S (&rcdi->coin_pub));
+ continue;
+ }
+ el = find_exchange (ctx,
+ rcdi->exchange_url);
+ if ( (NULL != el) &&
+ (TALER_amount_is_zero (&el->remaining_refund_limit)) )
+ {
+ /* legal limit reached, move to next coin */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Exchange %s legal limit reached, moving to next coin\n",
+ rcdi->exchange_url);
continue;
}
- rcd[i].max_rtransaction_id++;
+ rcdi->max_rtransaction_id++;
/* How much of the refund is still to be paid back? */
if (0 >
TALER_amount_subtract (&remaining_refund,
@@ -337,9 +497,23 @@ process_deposits_for_refund_cb (
{
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
}
+ /* cap by legal limit */
+ if (NULL != el)
+ {
+ struct TALER_Amount new_limit;
+ TALER_amount_min (&new_limit,
+ &remaining_refund,
+ &el->remaining_refund_limit);
+ if (0 != TALER_amount_cmp (&new_limit,
+ &remaining_refund))
+ {
+ remaining_refund = new_limit;
+ ctx->legal_capped = true;
+ }
+ }
/* By how much will we increase the refund for this coin? */
if (0 >= TALER_amount_cmp (&remaining_refund,
&left))
@@ -359,25 +533,26 @@ process_deposits_for_refund_cb (
{
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
}
-
+ GNUNET_break (lower_balance (el,
+ increment));
/* actually run the refund */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Coin %s deposit amount is %s\n",
- TALER_B2S (&rcd[i].coin_pub),
- TALER_amount2s (&rcd[i].deposited_with_fee));
+ TALER_B2S (&rcdi->coin_pub),
+ TALER_amount2s (&rcdi->deposited_with_fee));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Coin %s refund will be incremented by %s\n",
- TALER_B2S (&rcd[i].coin_pub),
+ TALER_B2S (&rcdi->coin_pub),
TALER_amount2s (increment));
{
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
- GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already
inc'ed */
+ GNUNET_PQ_query_param_uint64 (&rcdi->order_serial),
+ GNUNET_PQ_query_param_uint64 (&rcdi->max_rtransaction_id), /* already
inc'ed */
GNUNET_PQ_query_param_timestamp (&now),
- GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&rcdi->coin_pub),
GNUNET_PQ_query_param_string (ctx->reason),
TALER_PQ_query_param_amount_with_currency (pg->conn,
increment),
@@ -393,10 +568,10 @@ process_deposits_for_refund_cb (
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
+ goto cleanup;
case GNUNET_DB_STATUS_SOFT_ERROR:
ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
- return;
+ goto cleanup;
default:
ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs;
break;
@@ -408,10 +583,15 @@ process_deposits_for_refund_cb (
¤t_refund))
{
ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
- return;
+ goto cleanup;
}
}
+ if (ctx->legal_capped)
+ {
+ ctx->rs = TALER_MERCHANTDB_RS_LEGAL_FAILURE;
+ goto cleanup;
+ }
/**
* We end up here if not all of the refund has been covered.
* Although this should be checked as the business should never
@@ -422,15 +602,21 @@ process_deposits_for_refund_cb (
"The refund of %s is bigger than the order's value\n",
TALER_amount2s (ctx->refund));
ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH;
+cleanup:
+ for (unsigned int i = 0; i<num_results; i++)
+ GNUNET_free (rcd[i].exchange_url);
}
enum TALER_MERCHANTDB_RefundStatus
-TMH_PG_increase_refund (void *cls,
- const char *instance_id,
- const char *order_id,
- const struct TALER_Amount *refund,
- const char *reason)
+TMH_PG_increase_refund (
+ void *cls,
+ const char *instance_id,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ TALER_MERCHANTDB_OperationLimitCallback olc,
+ void *olc_cls,
+ const char *reason)
{
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs;
@@ -442,6 +628,8 @@ TMH_PG_increase_refund (void *cls,
struct InsertRefundContext ctx = {
.pg = pg,
.refund = refund,
+ .olc = olc,
+ .olc_cls = olc_cls,
.reason = reason
};
@@ -470,6 +658,7 @@ TMH_PG_increase_refund (void *cls,
" dep.coin_pub"
",dco.order_serial"
",dep.amount_with_fee"
+ ",dco.exchange_url"
" FROM merchant_deposits dep"
" JOIN merchant_deposit_confirmations dco"
" USING (deposit_confirmation_serial)"
@@ -491,6 +680,18 @@ TMH_PG_increase_refund (void *cls,
params,
&process_deposits_for_refund_cb,
&ctx);
+ {
+ struct ExchangeLimit *el;
+
+ while (NULL != (el = ctx.el_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ctx.el_head,
+ ctx.el_tail,
+ el);
+ GNUNET_free (el->exchange_url);
+ GNUNET_free (el);
+ }
+ }
switch (qs)
{
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
diff --git a/src/backenddb/pg_increase_refund.h
b/src/backenddb/pg_increase_refund.h
index 0fe8a470..549d79be 100644
--- a/src/backenddb/pg_increase_refund.h
+++ b/src/backenddb/pg_increase_refund.h
@@ -36,6 +36,9 @@
* @param instance_id instance identifier
* @param order_id the order to increase the refund for
* @param refund maximum refund to return to the customer for this contract
+ * @param olc function to call to obtain legal refund
+ * limits per exchange, NULL for no limits
+ * @param olc_cls closure for @a olc
* @param reason 0-terminated UTF-8 string giving the reason why the customer
* got a refund (free form, business-specific)
* @return transaction status
@@ -46,11 +49,14 @@
* what was already refunded (idempotency!)
*/
enum TALER_MERCHANTDB_RefundStatus
-TMH_PG_increase_refund (void *cls,
- const char *instance_id,
- const char *order_id,
- const struct TALER_Amount *refund,
- const char *reason);
+TMH_PG_increase_refund (
+ void *cls,
+ const char *instance_id,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ TALER_MERCHANTDB_OperationLimitCallback olc,
+ void *olc_cls,
+ const char *reason);
#endif
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index f1d6e392..104a1d5d 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -5119,6 +5119,7 @@ run_test_refunds (struct TestRefunds_Closure *cls)
{
struct TALER_Amount inc;
uint64_t refund_serial;
+ bool legal_failure = false;
/* Insert an instance */
TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
@@ -5213,7 +5214,10 @@ run_test_refunds (struct TestRefunds_Closure *cls)
cls->instance.instance.id,
cls->orders[0].id,
&inc,
- "more"),
+ NULL,
+ NULL,
+ "more",
+ &legal_failure),
"Increase refund failed\n");
/* Test increase refund */
GNUNET_assert (GNUNET_OK ==
@@ -5224,7 +5228,10 @@ run_test_refunds (struct TestRefunds_Closure *cls)
cls->instance.instance.id,
cls->orders[0].id,
&inc,
- "more"),
+ NULL,
+ NULL,
+ "more",
+ &legal_failure),
"Increase refund failed\n");
/* Test lookup refund proof */
TEST_RET_ON_FAIL (test_lookup_refund_proof (1,
@@ -5252,7 +5259,10 @@ run_test_refunds (struct TestRefunds_Closure *cls)
cls->instance.instance.id,
cls->orders[1].id,
&inc,
- cls->refunds[1].reason),
+ NULL,
+ NULL,
+ cls->refunds[1].reason,
+ &legal_failure),
"Increase refund failed\n");
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:20.00",
@@ -5262,8 +5272,12 @@ run_test_refunds (struct TestRefunds_Closure *cls)
cls->instance.instance.id,
cls->orders[1].id,
&inc,
- cls->refunds[2].reason),
+ NULL,
+ NULL,
+ cls->refunds[2].reason,
+ &legal_failure),
"Increase refund failed\n");
+ GNUNET_assert (! legal_failure);
TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance,
&cls->deposits[2].
h_contract_terms,
diff --git a/src/include/taler_merchantdb_plugin.h
b/src/include/taler_merchantdb_plugin.h
index 0c301a0a..ff8793ce 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -292,6 +292,23 @@ typedef void
const struct TALER_MERCHANTDB_AccountDetails *ad);
+/**
+ * Determines the maximum @a amount for a particular
+ * type of operation for a given @a exchange_url.
+ *
+ * @param cls closure
+ * @param exchange_url base URL of the exchange to get
+ * the limit for
+ * @param[in,out] amount lowered to the maximum amount
+ * allowed at the exchange
+ */
+typedef void
+(*TALER_MERCHANTDB_OperationLimitCallback)(
+ void *cls,
+ const char *exchange_url,
+ struct TALER_Amount *amount);
+
+
/**
* Typically called by `lookup_products`.
*
@@ -881,6 +898,11 @@ typedef void
enum TALER_MERCHANTDB_RefundStatus
{
+ /**
+ * Refund amount exceeds legal exchange limits.
+ */
+ TALER_MERCHANTDB_RS_LEGAL_FAILURE = -5,
+
/**
* Refund amount currency does not match original payment.
*/
@@ -2601,6 +2623,9 @@ struct TALER_MERCHANTDB_Plugin
* @param instance_id instance identifier
* @param order_id the order to increase the refund for
* @param refund maximum refund to return to the customer for this contract
+ * @param olc function to call to obtain legal refund
+ * limits per exchange, NULL for no limits
+ * @param olc_cls closure for @a olc
* @param reason 0-terminated UTF-8 string giving the reason why the customer
* got a refund (free form, business-specific)
* @return transaction status
@@ -2611,11 +2636,14 @@ struct TALER_MERCHANTDB_Plugin
* what was already refunded (idempotency!)
*/
enum TALER_MERCHANTDB_RefundStatus
- (*increase_refund)(void *cls,
- const char *instance_id,
- const char *order_id,
- const struct TALER_Amount *refund,
- const char *reason);
+ (*increase_refund)(
+ void *cls,
+ const char *instance_id,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ TALER_MERCHANTDB_OperationLimitCallback olc,
+ void *olc_cls,
+ const char *reason);
/**
@@ -2646,10 +2674,11 @@ struct TALER_MERCHANTDB_Plugin
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
- (*insert_refund_proof)(void *cls,
- uint64_t refund_serial,
- const struct TALER_ExchangeSignatureP *exchange_sig,
- const struct TALER_ExchangePublicKeyP *exchange_pub);
+ (*insert_refund_proof)(
+ void *cls,
+ uint64_t refund_serial,
+ const struct TALER_ExchangeSignatureP *exchange_sig,
+ const struct TALER_ExchangePublicKeyP *exchange_pub);
/**
@@ -2664,14 +2693,14 @@ struct TALER_MERCHANTDB_Plugin
* @return database result code
*/
enum GNUNET_DB_QueryStatus
- (*insert_spent_token)(void *cls,
- const struct TALER_PrivateContractHashP *
- h_contract_terms,
- const struct TALER_TokenIssuePublicKeyHashP
*h_issue_pub
- ,
- const struct TALER_TokenUsePublicKeyP *use_pub,
- const struct TALER_TokenUseSignatureP *use_sig,
- const struct TALER_TokenIssueSignatureP *issue_sig);
+ (*insert_spent_token)(
+ void *cls,
+ const struct TALER_PrivateContractHashP *
+ h_contract_terms,
+ const struct TALER_TokenIssuePublicKeyHashP *h_issue_pub,
+ const struct TALER_TokenUsePublicKeyP *use_pub,
+ const struct TALER_TokenUseSignatureP *use_sig,
+ const struct TALER_TokenIssueSignatureP *issue_sig);
/**
@@ -2684,13 +2713,11 @@ struct TALER_MERCHANTDB_Plugin
* @return database result code
*/
enum GNUNET_DB_QueryStatus
- (*insert_issued_token) (void *cls,
- const struct TALER_PrivateContractHashP *
- h_contract_terms,
- const struct TALER_TokenIssuePublicKeyHashP *
- h_issue_pub,
- const struct TALER_TokenIssueBlindSignatureP *
- blind_sig);
+ (*insert_issued_token) (
+ void *cls,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct TALER_TokenIssuePublicKeyHashP *h_issue_pub,
+ const struct TALER_TokenIssueBlindSignatureP *blind_sig);
/**
@@ -2703,10 +2730,11 @@ struct TALER_MERCHANTDB_Plugin
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
- (*lookup_refund_proof)(void *cls,
- uint64_t refund_serial,
- struct TALER_ExchangeSignatureP *exchange_sig,
- struct TALER_ExchangePublicKeyP *exchange_pub);
+ (*lookup_refund_proof)(
+ void *cls,
+ uint64_t refund_serial,
+ struct TALER_ExchangeSignatureP *exchange_sig,
+ struct TALER_ExchangePublicKeyP *exchange_pub);
/**
@@ -2723,12 +2751,13 @@ struct TALER_MERCHANTDB_Plugin
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
- (*lookup_order_by_fulfillment)(void *cls,
- const char *instance_id,
- const char *fulfillment_url,
- const char *session_id,
- bool allow_refunded_for_repurchase,
- char **order_id);
+ (*lookup_order_by_fulfillment)(
+ void *cls,
+ const char *instance_id,
+ const char *fulfillment_url,
+ const char *session_id,
+ bool allow_refunded_for_repurchase,
+ char **order_id);
/**
* Update information about progress made by taler-merchant-wirewatch.
@@ -2872,14 +2901,15 @@ struct TALER_MERCHANTDB_Plugin
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
- (*lookup_wire_fee)(void *cls,
- const struct TALER_MasterPublicKeyP *master_pub,
- const char *wire_method,
- struct GNUNET_TIME_Timestamp contract_date,
- struct TALER_WireFeeSet *fees,
- struct GNUNET_TIME_Timestamp *start_date,
- struct GNUNET_TIME_Timestamp *end_date,
- struct TALER_MasterSignatureP *master_sig);
+ (*lookup_wire_fee)(
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *wire_method,
+ struct GNUNET_TIME_Timestamp contract_date,
+ struct TALER_WireFeeSet *fees,
+ struct GNUNET_TIME_Timestamp *start_date,
+ struct GNUNET_TIME_Timestamp *end_date,
+ struct TALER_MasterSignatureP *master_sig);
/**
@@ -3014,16 +3044,17 @@ struct TALER_MERCHANTDB_Plugin
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
- (*lookup_transfers)(void *cls,
- const char *instance_id,
- const char *payto_uri,
- struct GNUNET_TIME_Timestamp before,
- struct GNUNET_TIME_Timestamp after,
- int64_t limit,
- uint64_t offset,
- enum TALER_EXCHANGE_YesNoAll yna,
- TALER_MERCHANTDB_TransferCallback cb,
- void *cb_cls);
+ (*lookup_transfers)(
+ void *cls,
+ const char *instance_id,
+ const char *payto_uri,
+ struct GNUNET_TIME_Timestamp before,
+ struct GNUNET_TIME_Timestamp after,
+ int64_t limit,
+ uint64_t offset,
+ enum TALER_EXCHANGE_YesNoAll yna,
+ TALER_MERCHANTDB_TransferCallback cb,
+ void *cb_cls);
/**
diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf
index 6472d580..ff3a80e0 100644
--- a/src/testing/test_kyc_api.conf
+++ b/src/testing/test_kyc_api.conf
@@ -47,7 +47,6 @@ ENABLE_KYC = YES
[kyc-provider-test-oauth2]
LOGIC = oauth2
-CONVERTER = taler-exchange-helper-converter-oauth2-address
KYC_OAUTH2_VALIDITY = forever
KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token
KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-merchant] branch master updated (84443a13 -> f0d362ff),
gnunet <=