exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 5bb1c3865627d53108ef602ee6339c4749664d73
parent 8156c65e18c08f98ac81c1e4bc86f643efd2e411
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  8 Jun 2026 21:53:17 +0200

properly signal tos_required

Diffstat:
Msrc/exchange/taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.c | 16++++++++++++++--
Msrc/include/taler/taler_kyclogic_lib.h | 25+++++++++++++++++++++++++
Msrc/kyclogic/kyclogic_api.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.c b/src/exchange/taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.c @@ -475,9 +475,21 @@ TEH_handler_kyc_check ( ? "wallet" : "account")); tos_required = false; - if (TEH_kyc_swap_tos_acceptance) + if ( (TEH_kyc_swap_tos_acceptance) && + (NULL != TEH_tos_etag) ) { - /* FIXME-#11183: derive tos_required from jrules! */ + /* #11183: With the terms-of-service / KYC-auth swap enabled, and if + the rule set that applies to this account actually requires the + client to accept our terms of service, surface that requirement + up-front by returning the current ToS ETag below. This lets the + merchant backend submit a previously collected acceptance on the + user's behalf (via the accept-tos requirement it finds under + /kyc-info) instead of forcing the user to interact again. We + inspect the rules precisely (rather than just whether *some* KYC + is required), as the rule set may demand entirely different + measures here. */ + tos_required = + TALER_KYCLOGIC_rules_require_tos_acceptance (jrules); } jlimits = TALER_KYCLOGIC_rules_to_limits (jrules, is_wallet); diff --git a/src/include/taler/taler_kyclogic_lib.h b/src/include/taler/taler_kyclogic_lib.h @@ -516,6 +516,31 @@ TALER_KYCLOGIC_rules_to_limits (const json_t *jrules, /** + * Name of the KYC form used to affirm acceptance of the exchange's + * terms of service. This is the ``form`` of the requirement returned + * by ``GET /kyc-info`` and the ``FORM_ID`` submitted to + * ``POST /kyc-upload/$ID`` for a terms-of-service acceptance. + */ +#define TALER_KYCLOGIC_TOS_ACCEPTANCE_FORM "accept-tos" + + +/** + * Check if @a jrules contains a (still active) rule that requires the + * client to accept the exchange's terms of service, that is a rule one + * of whose measures resolves to a KYC check of type + * #TALER_KYCLOGIC_CT_FORM rendering the + * #TALER_KYCLOGIC_TOS_ACCEPTANCE_FORM form. + * + * @param jrules a ``LegitimizationRuleSet`` with KYC rules; + * NULL to use default rules + * @return true if terms-of-service acceptance is among the + * measures required by @a jrules + */ +bool +TALER_KYCLOGIC_rules_require_tos_acceptance (const json_t *jrules); + + +/** * Parse the given @a jmeasures and return the measure * at the @a measure_index. * diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -1294,6 +1294,64 @@ TALER_KYCLOGIC_rules_to_limits (const json_t *jrules, } +bool +TALER_KYCLOGIC_rules_require_tos_acceptance (const json_t *jrules) +{ + struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs; + const struct TALER_KYCLOGIC_LegitimizationRuleSet *rs; + bool found = false; + + if (NULL == jrules) + { + /* default rules apply */ + lrs = NULL; + rs = &default_rules; + } + else + { + lrs = TALER_KYCLOGIC_rules_parse (jrules); + if (NULL == lrs) + { + GNUNET_break_op (0); + return false; + } + rs = lrs; + } + for (unsigned int i = 0; (! found) && (i < rs->num_kyc_rules); i++) + { + const struct TALER_KYCLOGIC_KycRule *rule = &rs->kyc_rules[i]; + + for (unsigned int j = 0; j < rule->num_measures; j++) + { + const struct TALER_KYCLOGIC_Measure *m; + const struct TALER_KYCLOGIC_KycCheck *c; + + /* Resolve the measure to its check exactly as GET /kyc-info does + (measure -> check -> form), so that our answer is consistent + with the requirements the merchant will observe there. */ + m = find_measure (lrs, + rule->next_measures[j]); + if (NULL == m) + continue; + c = find_check (m->check_name); + if (NULL == c) + continue; + if ( (TALER_KYCLOGIC_CT_FORM == c->type) && + (NULL != c->details.form.name) && + (0 == strcasecmp (c->details.form.name, + TALER_KYCLOGIC_TOS_ACCEPTANCE_FORM)) ) + { + found = true; + break; + } + } + } + if (NULL != lrs) + TALER_KYCLOGIC_rules_free (lrs); + return found; +} + + const struct TALER_KYCLOGIC_Measure * TALER_KYCLOGIC_rule_get_instant_measure ( const struct TALER_KYCLOGIC_KycRule *r)