commit ca30b4e11c847d6082555ae69628e5e169999be9
parent 1f75da20b85e367377c7bc7b60a7659014c609f2
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 9 Mar 2026 17:14:38 +0100
fix #11214
Diffstat:
5 files changed, 273 insertions(+), 74 deletions(-)
diff --git a/src/backenddb/merchant-0032.sql b/src/backenddb/merchant-0032.sql
@@ -46,80 +46,6 @@ COMMENT ON FUNCTION random_bytea(INT)
IS 'Create BYTEA of the given length and fill it with random bytes';
-CREATE FUNCTION base32_crockford(data BYTEA)
-RETURNS TEXT
-LANGUAGE plpgsql
-IMMUTABLE
-STRICT
-PARALLEL SAFE
-AS $$
-DECLARE
- alphabet TEXT := '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
- chars TEXT[] := '{}';
- bit_buf BIGINT := 0; -- sliding window of accumulated bits
- bit_count INT := 0; -- how many valid bits are in bit_buf
- b INT;
- i INT;
-BEGIN
- IF length(data) = 0 THEN
- RETURN '';
- END IF;
-
- -- Process each byte, emitting 5-bit groups as we go
- FOR i IN 0 .. length(data) - 1
- LOOP
- b := get_byte(data, i);
- bit_buf := (bit_buf << 8) | b;
- bit_count := bit_count + 8;
-
- WHILE bit_count >= 5
- LOOP
- bit_count := bit_count - 5;
- chars := array_append(
- chars,
- substr(alphabet, ((bit_buf >> bit_count) & 31) + 1, 1)
- );
- END LOOP;
- END LOOP;
-
- -- Flush any remaining bits (zero-padded to 5)
- IF bit_count > 0
- THEN
- chars := array_append(
- chars,
- substr(alphabet, ((bit_buf << (5 - bit_count)) & 31) + 1, 1)
- );
- END IF;
- RETURN array_to_string(chars, '');
-END;
-$$;
-COMMENT ON FUNCTION base32_crockford(BYTEA)
- IS 'Encodes binary data using Crockford Base32';
-
-
-CREATE FUNCTION uri_escape(input TEXT)
-RETURNS TEXT
-LANGUAGE sql
-IMMUTABLE
-PARALLEL SAFE
-AS $$
-SELECT COALESCE(string_agg(
- CASE
- WHEN chr(get_byte(b, i)) ~ '[A-Za-z0-9._~-]'
- THEN chr(get_byte(b, i))
- ELSE '%' || lpad(upper(to_hex(get_byte(b, i))), 2, '0')
- END,
- ''
-), '')
-FROM (
- SELECT convert_to(input, 'UTF8') AS b
-) s,
-generate_series(0, length(b) - 1) AS i;
-$$;
-COMMENT ON FUNCTION uri_escape(TEXT)
- IS 'Percent-encodes all characters except RFC 3986 unreserved chars: A-Z a-z 0-9 - . _ ~';
-
-
CREATE OR REPLACE PROCEDURE merchant_send_kyc_notification(
in_account_serial INT8
,in_exchange_url TEXT
diff --git a/src/backenddb/pg_base32_crockford.sql b/src/backenddb/pg_base32_crockford.sql
@@ -0,0 +1,66 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2026 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/>
+--
+
+DROP FUNCTION base32_crockford;
+CREATE FUNCTION base32_crockford(data BYTEA)
+RETURNS TEXT
+LANGUAGE plpgsql
+IMMUTABLE
+STRICT
+PARALLEL SAFE
+AS $$
+DECLARE
+ alphabet TEXT := '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
+ chars TEXT[] := '{}';
+ bit_buf BIGINT := 0; -- sliding window of accumulated bits
+ bit_count INT := 0; -- how many valid bits are in bit_buf
+ b INT;
+ i INT;
+BEGIN
+ IF length(data) = 0 THEN
+ RETURN '';
+ END IF;
+
+ -- Process each byte, emitting 5-bit groups as we go
+ FOR i IN 0 .. length(data) - 1
+ LOOP
+ b := get_byte(data, i);
+ bit_buf := (bit_buf << 8) | b;
+ bit_count := bit_count + 8;
+
+ WHILE bit_count >= 5
+ LOOP
+ bit_count := bit_count - 5;
+ chars := array_append(
+ chars,
+ substr(alphabet, ((bit_buf >> bit_count) & 31)::INT + 1, 1)
+ );
+ END LOOP;
+ END LOOP;
+
+ -- Flush any remaining bits (zero-padded to 5)
+ IF bit_count > 0
+ THEN
+ chars := array_append(
+ chars,
+ substr(alphabet, ((bit_buf << (5 - bit_count)) & 31)::INT + 1, 1)
+ );
+ END IF;
+ RETURN array_to_string(chars, '');
+END;
+$$;
+COMMENT ON FUNCTION base32_crockford(BYTEA)
+ IS 'Encodes binary data using Crockford Base32';
diff --git a/src/backenddb/pg_insert_deposit_confirmation.sql b/src/backenddb/pg_insert_deposit_confirmation.sql
@@ -0,0 +1,168 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2026 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/>
+--
+
+DROP FUNCTION IF EXISTS merchant_do_insert_deposit_confirmation;
+CREATE FUNCTION merchant_do_insert_deposit_confirmation (
+ IN in_instance_id TEXT,
+ IN in_h_contract_terms BYTEA,
+ IN in_deposit_timestamp INT8,
+ IN in_exchange_url TEXT,
+ IN in_total_without_fees taler_amount_currency,
+ IN in_wire_fee taler_amount_currency,
+ IN in_h_wire BYTEA,
+ IN in_exchange_sig BYTEA,
+ IN in_exchange_pub BYTEA,
+ IN in_wire_transfer_deadline INT8,
+ IN in_notify_arg_str TEXT,
+ OUT out_no_instance BOOL,
+ OUT out_no_order BOOL,
+ OUT out_no_account BOOL,
+ OUT out_no_signkey BOOL,
+ OUT out_conflict BOOL,
+ OUT out_deposit_confirmation_serial)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ my_merchant_id INT8;
+ my_order_serial INT8;
+ my_account_serial INT8;
+ my_signkey_serial INT8;
+ my_record RECORD;
+ my_bank_serial_id INT8;
+ my_credit_amount taler_amount_currency;
+BEGIN
+
+out_no_instance=TRUE;
+out_no_deposit=TRUE;
+out_no_account=TRUE;
+out_no_signkey=TRUE;
+out_conflict=FALSE;
+
+-- Which instance are we using?
+SELECT merchant_serial
+ INTO my_merchant_id
+ FROM merchant_instances
+ WHERE merchant_id=in_instance_id;
+IF NOT FOUND
+THEN
+ RETURN;
+END IF;
+out_no_instance=FALSE;
+
+SELECT account_serial
+ INTO my_account_serial
+ FROM merchant_accounts
+ WHERE merchant_serial=my_merchant_id
+ AND h_wire=in_h_wire;
+IF NOT FOUND
+THEN
+ RETURN;
+END IF;
+out_no_account=FALSE;
+
+SELECT account_serial
+ INTO my_account_serial
+ FROM merchant_exchange_signing_keys
+ WHERE exchange_pub=in_exchange_pub;
+ ORDER BY start_date DESC
+ LIMIT 1;
+IF NOT FOUND
+THEN
+ RETURN;
+END IF;
+out_no_signkey=FALSE;
+
+SELECT order_serial
+ INTO my_order_serial
+ FROM merchant_contract_terms
+ WHERE merchant_serial=my_merchant_id
+ AND h_contract_terms=in_h_contract_terms;
+IF NOT FOUND
+THEN
+ RETURN;
+END IF;
+out_no_order=FALSE;
+
+SELECT deposit_confirmation_serial
+ ,deposit_timestamp
+ ,exchange_url
+ ,total_without_fee
+ ,wire_fee
+ ,wire_transfer_deadline
+ ,account_serial
+ INTO my_record
+ FROM merchant_deposit_confirmations
+ WHERE order_serial=my_order_serial
+ AND exchange_url=in_exchange_url;
+IF NOT FOUND
+THEN
+ INSERT INTO merchant_deposit_confirmations
+ (order_serial
+ ,deposit_timestamp
+ ,exchange_url
+ ,total_without_fee
+ ,wire_fee
+ ,exchange_sig
+ ,wire_transfer_deadline
+ ,signkey_serial
+ ,account_serial
+ ) VALUES (
+ my_order_serial
+ ,in_deposit_timestamp
+ ,in_exchange_url
+ ,in_total_without_fee
+ ,in_wire_fee
+ ,in_exchange_sig
+ ,in_wire_transfer_deadline
+ ,my_signkey_serial
+ ,my_account_serial
+ ) RETURNING deposit_confirmation_serial
+ INTO out_deposit_confirmation_serial;
+ELSE
+ IF ( (in_deposit_timestamp != my_record.deposit_timestamp) ||
+ (in_wire_transfer_deadline != my_record.wire_transfer_deadline) ||
+ ((in_wire_fee).val != (my_record.wire_fee).val) ||
+ ((in_wire_frac).val != (my_record.wire_fee).frac) ||
+ (in_wire_transfer_deadline != my_record.wire_transfer_deadline) ||
+ (my_account_serial != my_record.account_serial) )
+ THEN
+ out_conflict = TRUE;
+ out_deposit_confirmation_serial = my_record.deposit_confirmation_serial;
+ RETURN;
+ END IF;
+ IF ( ((in_total_without_fee).val < (my_record.total_without_fee).val) ||
+ ( ((in_total_without_fee).val = (my_record.total_without_fee).val) &&
+ ((in_total_without_fee).frac <= (my_record.total_without_fee).frac) ) )
+ THEN
+ -- new amount smaller or did not change, do NOT update.
+ out_deposit_confirmation_serial = my_record.deposit_confirmation_serial;
+ RETURN;
+ END IF;
+
+ -- Same deposit, but total amount increased, store this!
+ UPDATE merchant_deposit_confirmations
+ SET total_without_fee = in_total_without_fee
+ ,exchange_sig = in_exchange_sig
+ ,signkey_serial = my_signkey_serial;
+ out_deposit_confirmation_serial = my_record.deposit_confirmation_serial;
+
+END IF;
+
+-- Do notify on TALER_DBEVENT_MERCHANT_NEW_WIRE_DEADLINE
+NOTIFY XFIXME,
+ in_notify_arg_str;
+
+END $$;
diff --git a/src/backenddb/pg_uri_excape.sql b/src/backenddb/pg_uri_excape.sql
@@ -0,0 +1,37 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2026 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/>
+--
+DROP FUNCTION uri_escape;
+CREATE FUNCTION uri_escape(input TEXT)
+RETURNS TEXT
+LANGUAGE sql
+IMMUTABLE
+PARALLEL SAFE
+AS $$
+SELECT COALESCE(string_agg(
+ CASE
+ WHEN chr(get_byte(b, i)) ~ '[A-Za-z0-9._~-]'
+ THEN chr(get_byte(b, i))
+ ELSE '%' || lpad(upper(to_hex(get_byte(b, i))), 2, '0')
+ END,
+ ''
+), '')
+FROM (
+ SELECT convert_to(input, 'UTF8') AS b
+) s,
+generate_series(0, length(b) - 1) AS i;
+$$;
+COMMENT ON FUNCTION uri_escape(TEXT)
+ IS 'Percent-encodes all characters except RFC 3986 unreserved chars: A-Z a-z 0-9 - . _ ~';
diff --git a/src/backenddb/procedures.sql.in b/src/backenddb/procedures.sql.in
@@ -35,6 +35,8 @@ SET search_path TO merchant;
#include "pg_increment_money_pots.sql"
#include "pg_account_kyc_get_status.sql"
#include "pg_insert_transfer.sql"
+#include "pg_insert_deposit_confirmation.sql"
+#include "pg_base32_crockford.sql"
DROP PROCEDURE IF EXISTS merchant_do_gc;
CREATE PROCEDURE merchant_do_gc(in_now INT8)