commit be36d19b930ada87ed724ba5452f8fd3131377ac
parent 5779a18b87620c4d62e3841c82879c42e099c697
Author: Florian Dold <dold@taler.net>
Date: Thu, 27 Aug 2026 14:08:23 +0200
merchant: release order claims and wake status long-polls
Diffstat:
3 files changed, 70 insertions(+), 35 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_get-orders-ORDER_ID.c b/src/backend/taler-merchant-httpd_get-orders-ORDER_ID.c
@@ -547,6 +547,11 @@ resume_by_event (void *cls,
: "not waiting for refund",
(int) extra_size,
(const char *) extra);
+ /* Return the current status after a relevant event. In particular, a
+ claim changes an order from unclaimed to claimed without changing its
+ unpaid HTTP response, so re-entering the long poll here would hide the
+ state transition until the original timeout. */
+ god->sc.long_poll_timeout = GNUNET_TIME_absolute_get ();
god->suspended = GNUNET_NO;
GNUNET_CONTAINER_DLL_remove (god_head,
god_tail,
@@ -602,12 +607,12 @@ phase_init (struct GetOrderData *god)
{
struct TMH_OrderPayEventP pay_eh = {
.header.size = htons (sizeof (pay_eh)),
- .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
+ .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
.merchant_pub = god->hc->instance->merchant_pub
};
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Subscribing to payments on %s\n",
+ "Subscribing to order status changes on %s\n",
god->order_id);
GNUNET_CRYPTO_hash (god->order_id,
strlen (god->order_id),
diff --git a/src/backenddb/insert_unclaim_signature.c b/src/backenddb/insert_unclaim_signature.c
@@ -63,16 +63,7 @@ TALER_MERCHANTDB_insert_unclaim_signature (
{
char nonce_str[sizeof (*nonce) * 2];
char *end;
- char *notify_str = get_notify_str (order_id,
- &pg->current_merchant_pub);
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (order_id),
- GNUNET_PQ_query_param_string (nonce_str),
- GNUNET_PQ_query_param_string (notify_str),
- GNUNET_PQ_query_param_auto_from_type (h_contract),
- GNUNET_PQ_query_param_auto_from_type (nsig),
- GNUNET_PQ_query_param_end
- };
+ char *notify_str;
bool found;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_bool ("out_found",
@@ -90,16 +81,29 @@ TALER_MERCHANTDB_insert_unclaim_signature (
sizeof (nonce_str));
GNUNET_assert (NULL != end);
*end = '\0';
+ notify_str = get_notify_str (order_id,
+ &pg->current_merchant_pub);
TMH_PQ_prepare_anon (pg,
"SELECT"
" out_found"
" FROM merchant_do_insert_unclaim_signature"
"($1, $2, $3, $4, $5);");
- qs = GNUNET_PQ_eval_prepared_singleton_select (
- pg->conn,
- "",
- params,
- rs);
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (order_id),
+ GNUNET_PQ_query_param_string (nonce_str),
+ GNUNET_PQ_query_param_string (notify_str),
+ GNUNET_PQ_query_param_auto_from_type (h_contract),
+ GNUNET_PQ_query_param_auto_from_type (nsig),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (
+ pg->conn,
+ "",
+ params,
+ rs);
+ }
GNUNET_free (notify_str);
if (qs <= 0)
return qs;
diff --git a/src/backenddb/insert_unclaim_signature.sql b/src/backenddb/insert_unclaim_signature.sql
@@ -27,19 +27,47 @@ LANGUAGE plpgsql
AS $$
DECLARE
my_expiration_time INT8;
+ my_order_serial INT8;
BEGIN
SELECT pay_deadline
+ ,order_serial
INTO my_expiration_time
- FROM merchant_contract_terms
- WHERE order_id=in_order_id
- AND contract_terms->>'nonce' = in_nonce_str;
+ ,my_order_serial
+ FROM merchant_contract_terms AS mct
+ WHERE mct.order_id=in_order_id
+ AND mct.contract_terms->>'nonce' = in_nonce_str
+ AND mct.h_contract_terms=in_h_contract_terms
+ AND NOT mct.paid
+ AND NOT EXISTS
+ (SELECT 1
+ FROM merchant_deposit_confirmations AS mdc
+ WHERE mdc.order_serial=mct.order_serial)
+ AND NOT EXISTS
+ (SELECT 1
+ FROM merchant_refunds AS mr
+ WHERE mr.order_serial=mct.order_serial)
+ AND NOT EXISTS
+ (SELECT 1
+ FROM merchant_order_token_blinded_sigs AS mots
+ WHERE mots.order_serial=mct.order_serial)
+ FOR UPDATE;
IF NOT FOUND
THEN
- -- FIXME: distinguish better between
- -- different "not found" cases (contract, unclaim sig exists)
- out_found = FALSE;
+ -- A repeated request after the contract row was removed is successful,
+ -- but an old proof must not release a contract claimed with a new nonce.
+ PERFORM FROM merchant_contract_terms
+ WHERE order_id = in_order_id;
+ IF FOUND
+ THEN
+ out_found = FALSE;
+ RETURN;
+ END IF;
+ PERFORM FROM merchant_unclaim_signatures
+ WHERE h_contract_terms = in_h_contract_terms
+ AND unclaim_sig = in_nonce_sig;
+ out_found = FOUND;
RETURN;
END IF;
@@ -53,21 +81,19 @@ BEGIN
,my_expiration_time)
ON CONFLICT DO NOTHING;
- IF FOUND
+ DELETE FROM merchant_contract_terms
+ WHERE order_serial=my_order_serial;
+ IF NOT FOUND
THEN
- out_found = TRUE;
-
- -- order status change notification
- EXECUTE FORMAT (
- 'NOTIFY %s'
- ,in_notify_str);
-
+ out_found = FALSE;
RETURN;
END IF;
- PERFORM FROM merchant_unclaim_signatures
- WHERE h_contract_terms = in_h_contract_terms
- AND unclaim_sig = in_nonce_sig;
- out_found = FOUND;
+ out_found = TRUE;
+
+ -- order status change notification
+ EXECUTE FORMAT (
+ 'NOTIFY %s'
+ ,in_notify_str);
END $$;