merchant

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

commit b63b1ec2a5d916113d96b42d91fc9c237cc5a037
parent e9d4f60108e12467b59f386b9fb1e88e5180ac18
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 15 Jun 2026 23:30:19 +0200

implement #11484, needs testing/QC

Diffstat:
Mcontrib/meson.build | 1+
Msrc/backend/taler-merchant-httpd_get-private-accounts-H_WIRE.c | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Asrc/backenddb/pg_merchant_account_trigger.sql | 35+++++++++++++++++++++++++++++++++++
Msrc/backenddb/pg_merchant_kyc_trigger.sql | 2+-
Asrc/backenddb/pg_merchant_send_account_notification.sql | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backenddb/sql-schema/merchant-0040.sql | 2+-
Asrc/backenddb/sql-schema/merchant-0041.sql | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backenddb/sql-schema/meson.build | 4++++
8 files changed, 279 insertions(+), 8 deletions(-)

diff --git a/contrib/meson.build b/contrib/meson.build @@ -36,6 +36,7 @@ foreach f : dist_tmplpkgdata_DATA endforeach install_data( 'kyc_text.en.must', + 'account_status.en.must', install_dir: get_option('datadir') / 'taler-merchant' / 'templates', ) diff --git a/src/backend/taler-merchant-httpd_get-private-accounts-H_WIRE.c b/src/backend/taler-merchant-httpd_get-private-accounts-H_WIRE.c @@ -21,6 +21,7 @@ #include "platform.h" #include "taler-merchant-httpd_get-private-accounts-H_WIRE.h" #include <taler/taler_json_lib.h> +#include <taler/taler_templating_lib.h> #include "merchant-database/select_account.h" @@ -42,6 +43,12 @@ TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh, struct TALER_MerchantWireHashP h_wire; struct TALER_MERCHANTDB_AccountDetails tp = { 0 }; enum GNUNET_DB_QueryStatus qs; + enum + { + POF_JSON, + POF_TEXT + } format; + enum MHD_Result ret; GNUNET_assert (NULL != mi); GNUNET_assert (NULL != h_wire_s); @@ -76,9 +83,43 @@ TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh, TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN, hc->infix); } + + /* Determine desired output format from Accept header */ { - enum MHD_Result ret; + const char *mime; + + mime = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_ACCEPT); + if (NULL == mime) + mime = "application/json"; + if (0 == strcmp (mime, + "*/*")) + mime = "application/json"; + if (0 == strcmp (mime, + "application/json")) + { + format = POF_JSON; + } + else if (0 == strcmp (mime, + "text/plain")) + { + format = POF_TEXT; + } + else + { + GNUNET_break_op (0); + return TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_NOT_ACCEPTABLE, + GNUNET_JSON_pack_string ("hint", + mime)); + } + } + switch (format) + { + case POF_JSON: ret = TALER_MHD_REPLY_JSON_PACK ( connection, MHD_HTTP_OK, @@ -98,12 +139,54 @@ TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh, tp.credit_facade_url))); /* We do not return the credentials, as they may be sensitive */ - json_decref (tp.credit_facade_credentials); - GNUNET_free (tp.extra_wire_subject_metadata); - GNUNET_free (tp.payto_uri.full_payto); - GNUNET_free (tp.credit_facade_url); - return ret; + break; + case POF_TEXT: + { + struct MHD_Response *resp; + json_t *obj; + unsigned int response_code = MHD_HTTP_OK; + enum GNUNET_GenericReturnValue res; + + obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_bool ("active", + tp.active), + TALER_JSON_pack_full_payto ("payto_uri", + tp.payto_uri)); + res = TALER_TEMPLATING_build (connection, + &response_code, + "account_status", + mi->settings.id, + NULL, + obj, + &resp); + json_decref (obj); + if (GNUNET_OK != res) + { + ret = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_NOT_ACCEPTABLE, + GNUNET_JSON_pack_string ("hint", + "text/plain")); + break; + } + TALER_MHD_add_global_headers (resp, + false); + GNUNET_break (MHD_YES == + MHD_add_response_header (resp, + MHD_HTTP_HEADER_CONTENT_TYPE, + "text/plain")); + ret = MHD_queue_response (connection, + response_code, + resp); + MHD_destroy_response (resp); + } + break; } + json_decref (tp.credit_facade_credentials); + GNUNET_free (tp.extra_wire_subject_metadata); + GNUNET_free (tp.payto_uri.full_payto); + GNUNET_free (tp.credit_facade_url); + return ret; } diff --git a/src/backenddb/pg_merchant_account_trigger.sql b/src/backenddb/pg_merchant_account_trigger.sql @@ -0,0 +1,35 @@ +-- +-- 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/> + +-- @file pg_merchant_account_trigger.sql +-- @brief Run on INSERT/DELETE/UPDATE of accounts to generate report +-- @author Christian Grothoff + + +CREATE OR REPLACE FUNCTION merchant_kyc_update_trigger() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +BEGIN + IF (OLD.payto_uri + ,OLD.active) + IS DISTINCT FROM + (NEW.payto_uri + ,NEW.active) + THEN + CALL merchant_send_account_notification(NEW.account_serial); + END IF; + RETURN NEW; +END $$; diff --git a/src/backenddb/pg_merchant_kyc_trigger.sql b/src/backenddb/pg_merchant_kyc_trigger.sql @@ -14,7 +14,7 @@ -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -- @file pg_merchant_kyc_trigger.sql --- @brief Fix trigger logic +-- @brief Run on changes to our KYC status to generate report -- @author Christian Grothoff diff --git a/src/backenddb/pg_merchant_send_account_notification.sql b/src/backenddb/pg_merchant_send_account_notification.sql @@ -0,0 +1,96 @@ +-- +-- 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/> + +-- @file pg_merchant_send_account_notification.sql +-- @brief Send notification to merchant about new account status +-- @author Christian Grothoff + + +DROP PROCEDURE IF EXISTS merchant_send_account_notification; +CREATE PROCEDURE merchant_send_account_notification( + in_account_serial INT8 + ) +LANGUAGE plpgsql +AS $$ +DECLARE + my_instance_serial INT8; + my_report_token BYTEA; + my_h_wire BYTEA; + my_email TEXT; + my_notification_language TEXT; +BEGIN + SELECT SUBSTRING(current_schema()::TEXT + FROM 'merchant_instance_([0-9]+)')::INT8 + INTO my_instance_serial; + SELECT h_wire + INTO my_h_wire + FROM merchant_accounts + WHERE account_serial=in_account_serial; + IF NOT FOUND + THEN + RAISE WARNING 'Account not found, account change notification not triggered'; + RETURN; + END IF; + SELECT email + ,notification_language + INTO my_email + ,my_notification_language + FROM merchant.merchant_instances + WHERE merchant_serial=my_instance_serial; + IF NOT FOUND + THEN + RAISE WARNING 'Instance not found, account change notification not triggered'; + RETURN; + END IF; + IF my_notification_language IS NULL + THEN + -- Disabled + RETURN; + END IF; + IF my_email IS NULL + THEN + -- Note: we MAY want to consider sending an SMS instead... + RETURN; + END IF; + + my_report_token = random_bytea(32); + INSERT INTO merchant_reports ( + merchant_serial + ,report_program_section + ,report_description + ,mime_type + ,report_token + ,data_source + ,target_address + ,frequency + ,frequency_shift + ,next_transmission + ,one_shot_hidden + ) VALUES ( + my_instance_serial + ,'email' + ,'automatically triggered account change alert' + ,'text/plain' + ,my_report_token + ,'/private/accounts/' || base32_crockford (my_h_wire) + ,my_email + ,0 + ,0 + ,0 + ,TRUE + ); + -- Notify taler-merchant-report-generator + NOTIFY XSSAB8NCBQR1K2VK7H2M6SMY3V5TNJT1C3BW0SN4F2QV0KHR3PRB0; +END $$; diff --git a/src/backenddb/sql-schema/merchant-0040.sql b/src/backenddb/sql-schema/merchant-0040.sql @@ -13,7 +13,7 @@ -- 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/> --- @file merchant-0039.sql +-- @file merchant-0040.sql -- @brief Remove unused columns from merchant.merchant_instances. -- These validation_needed and validation_expired columns are not needed, since -- merchant instances are only created after MFA validation in the first place. diff --git a/src/backenddb/sql-schema/merchant-0041.sql b/src/backenddb/sql-schema/merchant-0041.sql @@ -0,0 +1,52 @@ +-- +-- 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/> + +-- @file merchant-0041.sql +-- @brief Add trigger to notify merchant on account changes +-- @author Christian Grothoff + +BEGIN; + +SELECT _v.register_patch('merchant-0041', NULL, NULL); + +SET search_path TO merchant; + +CREATE PROCEDURE merchant.merchant_0041_init(s TEXT) + LANGUAGE plpgsql + AS $OUTER$ +BEGIN + EXECUTE format('SET LOCAL search_path TO %I', s); + + CREATE OR REPLACE FUNCTION merchant_account_change_trigger() + RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NULL; END $$; + CREATE TRIGGER merchant_accounts_on_insert + AFTER INSERT OR UPDATE OR DELETE + ON merchant_accounts + FOR EACH ROW EXECUTE FUNCTION merchant_account_change_trigger(); + + SET LOCAL search_path TO merchant; +END +$OUTER$; + +INSERT INTO merchant.instance_fixups + (migration_name + ,version) + VALUES + ('merchant_0041_init' + ,41); +-- Apply new fix-up to existing instances +CALL merchant.fixup_instance_schema (41::INT8); + +COMMIT; diff --git a/src/backenddb/sql-schema/meson.build b/src/backenddb/sql-schema/meson.build @@ -59,6 +59,8 @@ sql_instance_procedures = [ '../activate_account.sql', '../inactivate_account.sql', '../expire_locks.sql', + '../pg_merchant_send_account_notification.sql', + '../pg_merchant_account_trigger.sql', '../pg_merchant_send_kyc_notification.sql', '../pg_merchant_kyc_trigger.sql', '../pg_triggers.sql', @@ -111,10 +113,12 @@ generated_sql = [ ['merchant-0033.sql'], ['merchant-0034.sql'], ['merchant-0035.sql'], + # Note: 36 generated below! ['merchant-0037.sql'], ['merchant-0038.sql'], ['merchant-0039.sql'], ['merchant-0040.sql'], + ['merchant-0041.sql'], ] foreach g : generated_sql