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:
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