-- -- This file is part of TALER -- Copyright (C) 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 -- 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 -- CREATE OR REPLACE FUNCTION merchant_do_insert_transfer_details ( IN in_instance_id TEXT, IN in_exchange_url TEXT, IN in_payto_uri TEXT, IN in_wtid BYTEA, IN in_execution_time INT8, IN in_exchange_pub BYTEA, IN in_exchange_sig BYTEA, IN in_total_amount taler_amount_currency, IN in_wire_fee taler_amount_currency, IN ina_coin_values taler_amount_currency[], IN ina_deposit_fees taler_amount_currency[], IN ina_coin_pubs BYTEA[], IN ina_contract_terms BYTEA[], OUT out_no_instance BOOL, OUT out_no_account BOOL, OUT out_no_exchange BOOL, OUT out_duplicate BOOL, OUT out_conflict BOOL) LANGUAGE plpgsql AS $$ DECLARE my_merchant_id INT8; my_signkey_serial INT8; my_credit_serial INT8; my_affected_orders RECORD; i INT8; curs CURSOR (arg_coin_pub BYTEA) FOR SELECT mcon.deposit_confirmation_serial, mcon.order_serial FROM merchant_deposits dep JOIN merchant_deposit_confirmations mcon USING (deposit_confirmation_serial) WHERE dep.coin_pub=arg_coin_pub; ini_coin_pub BYTEA; ini_contract_term BYTEA; ini_coin_value taler_amount_currency; ini_deposit_fee taler_amount_currency; BEGIN -- 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 out_no_instance=TRUE; out_no_account=FALSE; out_no_exchange=FALSE; out_duplicate=FALSE; out_conflict=FALSE; RETURN; END IF; out_no_instance=FALSE; -- Determine account that was credited. SELECT credit_serial INTO my_credit_serial FROM merchant_transfers WHERE exchange_url=in_exchange_url AND wtid=in_wtid AND account_serial= (SELECT account_serial FROM merchant_accounts WHERE payto_uri=in_payto_uri AND exchange_url=in_exchange_url AND merchant_serial=my_merchant_id); IF NOT FOUND THEN out_no_account=TRUE; out_no_exchange=FALSE; out_duplicate=FALSE; out_conflict=FALSE; RETURN; END IF; out_no_account=FALSE; -- Find exchange sign key SELECT signkey_serial INTO my_signkey_serial FROM merchant_exchange_signing_keys WHERE exchange_pub=in_exchange_pub ORDER BY start_date DESC LIMIT 1; IF NOT FOUND THEN out_no_exchange=TRUE; out_conflict=FALSE; out_duplicate=FALSE; RETURN; END IF; out_no_exchange=FALSE; -- Add signature first, check for idempotent request INSERT INTO merchant_transfer_signatures (credit_serial ,signkey_serial ,credit_amount ,wire_fee ,execution_time ,exchange_sig) VALUES (my_credit_serial ,my_signkey_serial ,in_total_amount ,in_wire_fee ,in_execution_time ,in_exchange_sig) ON CONFLICT DO NOTHING; IF NOT FOUND THEN PERFORM 1 FROM merchant_transfer_signatures WHERE credit_serial=my_credit_serial AND signkey_serial=my_signkey_serial AND credit_amount=in_total_amount AND wire_fee=in_wire_fee AND execution_time=in_execution_time AND exchange_sig=in_exchange_sig; IF FOUND THEN -- duplicate case out_duplicate=TRUE; out_conflict=FALSE; RETURN; END IF; -- conflict case out_duplicate=FALSE; out_conflict=TRUE; RETURN; END IF; out_duplicate=FALSE; out_conflict=FALSE; FOR i IN 1..array_length(ina_coin_pubs,1) LOOP ini_coin_value=ina_coin_values[i]; ini_deposit_fee=ina_deposit_fees[i]; ini_coin_pub=ina_coin_pubs[i]; ini_contract_term=ina_contract_terms[i]; INSERT INTO merchant_transfer_to_coin (deposit_serial ,credit_serial ,offset_in_exchange_list ,exchange_deposit_value ,exchange_deposit_fee) SELECT dep.deposit_serial ,my_credit_serial ,i ,ini_coin_value ,ini_deposit_fee FROM merchant_deposits dep JOIN merchant_deposit_confirmations dcon USING (deposit_confirmation_serial) JOIN merchant_contract_terms cterm USING (order_serial) WHERE dep.coin_pub=ini_coin_pub AND cterm.h_contract_terms=ini_contract_term AND cterm.merchant_serial=my_merchant_id; RAISE NOTICE 'iterating over affected orders'; OPEN curs (arg_coin_pub:=ini_coin_pub); LOOP FETCH NEXT FROM curs INTO my_affected_orders; EXIT WHEN NOT FOUND; RAISE NOTICE 'checking affected order for completion'; -- First, check if deposit confirmation is done. UPDATE merchant_deposit_confirmations SET wire_pending=FALSE WHERE (deposit_confirmation_serial=my_affected_orders.deposit_confirmation_serial) AND NOT EXISTS (SELECT 1 FROM merchant_deposits md LEFT JOIN merchant_deposit_to_transfer mdtt USING (deposit_serial) WHERE md.deposit_confirmation_serial=my_affected_orders.deposit_confirmation_serial AND mdtt.wtid IS NULL); -- wtid will be NULL due to LEFT JOIN -- if we do not have an entry in mdtt for the deposit -- and thus some entry in md was not yet wired. IF FOUND THEN -- Also update contract terms, if all (other) associated -- deposit_confirmations are also done. UPDATE merchant_contract_terms SET wired=TRUE WHERE (order_serial=my_affected_orders.order_serial) AND NOT EXISTS (SELECT 1 FROM merchant_deposit_confirmations mdc WHERE mdc.wire_pending AND mdc.order_serial=my_affected_orders.order_serial); END IF; END LOOP; -- END curs LOOP CLOSE curs; END LOOP; -- END FOR loop END $$;