merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 20234ecc12e10c47dc9d775c7bdcefd78eb6d8ed
parent 29663c0c88a3b6b3a3cf2587393d1e3c713f4236
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Wed,  4 Dec 2024 20:00:45 +0100

tests added

Diffstat:
Msrc/backenddb/pg_insert_deposit_to_transfer.sql | 37+++++++++++--------------------------
Msrc/backenddb/pg_insert_transfer_details.sql | 33++++++++++++---------------------
Msrc/backenddb/test_webhook_trigger.sh | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 383 insertions(+), 52 deletions(-)

diff --git a/src/backenddb/pg_insert_deposit_to_transfer.sql b/src/backenddb/pg_insert_deposit_to_transfer.sql @@ -163,33 +163,18 @@ UPDATE merchant_contract_terms -- POSSIBLE LOCATION FOR THE WIRE WEBHOOK OF ORDER -- --- SELECT merchant_serial, order_id --- INTO my_merchant_serial, my_order_id --- FROM merchant_contract_terms --- WHERE order_serial=my_order_serial; --- --- PERFORM +-- INSERT INTO merchant_pending_webhooks +-- (merchant_serial +-- ,webhook_serial +-- ,url +-- ,http_method +-- ,body) +-- SELECT mw.merchant_serial +-- ,mw.webhook_serial +-- ,mw.url +-- ,mw.http_method +-- ,json_build_object('order_id', my_order_id)::TEXT -- FROM merchant_webhook mw -- WHERE mw.event_type = 'order_settled' -- AND mw.merchant_serial = my_merchant_serial; --- --- IF FOUND --- THEN --- -- Insert a pending webhook for the merchant --- INSERT INTO merchant_pending_webhooks --- (merchant_serial --- ,webhook_serial --- ,url --- ,http_method --- ,body) --- SELECT mw.merchant_serial --- ,mw.webhook_serial --- ,mw.url --- ,mw.http_method --- ,json_build_object('order_id', my_order_id)::TEXT --- FROM merchant_webhook mw --- WHERE mw.event_type = 'order_settled' --- AND mw.merchant_serial = my_merchant_serial; --- END IF; - END $$; diff --git a/src/backenddb/pg_insert_transfer_details.sql b/src/backenddb/pg_insert_transfer_details.sql @@ -229,31 +229,22 @@ LOOP FROM merchant_contract_terms WHERE order_serial=my_affected_orders.order_serial; - -- Check if a webhook exists for order settlement - PERFORM + -- Insert pending webhook + INSERT INTO merchant_pending_webhooks + (merchant_serial + ,webhook_serial + ,url + ,http_method + ,body) + SELECT mw.merchant_serial + ,mw.webhook_serial + ,mw.url + ,mw.http_method + ,json_build_object('order_id', my_order_id)::TEXT FROM merchant_webhook mw WHERE mw.event_type = 'order_settled' AND mw.merchant_serial = my_merchant_serial; - IF FOUND - THEN - -- Insert pending webhook - INSERT INTO merchant_pending_webhooks - (merchant_serial - ,webhook_serial - ,url - ,http_method - ,body) - SELECT mw.merchant_serial - ,mw.webhook_serial - ,mw.url - ,mw.http_method - ,json_build_object('order_id', my_order_id)::TEXT - FROM merchant_webhook mw - WHERE mw.event_type = 'order_settled' - AND mw.merchant_serial = my_merchant_serial; - END IF; - END IF; END LOOP; -- END curs LOOP diff --git a/src/backenddb/test_webhook_trigger.sh b/src/backenddb/test_webhook_trigger.sh @@ -2,8 +2,362 @@ set -eu taler-merchant-dbinit -r -c test-merchantdb-postgres.conf -echo "INSERT INTO foo VALUES (bar);" | psql talercheck -echo "UPDATE exchange.reserves_in SET credit.val=5 WHERE reserve_in_serial_id=1" \ - | psql -At "$DB" -SERIALE=$(echo "SELECT coin_deposit_serial_id FROM exchange.coin_deposits WHERE (amount_with_fee).val=3 ORDER BY coin_deposit_serial_id LIMIT 1;" | psql "$DB" -Aqt) -OLD_COIN_SIG=$(echo "SELECT coin_sig FROM exchange.coin_deposits WHERE coin_deposit_serial_id=${SERIALE};" | psql "$DB" -Aqt) + +# Generate random BYTEA values for testing +WTID=$(openssl rand -hex 32) +EXCHANGE_PUB=$(openssl rand -hex 32) +EXCHANGE_SIG=$(openssl rand -hex 64) +COIN_PUB=$(openssl rand -hex 32) + +# Generate valid BYTEA values +MERCHANT_PUB=$(openssl rand -hex 32) +AUTH_HASH=$(openssl rand -hex 64) +AUTH_SALT=$(openssl rand -hex 32) + +# Define other parameters +INSTANCE_ID="test-merchant" +EXCHANGE_URL="http://example.com/exchange" +PAYTO_URI="payto://example.com/account" +EXECUTION_TIME=$(date +%s) + +# Generate taler_amount_currency values +TOTAL_AMOUNT="ROW(100, 0, 'USD')::taler_amount_currency" +WIRE_FEE="ROW(1, 0, 'USD')::taler_amount_currency" + +# Generate arrays for taler_amount_currency and BYTEA +# Generate arrays for taler_amount_currency and BYTEA +COIN_VALUES="ARRAY[ROW(10, 0, 'USD')::taler_amount_currency, ROW(20, 0, 'USD')::taler_amount_currency, ROW(30, 0, 'USD')::taler_amount_currency]::taler_amount_currency[]" +DEPOSIT_FEES="ARRAY[ROW(1, 0, 'USD')::taler_amount_currency, ROW(2, 50000000, 'USD')::taler_amount_currency, ROW(3, 25000000, 'USD')::taler_amount_currency]::taler_amount_currency[]" +COIN_PUBS="ARRAY[decode('$COIN_PUB', 'hex'), decode('$(openssl rand -hex 32)', 'hex'), decode('$(openssl rand -hex 32)', 'hex')]::BYTEA[]" +CONTRACT_TERMS="ARRAY[decode('$(openssl rand -hex 32)', 'hex'), decode('$(openssl rand -hex 32)', 'hex'), decode('$(openssl rand -hex 32)', 'hex')]::BYTEA[]" + +# Create the merchant database +echo "SET search_path TO merchant; + INSERT INTO merchant_instances ( + merchant_serial, + merchant_name, + merchant_id, + address, + jurisdiction, + default_wire_transfer_delay, + default_pay_delay, + merchant_pub, + auth_hash, + auth_salt + ) VALUES ( + 1, + 'Test Merchant', + 'test-merchant', + '{"city":"TestCity"}', + '{"country":"TestCountry"}', + 3600, + 3600, + decode('$MERCHANT_PUB', 'hex'), + decode('$AUTH_HASH', 'hex'), + decode('$AUTH_SALT', 'hex') + );" | psql talercheck + +# Create the webhooks +echo "SET search_path TO merchant; + INSERT INTO merchant_webhook (merchant_serial, webhook_id, event_type, url, http_method, body_template) + VALUES + (1, 'category_added_hook', 'category_added', 'http://example.com/category_added', 'POST', '{"event":"{{ webhook_type }}"}'), + (1, 'category_updated_hook', 'category_updated', 'http://example.com/category_updated', 'POST', '{"event":"{{ webhook_type }}"}'), + (1, 'category_deleted_hook', 'category_deleted', 'http://example.com/category_deleted', 'POST', '{"event":"{{ webhook_type }}"}'), + (1, 'inventory_added_hook', 'inventory_added', 'http://example.com/inventory_added', 'POST', '{"event":"{{ webhook_type }}"}'), + (1, 'inventory_updated_hook', 'inventory_updated', 'http://example.com/inventory_updated', 'POST', '{"event":"{{ webhook_type }}"}'), + (1, 'inventory_deleted_hook', 'inventory_deleted', 'http://example.com/inventory_deleted', 'POST', '{"event":"{{ webhook_type }}"}'), + (1, 'order_settled_hook', 'order_settled', 'http://example.com/order_settled', 'POST', '{"event":"{{ webhook_type }}"}');" | psql talercheck + +# Create the category +echo "SET search_path TO merchant; + INSERT INTO merchant_categories (merchant_serial, category_name, category_name_i18n) + VALUES (1, 'Test Category', '{"en":"Test Category"}');" | psql talercheck + +# Update the category +echo "SET search_path TO merchant; + UPDATE merchant_categories + SET category_name = 'Updated Category' + WHERE category_name = 'Test Category';" | psql talercheck + +# Delete the category +echo "SET search_path TO merchant; + DELETE FROM merchant_categories + WHERE category_name = 'Updated Category';" | psql talercheck + +# Add product to inventory +echo "SET search_path TO merchant; + INSERT INTO merchant_inventory (merchant_serial, product_id, description, description_i18n, unit, image, taxes, price, total_stock, address, next_restock) + VALUES + (1, 'test-product', 'Test Product', '{"en":"Test Product"}', 'unit', 'image-data-url', '[]', ROW(10, 0, 'USD'), 100, '{"city":"TestCity"}', 0);" | psql talercheck + +# Update product in inventory +echo "SET search_path TO merchant; + UPDATE merchant_inventory + SET description = 'Updated Test Product' + WHERE product_id = 'test-product';" | psql talercheck + +# Delete product from inventory +echo "SET search_path TO merchant; + DELETE FROM merchant_inventory + WHERE product_id = 'test-product';" | psql talercheck + +# Create the merchant account +echo "SET search_path TO merchant; + INSERT INTO merchant_accounts ( + merchant_serial, + h_wire, + salt, + credit_facade_url, + credit_facade_credentials, + last_bank_serial, + payto_uri, + active + ) VALUES ( + 1, + decode('$(openssl rand -hex 64)', 'hex'), + decode('$(openssl rand -hex 16)', 'hex'), + 'http://example.com/credit', + '{"key":"value"}', + 0, + '$PAYTO_URI', + TRUE + );" | psql talercheck + +# Create the merchant transfer +echo "SET search_path TO merchant; + INSERT INTO merchant_transfers ( + credit_serial, + exchange_url, + wtid, + credit_amount, + account_serial + ) VALUES ( + 1, + 'http://example.com/exchange', + decode('$WTID', 'hex'), + ROW(100, 0, 'USD')::taler_amount_currency, + 1 + );" | psql talercheck + +# Create the exchange signing key +echo "SET search_path TO merchant; + INSERT INTO merchant_exchange_signing_keys ( + master_pub, + exchange_pub, + start_date, + expire_date, + end_date, + master_sig + ) VALUES ( + decode('$(openssl rand -hex 32)', 'hex'), + decode('$EXCHANGE_PUB', 'hex'), + EXTRACT(EPOCH FROM NOW()), + EXTRACT(EPOCH FROM NOW()) + 3600, + EXTRACT(EPOCH FROM NOW()) + 7200, + decode('$(openssl rand -hex 64)', 'hex') + );" | psql talercheck + +# Create the merchant_contract_terms +echo "SET search_path TO merchant; + INSERT INTO merchant_contract_terms ( + order_serial, + merchant_serial, + order_id, + contract_terms, + wallet_data, + h_contract_terms, + creation_time, + pay_deadline, + refund_deadline, + paid, + wired, + fulfillment_url, + session_id, + pos_key, + pos_algorithm, + claim_token + ) VALUES ( + 1001, + 1, + 'order-1234', + decode('$(openssl rand -hex 64)', 'hex'), + '{"wallet":"test"}', + decode('$(openssl rand -hex 64)', 'hex'), + EXTRACT(EPOCH FROM NOW()), + EXTRACT(EPOCH FROM NOW()) + 3600, + EXTRACT(EPOCH FROM NOW()) + 7200, + FALSE, + FALSE, + 'http://example.com/fulfillment', + '', + NULL, + 0, + decode('$(openssl rand -hex 16)', 'hex') + );" | psql talercheck + +echo "SET search_path TO merchant; + INSERT INTO merchant_deposit_confirmations ( + order_serial, + deposit_timestamp, + exchange_url, + total_without_fee, + wire_fee, + signkey_serial, + exchange_sig, + account_serial, + wire_pending + ) VALUES ( + 1001, + EXTRACT(EPOCH FROM NOW()), + '$EXCHANGE_URL', + ROW(90, 0, 'USD')::taler_amount_currency, + ROW(10, 0, 'USD')::taler_amount_currency, + 1, + decode('$EXCHANGE_SIG', 'hex'), + 1, + TRUE + );" | psql talercheck + +echo "SET search_path TO merchant; + UPDATE merchant_contract_terms + SET wired = TRUE + WHERE order_serial = 1001 + AND NOT EXISTS ( + SELECT 1 + FROM merchant_deposit_confirmations + WHERE wire_pending + AND order_serial = 1001 + );" | psql talercheck + +# Handle deposit confirmation +echo "SET search_path TO merchant; + INSERT INTO merchant_deposit_confirmations ( + order_serial, + deposit_timestamp, + exchange_url, + total_without_fee, + wire_fee, + signkey_serial, + exchange_sig, + account_serial, + wire_pending + ) VALUES ( + 1001, + EXTRACT(EPOCH FROM NOW()), + 'http://example.com/exchange', + ROW(90, 0, 'USD')::taler_amount_currency, + ROW(10, 0, 'USD')::taler_amount_currency, + 1, + decode('$(openssl rand -hex 64)', 'hex'), + 1, + FALSE + );" | psql talercheck + +echo "SET search_path TO merchant; + UPDATE merchant_contract_terms + SET wired = TRUE + WHERE order_serial = 1001 + AND NOT EXISTS ( + SELECT 1 + FROM merchant_deposit_confirmations + WHERE wire_pending + AND order_serial = 1001 + );" | psql talercheck + +# Add entries to merchant_deposits +echo "SET search_path TO merchant; + INSERT INTO merchant_deposits ( + deposit_serial, + coin_offset, + deposit_confirmation_serial, + coin_pub, + coin_sig, + amount_with_fee, + deposit_fee, + refund_fee + ) VALUES ( + (SELECT COALESCE(MAX(deposit_serial), 0) + 1 FROM merchant_deposits), -- deposit_serial + 0, -- coin_offset + 1, -- deposit_confirmation_serial (link to confirmation) + decode('$COIN_PUB', 'hex'), -- coin_pub + decode('$(openssl rand -hex 64)', 'hex'), -- coin_sig + ROW(30, 0, 'USD')::taler_amount_currency, -- amount_with_fee + ROW(1, 0, 'USD')::taler_amount_currency, -- deposit_fee + ROW(0, 0, 'USD')::taler_amount_currency -- refund_fee + );" | psql talercheck + +# Link deposits to wire transfers +echo "SET search_path TO merchant; + INSERT INTO merchant_deposit_to_transfer ( + deposit_serial, + coin_contribution_value, + execution_time, + signkey_serial, + exchange_sig, + wtid + ) VALUES ( + (SELECT MAX(deposit_serial) FROM merchant_deposits), -- deposit_serial + ROW(10, 0, 'USD')::taler_amount_currency, -- coin_contribution_value + $EXECUTION_TIME, -- execution_time + 1, -- signkey_serial + decode('$(openssl rand -hex 64)', 'hex'), -- exchange_sig + decode('$WTID', 'hex') -- wtid + );" | psql talercheck + + +# Debug the UPDATE condition +echo "SET search_path TO merchant; + SELECT md.deposit_confirmation_serial, mdtt.wtid + FROM merchant_deposits md + LEFT JOIN merchant_deposit_to_transfer mdtt + USING (deposit_serial) + WHERE md.deposit_confirmation_serial=1 + AND mdtt.wtid IS NULL;" | psql talercheck + +# Re-run the update logic +echo "SET search_path TO merchant; + UPDATE merchant_deposit_confirmations + SET wire_pending=FALSE + WHERE deposit_confirmation_serial=1 + AND NOT EXISTS + (SELECT 1 + FROM merchant_deposits md + LEFT JOIN merchant_deposit_to_transfer mdtt + USING (deposit_serial) + WHERE md.deposit_confirmation_serial=1 + AND mdtt.wtid IS NULL);" | psql talercheck + + +echo "SET search_path TO merchant; + SELECT merchant_do_insert_transfer_details( + '$INSTANCE_ID', + '$EXCHANGE_URL', + '$PAYTO_URI', + decode('$WTID', 'hex')::BYTEA, + $EXECUTION_TIME::BIGINT, + decode('$EXCHANGE_PUB', 'hex')::BYTEA, + decode('$EXCHANGE_SIG', 'hex')::BYTEA, + $TOTAL_AMOUNT, + $WIRE_FEE, + $COIN_VALUES, + $DEPOSIT_FEES, + $COIN_PUBS, + $CONTRACT_TERMS + );" | psql talercheck + +# Echo all webhooks +echo "SET search_path TO merchant; + SELECT * FROM merchant_pending_webhooks;" | psql talercheck + +# Fetch and validate the number of pending webhooks +WEBHOOK_COUNT=$(echo "SET search_path TO merchant; + SELECT COUNT(*) + FROM merchant_pending_webhooks;" | psql talercheck -t -A | tr -d '[:space:]') + +if [ "$WEBHOOK_COUNT" -ne 7 ]; then + echo "ERROR: Expected 7 webhook entries, but received $WEBHOOK_COUNT." + exit 1 +else + echo "SUCCESS: Received 7 webhook entries as expected." +fi +\ No newline at end of file