commit 749aec3207ec3d8b203b3f0d6720a1e1354c2d04
parent f5eb43fc93770b12885433ffb5b1720e9f0a6d95
Author: Florian Dold <dold@taler.net>
Date: Mon, 31 Aug 2026 10:57:57 +0200
exchange: expose consistent AML account summaries
Diffstat:
4 files changed, 95 insertions(+), 4 deletions(-)
diff --git a/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-accounts.c b/src/exchange/taler-exchange-httpd_get-aml-OFFICER_PUB-accounts.c
@@ -137,6 +137,7 @@ free_rc (struct ResponseContext *rc)
* @param close_time when was the account formally closed,
* GNUNET_TIME_UNIT_ZERO_TS if it was never closed
* @param comments comments on the account
+ * @param customer_label human-readable customer label
* @param high_risk is this a high-risk business relationship
* @param to_investigate TRUE if this account should be investigated
* @param payto the payto URI of the account
@@ -149,13 +150,13 @@ record_cb (
struct GNUNET_TIME_Timestamp open_time,
struct GNUNET_TIME_Timestamp close_time,
const char *comments,
+ const char *customer_label,
bool high_risk,
bool to_investigate,
struct TALER_FullPayto payto)
{
const bool below_opening_threshold =
- (NULL == comments) &&
- (GNUNET_TIME_absolute_is_never (open_time.abs_time));
+ GNUNET_TIME_absolute_is_never (open_time.abs_time);
const char *system_note = below_opening_threshold
? "BELOW_ACCOUNT_OPENING_THRESHOLD"
: NULL;
@@ -178,6 +179,9 @@ record_cb (
GNUNET_JSON_pack_string ("comments",
comments)),
GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("customer_label",
+ customer_label)),
+ GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("system_note",
system_note)),
GNUNET_JSON_pack_int64 ("rowid",
@@ -201,7 +205,8 @@ record_cb (
const char *export_comments = comments;
epayto = TALER_escape_xml (payto.full_payto);
- if (below_opening_threshold)
+ if ( (NULL == export_comments) &&
+ below_opening_threshold)
export_comments =
"System: transactions remain below the threshold "
"that opens an AML file";
@@ -270,7 +275,8 @@ record_cb (
size_t plen = strlen (payto.full_payto);
size_t wpos = 0;
- if (below_opening_threshold)
+ if ( (NULL == export_comments) &&
+ below_opening_threshold)
export_comments =
"System: transactions remain below the threshold "
"that opens an AML file";
@@ -337,6 +343,8 @@ TEH_handler_aml_accounts_get (
enum TALER_EXCHANGE_YesNoAll open_filter;
enum TALER_EXCHANGE_YesNoAll high_risk_filter;
enum TALER_EXCHANGE_YesNoAll investigation_filter;
+ struct TALER_NormalizedPaytoHashP h_payto;
+ bool have_payto = false;
memset (&rctx,
0,
@@ -377,6 +385,10 @@ TEH_handler_aml_accounts_get (
"high_risk",
TALER_EXCHANGE_YNA_ALL,
&high_risk_filter);
+ TALER_MHD_parse_request_arg_auto (rc->connection,
+ "h_payto",
+ &h_payto,
+ have_payto);
{
const char *mime;
@@ -426,6 +438,9 @@ TEH_handler_aml_accounts_get (
limit = -MAX_RECORDS;
qs = TALER_EXCHANGEDB_iterate_kyc_accounts (
TEH_pg,
+ have_payto
+ ? &h_payto
+ : NULL,
investigation_filter,
open_filter,
high_risk_filter,
diff --git a/src/exchangedb/iterate_kyc_accounts.c b/src/exchangedb/iterate_kyc_accounts.c
@@ -78,6 +78,7 @@ handle_kyc_account_cb (void *cls,
uint64_t rowid;
struct TALER_NormalizedPaytoHashP h_payto;
char *comments = NULL;
+ char *customer_label = NULL;
struct GNUNET_TIME_Timestamp open_time
= GNUNET_TIME_UNIT_FOREVER_TS;
struct GNUNET_TIME_Timestamp close_time
@@ -95,6 +96,10 @@ handle_kyc_account_cb (void *cls,
&comments),
NULL),
GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("customer_label",
+ &customer_label),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_timestamp ("open_time",
&open_time),
NULL),
@@ -126,6 +131,7 @@ handle_kyc_account_cb (void *cls,
open_time,
close_time,
comments,
+ customer_label,
high_risk,
to_investigate,
payto);
@@ -137,6 +143,7 @@ handle_kyc_account_cb (void *cls,
enum GNUNET_DB_QueryStatus
TALER_EXCHANGEDB_iterate_kyc_accounts (
struct TALER_EXCHANGEDB_PostgresContext *pg,
+ const struct TALER_NormalizedPaytoHashP *h_payto,
enum TALER_EXCHANGE_YesNoAll investigation_only,
enum TALER_EXCHANGE_YesNoAll open_only,
enum TALER_EXCHANGE_YesNoAll high_risk_only,
@@ -147,6 +154,7 @@ TALER_EXCHANGEDB_iterate_kyc_accounts (
{
uint64_t ulimit = GNUNET_MIN ((uint64_t) MAX_RECORDS,
TALER_EXCHANGEDB_abs_limit (limit));
+ const struct TALER_NormalizedPaytoHashP no_h_payto = { 0 };
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&offset),
GNUNET_PQ_query_param_uint64 (&ulimit),
@@ -162,6 +170,10 @@ TALER_EXCHANGEDB_iterate_kyc_accounts (
high_risk_only)),
GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES ==
high_risk_only)),
+ GNUNET_PQ_query_param_bool (NULL == h_payto),
+ GNUNET_PQ_query_param_auto_from_type (NULL == h_payto
+ ? &no_h_payto
+ : h_payto),
GNUNET_PQ_query_param_end
};
struct KycAccountResultContext ctx = {
@@ -183,6 +195,7 @@ TALER_EXCHANGEDB_iterate_kyc_accounts (
",kt.open_time"
",kt.close_time"
",lo.jproperties ->> 'FILE_NOTE' AS comments"
+ ",lo.jproperties ->> 'CUSTOMER_LABEL' AS customer_label"
",COALESCE(lo.to_investigate,FALSE) AS to_investigate"
",COALESCE((lo.jproperties ->> 'HIGH_RISK_CUSTOMER')::bool,FALSE) AS high_risk"
",wt.payto_uri"
@@ -204,6 +217,7 @@ TALER_EXCHANGEDB_iterate_kyc_accounts (
" AND ($5 OR (((kt.open_time IS NOT NULL)"
" AND (kt.close_time IS NULL)) = $6))"
" AND ($7 OR ((COALESCE((lo.jproperties ->>'HIGH_RISK_CUSTOMER')::bool,FALSE) = $8)))"
+ " AND ($9 OR (kt.h_normalized_payto = $10))"
" ORDER BY kt.kyc_target_serial_id ASC"
" LIMIT $2");
PREPARE (pg,
@@ -214,6 +228,7 @@ TALER_EXCHANGEDB_iterate_kyc_accounts (
",kt.open_time"
",kt.close_time"
",lo.jproperties ->> 'FILE_NOTE' AS comments"
+ ",lo.jproperties ->> 'CUSTOMER_LABEL' AS customer_label"
",COALESCE(lo.to_investigate,FALSE) AS to_investigate"
",COALESCE((lo.jproperties ->> 'HIGH_RISK_CUSTOMER')::bool,FALSE) AS high_risk"
",wt.payto_uri"
@@ -235,6 +250,7 @@ TALER_EXCHANGEDB_iterate_kyc_accounts (
" AND ($5 OR (((kt.open_time IS NOT NULL)"
" AND (kt.close_time IS NULL)) = $6))"
" AND ($7 OR ((COALESCE((lo.jproperties ->>'HIGH_RISK_CUSTOMER')::bool,FALSE) = $8)))"
+ " AND ($9 OR (kt.h_normalized_payto = $10))"
" ORDER BY kt.kyc_target_serial_id DESC"
" LIMIT $2");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
diff --git a/src/exchangedb/test_kyc_targets.c b/src/exchangedb/test_kyc_targets.c
@@ -174,6 +174,7 @@ struct AccountContext
* @param open_time when the account was opened
* @param close_time when it was closed
* @param comments file note on the account
+ * @param customer_label human-readable customer label
* @param high_risk whether it is a high-risk relationship
* @param to_investigate whether it is flagged for investigation
* @param payto the account's payto URI
@@ -185,6 +186,7 @@ account_cb (void *cls,
struct GNUNET_TIME_Timestamp open_time,
struct GNUNET_TIME_Timestamp close_time,
const char *comments,
+ const char *customer_label,
bool high_risk,
bool to_investigate,
struct TALER_FullPayto payto)
@@ -194,6 +196,7 @@ account_cb (void *cls,
(void) open_time;
(void) close_time;
(void) comments;
+ (void) customer_label;
if (0 == ctx->total++)
ctx->first_row = row_id;
if ( (NULL != ctx->h_payto) &&
@@ -248,6 +251,7 @@ check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
&h_payto));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
@@ -375,6 +379,7 @@ static int
check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
{
struct AccountContext ctx;
+ struct TALER_NormalizedPaytoHashP unknown;
uint64_t lowest;
TDB_account (pg,
@@ -386,6 +391,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
ctx.h_payto = &account.h_normalized;
FAILIF (2 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
@@ -400,12 +406,49 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
FAILIF (ctx.high_risk);
lowest = ctx.first_row;
+ /* An exact account filter returns only the requested account. */
+ memset (&ctx,
+ 0,
+ sizeof (ctx));
+ ctx.h_payto = &account.h_normalized;
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ &account.h_normalized,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ 0,
+ 10,
+ &account_cb,
+ &ctx));
+ FAILIF (1 != ctx.total);
+ FAILIF (1 != ctx.matched);
+
+ /* An unknown exact account filter returns no result. */
+ TDB_FILL (unknown,
+ 99);
+ memset (&ctx,
+ 0,
+ sizeof (ctx));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ &unknown,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ 0,
+ 10,
+ &account_cb,
+ &ctx));
+ FAILIF (0 != ctx.total);
+
/* the limit caps the result set */
memset (&ctx,
0,
sizeof (ctx));
FAILIF (1 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
@@ -421,6 +464,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (2 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
@@ -436,6 +480,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_YES,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
@@ -450,6 +495,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (2 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_NO,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
@@ -464,6 +510,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_YES,
@@ -479,6 +526,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_YES,
TALER_EXCHANGE_YNA_ALL,
@@ -491,6 +539,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (2 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_NO,
TALER_EXCHANGE_YNA_ALL,
@@ -509,6 +558,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (1 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_YES,
TALER_EXCHANGE_YNA_ALL,
@@ -521,6 +571,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (1 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_NO,
TALER_EXCHANGE_YNA_ALL,
@@ -539,6 +590,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_YES,
TALER_EXCHANGE_YNA_ALL,
@@ -551,6 +603,7 @@ check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
sizeof (ctx));
FAILIF (2 !=
TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
+ NULL,
TALER_EXCHANGE_YNA_ALL,
TALER_EXCHANGE_YNA_NO,
TALER_EXCHANGE_YNA_ALL,
diff --git a/src/include/exchange-database/iterate_kyc_accounts.h b/src/include/exchange-database/iterate_kyc_accounts.h
@@ -37,6 +37,7 @@
* @param close_time when was the account formally closed,
* GNUNET_TIME_UNIT_ZERO_TS if it was never closed
* @param comments comments on the account
+ * @param customer_label human-readable customer label
* @param high_risk is this a high-risk business relationship
* @param to_investigate TRUE if this account should be investigated
* @param payto the payto URI of the account
@@ -55,6 +56,7 @@ typedef void
struct GNUNET_TIME_Timestamp open_time,
struct GNUNET_TIME_Timestamp close_time,
const char *comments,
+ const char *customer_label,
bool high_risk,
bool to_investigate,
struct TALER_FullPayto payto);
@@ -72,6 +74,7 @@ typedef void
* @param close_time when was the account formally closed,
* GNUNET_TIME_UNIT_ZERO_TS if it was never closed
* @param comments comments on the account
+ * @param customer_label human-readable customer label
* @param high_risk is this a high-risk business relationship
* @param to_investigate TRUE if this account should be investigated
* @param payto the payto URI of the account
@@ -84,6 +87,7 @@ typedef void
struct GNUNET_TIME_Timestamp open_time,
struct GNUNET_TIME_Timestamp close_time,
const char *comments,
+ const char *customer_label,
bool high_risk,
bool to_investigate,
struct TALER_FullPayto payto);
@@ -94,6 +98,7 @@ typedef void
* Primary test table: `kyc_targets` (see test_kyc_targets.c).
*
* @param pg the database context
+ * @param h_payto account to return, NULL to return all accounts
* @param investigation_only filter by investigation state
* @param open_only filter for only open accounts
* @param high_risk_only filter for only high-risk accounts
@@ -107,6 +112,8 @@ typedef void
enum GNUNET_DB_QueryStatus
TALER_EXCHANGEDB_iterate_kyc_accounts (struct TALER_EXCHANGEDB_PostgresContext *
pg,
+ const struct
+ TALER_NormalizedPaytoHashP *h_payto,
enum TALER_EXCHANGE_YesNoAll
investigation_only,
enum TALER_EXCHANGE_YesNoAll open_only,