diff --git a/src/exchange/taler-exchange-httpd_kyc-check.c b/src/exchange/taler-exchange-httpd_kyc-check.c index c88859268..bf4e4dea1 100644 --- a/src/exchange/taler-exchange-httpd_kyc-check.c +++ b/src/exchange/taler-exchange-httpd_kyc-check.c @@ -112,6 +112,11 @@ struct KycPoller */ const char *section_name; + /** + * Set to AML status of the account. + */ + enum TALER_AmlDecisionState aml_status; + /** * Set to error encountered with KYC logic, if any. */ @@ -303,6 +308,7 @@ kyc_check (void *cls, TEH_plugin->cls, kyp->requirement_row, &requirements, + &kyp->aml_status, &h_payto); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { @@ -580,6 +586,17 @@ TEH_handler_kyc_check ( if ( (NULL == kyp->ih) && (! kyp->kyc_required) ) { + if (TALER_AML_NORMAL != kyp->aml_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC is OK, but AML active: %d\n", + (int) kyp->aml_status); + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status)); + } /* KYC not required */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC not required %llu\n", @@ -628,6 +645,8 @@ TEH_handler_kyc_check ( return TALER_MHD_REPLY_JSON_PACK ( rc->connection, MHD_HTTP_ACCEPTED, + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status), GNUNET_JSON_pack_string ("kyc_url", kyp->kyc_url)); } @@ -665,6 +684,8 @@ TEH_handler_kyc_check ( &sig), GNUNET_JSON_pack_data_auto ("exchange_pub", &pub), + GNUNET_JSON_pack_uint64 ("aml_status", + kyp->aml_status), GNUNET_JSON_pack_object_incref ("kyc_details", kyp->kyc_details), GNUNET_JSON_pack_timestamp ("now", diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.c b/src/exchangedb/pg_lookup_kyc_requirement_by_row.c index 6542aa28f..9651359e2 100644 --- a/src/exchangedb/pg_lookup_kyc_requirement_by_row.c +++ b/src/exchangedb/pg_lookup_kyc_requirement_by_row.c @@ -30,9 +30,11 @@ TEH_PG_lookup_kyc_requirement_by_row ( void *cls, uint64_t requirement_row, char **requirements, + enum TALER_AmlDecisionState *aml_status, struct TALER_PaytoHashP *h_payto) { struct PostgresClosure *pg = cls; + uint32_t status; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&requirement_row), GNUNET_PQ_query_param_end @@ -42,19 +44,26 @@ TEH_PG_lookup_kyc_requirement_by_row ( requirements), GNUNET_PQ_result_spec_auto_from_type ("h_payto", h_payto), + GNUNET_PQ_result_spec_uint32 ("status", + &status), GNUNET_PQ_result_spec_end }; -/* Used in #postgres_lookup_kyc_requirement_by_row() */ + enum GNUNET_DB_QueryStatus qs; + PREPARE (pg, "lookup_legitimization_requirement_by_row", "SELECT " - " required_checks" - ",h_payto" - " FROM legitimization_requirements" + " lr.required_checks" + ",lr.h_payto" + ",aml.status" + " FROM legitimization_requirements lr" + " JOIN aml_status aml USING (h_payto)" " WHERE legitimization_requirement_serial_id=$1;"); - return GNUNET_PQ_eval_prepared_singleton_select ( + qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, "lookup_legitimization_requirement_by_row", params, rs); + *aml_status = (enum TALER_AmlDecisionState) status; + return qs; } diff --git a/src/exchangedb/pg_lookup_kyc_requirement_by_row.h b/src/exchangedb/pg_lookup_kyc_requirement_by_row.h index 12d726187..3d223c985 100644 --- a/src/exchangedb/pg_lookup_kyc_requirement_by_row.h +++ b/src/exchangedb/pg_lookup_kyc_requirement_by_row.h @@ -32,6 +32,7 @@ * @param cls closure * @param requirement_row identifies requirement to look up * @param[out] requirements provider that must be checked + * @param[out] aml_status set to the AML status of the account * @param[out] h_payto account that must be KYC'ed * @return database transaction status */ @@ -40,5 +41,7 @@ TEH_PG_lookup_kyc_requirement_by_row ( void *cls, uint64_t requirement_row, char **requirements, + enum TALER_AmlDecisionState *aml_status, struct TALER_PaytoHashP *h_payto); + #endif diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 2bf71a17e..4099d6bb0 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -3490,8 +3490,16 @@ struct TALER_EXCHANGE_KycStatus */ struct TALER_ExchangeSignatureP exchange_sig; + /** + * AML status for the account. + */ + enum TALER_AmlDecisionState aml_status; + } success; + /** + * KYC is required. + */ struct { @@ -3502,8 +3510,26 @@ struct TALER_EXCHANGE_KycStatus */ const char *kyc_url; + /** + * AML status for the account. + */ + enum TALER_AmlDecisionState aml_status; + } accepted; + /** + * KYC is OK, but account needs positive AML decision. + */ + struct + { + + /** + * AML status for the account. + */ + enum TALER_AmlDecisionState aml_status; + + } unavailable_for_legal_reasons; + } details; }; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index a0caf4f85..32a18f638 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -6457,6 +6457,7 @@ struct TALER_EXCHANGEDB_Plugin * @param cls closure * @param legi_row identifies requirement to look up * @param[out] requirements space-separated list of requirements + * @param[out] aml_status set to the AML status of the account * @param[out] h_payto account that must be KYC'ed * @return database transaction status */ @@ -6465,6 +6466,7 @@ struct TALER_EXCHANGEDB_Plugin void *cls, uint64_t requirement_row, char **requirements, + enum TALER_AmlDecisionState *aml_status, struct TALER_PaytoHashP *h_payto); diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c index 68a40a962..2f03730a8 100644 --- a/src/lib/exchange_api_kyc_check.c +++ b/src/lib/exchange_api_kyc_check.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021 Taler Systems SA + Copyright (C) 2021-2023 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 @@ -97,6 +97,7 @@ handle_kyc_check_finished (void *cls, case MHD_HTTP_OK: { json_t *kyc_details; + uint32_t status; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", &ks.details.success.exchange_sig), @@ -106,6 +107,8 @@ handle_kyc_check_finished (void *cls, &ks.details.success.timestamp), GNUNET_JSON_spec_json ("kyc_details", &kyc_details), + GNUNET_JSON_spec_uint32 ("aml_status", + &status), GNUNET_JSON_spec_end () }; const struct TALER_EXCHANGE_Keys *key_state; @@ -121,6 +124,8 @@ handle_kyc_check_finished (void *cls, break; } ks.details.success.kyc_details = kyc_details; + ks.details.success.aml_status + = (enum TALER_AmlDecisionState) status; key_state = TALER_EXCHANGE_get_keys (kch->exchange); if (GNUNET_OK != TALER_EXCHANGE_test_signing_key (key_state, @@ -155,9 +160,12 @@ handle_kyc_check_finished (void *cls, } case MHD_HTTP_ACCEPTED: { + uint32_t status; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("kyc_url", &ks.details.accepted.kyc_url), + GNUNET_JSON_spec_uint32 ("aml_status", + &status), GNUNET_JSON_spec_end () }; @@ -171,6 +179,8 @@ handle_kyc_check_finished (void *cls, ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } + ks.details.accepted.aml_status + = (enum TALER_AmlDecisionState) status; kch->cb (kch->cb_cls, &ks); GNUNET_JSON_parse_free (spec); @@ -190,6 +200,33 @@ handle_kyc_check_finished (void *cls, case MHD_HTTP_NOT_FOUND: ks.ec = TALER_JSON_get_error_code (j); break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + { + uint32_t status; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint32 ("aml_status", + &status), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + ks.http_status = 0; + ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + ks.details.unavailable_for_legal_reasons.aml_status + = (enum TALER_AmlDecisionState) status; + kch->cb (kch->cb_cls, + &ks); + GNUNET_JSON_parse_free (spec); + TALER_EXCHANGE_kyc_check_cancel (kch); + return; + } case MHD_HTTP_INTERNAL_SERVER_ERROR: ks.ec = TALER_JSON_get_error_code (j); /* Server had an internal issue; we should retry, but this API