commit 90fce64110ceee3be4232c96959e2460dca1e0fa
parent de1442ff02889a71651446bff27c0099ec80254a
Author: Christian Grothoff <christian@grothoff.org>
Date: Thu, 29 Oct 2026 21:42:10 +0100
fix refund computation
Diffstat:
1 file changed, 87 insertions(+), 26 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c b/src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c
@@ -92,6 +92,13 @@ struct TransferQuery
struct TALER_Amount deposit_fee;
/**
+ * Total amount refunded so far for this coin in this order. The
+ * exchange only returns @e deposit_fee to us once this reaches
+ * @e amount_with_fee.
+ */
+ struct TALER_Amount refunded_total;
+
+ /**
* Public key of the coin this is about.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
@@ -519,6 +526,27 @@ TMH_force_gorc_resume (void)
/**
+ * Discard the list of deposits collected for @a gorc.
+ *
+ * @param[in,out] gorc order context to update
+ */
+static void
+clear_transfer_queries (struct GetOrderRequestContext *gorc)
+{
+ struct TransferQuery *tq;
+
+ while (NULL != (tq = gorc->tq_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
+ gorc->tq_tail,
+ tq);
+ GNUNET_free (tq->exchange_url);
+ GNUNET_free (tq);
+ }
+}
+
+
+/**
* We have received a trigger from the database
* that we should (possibly) resume the request.
*
@@ -559,17 +587,8 @@ static void
gorc_cleanup (void *cls)
{
struct GetOrderRequestContext *gorc = cls;
- struct TransferQuery *tq;
-
- while (NULL != (tq = gorc->tq_head))
- {
- GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
- gorc->tq_tail,
- tq);
- GNUNET_free (tq->exchange_url);
- GNUNET_free (tq);
- }
+ clear_transfer_queries (gorc);
if (NULL != gorc->contract_terms_json)
{
json_decref (gorc->contract_terms_json);
@@ -1385,6 +1404,17 @@ deposit_cb (
"Checking deposit status for coin %s (over %s)\n",
TALER_B2S (coin_pub),
TALER_amount2s (amount_with_fee));
+ if ( (GNUNET_OK !=
+ TALER_amount_cmp_currency (&gorc->deposit_fees_total,
+ amount_with_fee)) ||
+ (GNUNET_OK !=
+ TALER_amount_cmp_currency (&gorc->deposit_fees_total,
+ deposit_fee)) )
+ {
+ GNUNET_break (0);
+ gorc->deposit_currency_mismatch = true;
+ return;
+ }
gorc->last_payment
= GNUNET_TIME_timestamp_max (gorc->last_payment,
deposit_timestamp);
@@ -1399,6 +1429,13 @@ deposit_cb (
tq->h_wire = *h_wire;
tq->amount_with_fee = *amount_with_fee;
tq->deposit_fee = *deposit_fee;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (amount_with_fee->currency,
+ &tq->refunded_total));
+ GNUNET_assert (0 <=
+ TALER_amount_add (&gorc->deposit_fees_total,
+ &gorc->deposit_fees_total,
+ deposit_fee));
}
@@ -1416,7 +1453,9 @@ phase_check_deposits (struct GetOrderRequestContext *gorc)
/* amount must be always valid for paid orders */
GNUNET_assert (GNUNET_OK ==
TALER_amount_is_valid (&gorc->contract_amount));
-
+ /* We may be running again due to long-polling;
+ clear left-overs from previous iterations */
+ clear_transfer_queries (gorc);
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (gorc->contract_amount.currency,
&gorc->deposits_total));
@@ -1430,6 +1469,17 @@ phase_check_deposits (struct GetOrderRequestContext *gorc)
gorc->order_serial,
&deposit_cb,
gorc);
+ if (gorc->deposit_currency_mismatch)
+ {
+ GNUNET_break (0);
+ phase_end (gorc,
+ TALER_MHD_reply_with_error (
+ gorc->sc.con,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "deposits in different currency than original order price"));
+ return;
+ }
gorc->phase++;
}
@@ -1480,8 +1530,8 @@ process_refunds_cb (
timestamp),
GNUNET_JSON_pack_string ("reason",
reason))));
- /* For refunded coins, we are not charged deposit fees, so subtract those
- again */
+ /* Add up the refunds per coin; #phase_check_refunds() checks if
+ the total is high enough to void the deposit fee. */
for (struct TransferQuery *tq = gorc->tq_head;
NULL != tq;
tq = tq->next)
@@ -1496,17 +1546,17 @@ process_refunds_cb (
continue;
if (GNUNET_OK !=
TALER_amount_cmp_currency (
- &gorc->deposit_fees_total,
- &tq->deposit_fee))
+ &tq->refunded_total,
+ refund_amount))
{
gorc->refund_currency_mismatch = true;
return;
}
GNUNET_assert (
0 <=
- TALER_amount_add (&gorc->deposit_fees_refunded_total,
- &gorc->deposit_fees_refunded_total,
- &tq->deposit_fee));
+ TALER_amount_add (&tq->refunded_total,
+ &tq->refunded_total,
+ refund_amount));
}
if (GNUNET_OK !=
TALER_amount_cmp_currency (
@@ -1616,6 +1666,21 @@ phase_check_refunds (struct GetOrderRequestContext *gorc)
"refunds in different currency than original order price"));
return;
}
+ /* Exchange cancels deposit fees of fully refunded coins, check for that case */
+ for (struct TransferQuery *tq = gorc->tq_head;
+ NULL != tq;
+ tq = tq->next)
+ {
+ if (0 !=
+ TALER_amount_cmp (&tq->refunded_total,
+ &tq->amount_with_fee))
+ continue; /* not refunded in full, we still owe the deposit fee */
+ GNUNET_assert (
+ 0 <=
+ TALER_amount_add (&gorc->deposit_fees_refunded_total,
+ &gorc->deposit_fees_refunded_total,
+ &tq->deposit_fee));
+ }
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Total refunds are %s\n",
TALER_amount2s (&gorc->refund_amount));
@@ -1683,15 +1748,14 @@ process_transfer_details (
return;
}
- /* Compute total amount *wired* */
+ /* Compute total amount *wired*. Note that @e deposit_fees_total is
+ deliberately not touched here: it covers every coin of the order,
+ while this callback only sees the coins whose wire transfer we
+ already know about. */
GNUNET_assert (0 <=
TALER_amount_add (&gorc->deposits_total,
&gorc->deposits_total,
deposit_value));
- GNUNET_assert (0 <=
- TALER_amount_add (&gorc->deposit_fees_total,
- &gorc->deposit_fees_total,
- deposit_fee));
GNUNET_assert (0 <= TALER_amount_subtract (&wired,
deposit_value,
deposit_fee));
@@ -1733,9 +1797,6 @@ phase_check_local_transfers (struct GetOrderRequestContext *gorc)
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (gorc->contract_amount.currency,
&gorc->deposits_total));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (gorc->contract_amount.currency,
- &gorc->deposit_fees_total));
GNUNET_assert (NULL != gorc->wire_details);
/* We may be running again due to long-polling, clear state first */
json_array_clear (gorc->wire_details);