diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_batch-withdraw.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_batch-withdraw.c | 132 |
1 files changed, 131 insertions, 1 deletions
diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c index b2f35b56..e6be54a5 100644 --- a/src/exchange/taler-exchange-httpd_batch-withdraw.c +++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -108,6 +108,11 @@ struct BatchWithdrawContext */ unsigned int planchets_length; + /** + * AML decision, #TALER_AML_NORMAL if we may proceed. + */ + enum TALER_AmlDecisionState aml_decision; + }; @@ -151,6 +156,34 @@ batch_withdraw_amount_cb (void *cls, /** + * Function called on each @a amount that was found to + * be relevant for the AML check as it was merged into + * the reserve. + * + * @param cls `struct TALER_Amount *` to total up the amounts + * @param amount encountered transaction amount + * @param date when was the amount encountered + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to abort iteration + * #GNUNET_SYSERR on internal error (also abort itaration) + */ +static enum GNUNET_GenericReturnValue +aml_amount_cb ( + void *cls, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Absolute date) +{ + struct TALER_Amount *total = cls; + + GNUNET_assert (0 <= + TALER_amount_add (total, + total, + amount)); + return GNUNET_OK; +} + + +/** * Function implementing withdraw transaction. Runs the * transaction logic; IF it returns a non-error code, the transaction * logic MUST NOT queue a MHD response. IF it returns an hard error, @@ -178,8 +211,102 @@ batch_withdraw_transaction (void *cls, bool balance_ok = false; bool found = false; const char *kyc_required; + struct TALER_PaytoHashP reserve_h_payto; wc->now = GNUNET_TIME_timestamp_get (); + /* Do AML check: compute total merged amount and check + against applicable AML threshold */ + { + char *reserve_payto; + + reserve_payto = TALER_reserve_make_payto (TEH_base_url, + wc->reserve_pub); + TALER_payto_hash (reserve_payto, + &reserve_h_payto); + GNUNET_free (reserve_payto); + } + { + struct TALER_Amount merge_amount; + struct TALER_Amount threshold; + struct GNUNET_TIME_Absolute now_minus_one_month; + + now_minus_one_month + = GNUNET_TIME_absolute_subtract (wc->now.abs_time, + GNUNET_TIME_UNIT_MONTHS); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TEH_currency, + &merge_amount)); + qs = TEH_plugin->select_merge_amounts_for_kyc_check (TEH_plugin->cls, + &reserve_h_payto, + now_minus_one_month, + &aml_amount_cb, + &merge_amount); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_merge_amounts_for_kyc_check"); + return qs; + } + qs = TEH_plugin->select_aml_threshold (TEH_plugin->cls, + &reserve_h_payto, + &wc->aml_decision, + &threshold); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_aml_threshold"); + return qs; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + threshold = TEH_aml_threshold; /* use default */ + wc->aml_decision = TALER_AML_NORMAL; + } + + switch (wc->aml_decision) + { + case TALER_AML_NORMAL: + if (0 >= TALER_amount_cmp (&merge_amount, + &threshold)) + { + /* merge_amount <= threshold, continue withdraw below */ + break; + } + wc->aml_decision = TALER_AML_PENDING; + qs = TEH_plugin->trigger_aml_process (TEH_plugin->cls, + &reserve_h_payto, + &merge_amount); + if (qs <= 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "trigger_aml_process"); + return qs; + } + return qs; + case TALER_AML_PENDING: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "AML already pending, doing nothing\n"); + return qs; + case TALER_AML_FROZEN: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Account frozen, doing nothing\n"); + return qs; + } + } + + /* Check if the money came from a wire transfer */ qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls, wc->reserve_pub, &wc->h_payto); @@ -352,6 +479,9 @@ generate_reply_success (const struct TEH_RequestContext *rc, &wc->h_payto, &wc->kyc); } + if (TALER_AML_NORMAL != wc->aml_decision) + return TEH_RESPONSE_reply_aml_blocked (rc->connection, + wc->aml_decision); sigs = json_array (); GNUNET_assert (NULL != sigs); |