The Bank Data Agent is a specialized AI agent for the efficient processing of bank account changes. It validates and processes customer requests to update bank data automatically, ensuring a seamless integration into existing payment systems. Intelligent verification mechanisms and structured processes perform bank data changes precisely, securely, and with minimal manual effort.

Core Functions

The Bank Data Agent handles the entire process of bank data updating, offering the following key functionalities:

  • IBAN validation: Automated checking of the format and checksum compliance of new bank connections
  • Identity verification: Comparison of the account holder with contract information to ensure legitimacy
  • System integration: Seamless updating of bank data in all relevant systems
  • Customer communication: Automated confirmations of successful changes or queries in case of uncertainties

Process Flow

The agent operates according to a clearly defined process that ensures maximum security while providing high user-friendliness:

  1. Extraction of relevant data from the customer request (new IBAN, account holder, desired date)
  2. Validation of the new bank connection based on international standards
  3. Verification of authorization to change the bank details
  4. Implementation of the change in all linked systems
  5. Confirmation to the customer with the relevant information on the new bank connection

Thanks to rule-based logic, all inputs are systematically checked and processed, minimizing errors while significantly reducing manual processing effort.


<?php

use EnneoSDK\Api;
use EnneoSDK\Helpers;
use EnneoSDK\IntentInfo;
use EnneoSDK\IntentOption;
use EnneoSDK\Interaction;

require(getenv()['SDK'] ?? 'sdk.php');
/** @var stdClass $in */

######### Expected Input:
// date -> Date for deposit change (optional)
// channel -> ticket channel. Voice and chat can be handled synchronously (required)
// newIBAN -> IBAN of the new bank account (required)
// oldIBAN -> IBAN of the old bank account (optional)
// payoutOnly -> true if only payout should be changed (optional)


// Initialize variables
if ($in->date ?? false) {
    $in->date = Helpers::parseDateToYMD($in->date);
} else {
    $in->date = date('Y-m-d', strtotime('+1 day'));
}
$interaction = new Interaction(data: $in);
$interaction->data->oldIBAN = formatIban($in->oldIBAN ?: "");
$interaction->data->newIBAN = formatIban($in->newIBAN ?: "");

// Handle actual change of bank data in the backend system
if ($in->_action == 'enter_into_system') {
    $response = Api::call(
        method: 'POST',
        url: 'https://echo.enneo.ai',
        params: json_decode(json_encode($interaction), true)
    );
    echo json_encode($interaction);
    exit();
}

// Business logic
if ($in->oldIBAN == $in->newIBAN && $in->newIBAN) {
    $interaction->infos[] = new IntentInfo(
        type: 'warning',
        message: 'IBAN ist schon im System',
    );
    $interaction->options[] = new IntentOption(
        type: 'iban_already_in_system',
        name: 'Kunden darauf hinweisen',
        recommended: true,
    );
} elseif (!validateIbanFormatting($in->newIBAN)) {
    $interaction->infos[] = new IntentInfo(
        type: 'warning',
        message: 'IBAN nicht im korrekten Format',
    );
    $interaction->options[] = new IntentOption(
        type: 'ask_for_verification_iban_invalid_format',
        name: 'Kunden darauf hinweisen',
        recommended: true,
    );
} elseif (!validateIbanChecksum($in->newIBAN)) {
    $interaction->infos[] = new IntentInfo(
        type: 'warning',
        message: 'IBAN-Prüfsumme ungültig',
    );
    $interaction->options[] = new IntentOption(
        type: 'ask_for_verification_iban_invalid_checksum',
        name: 'Kunden darauf hinweisen',
        recommended: true,
    );
} else {
    if ($in->date < date('Y-m-d')) {
        // If the customer wants to use an IBAN effective in the past, we default to tomorrow
        $interaction->infos[] = new IntentInfo(
            type: 'warning',
            message: 'Gewünschtes Gültigkeitsdatum '.EnneoSDK\Helpers::formatDate($in->date).' liegt in der Vergangenheit. Gültigkeit auf morgen gesetzt'
        );
        $interaction->data->date = date('Y-m-d', strtotime('+1 day'));
    }

    $interaction->infos[] = new IntentInfo(
        type: 'success',
        message: 'IBAN gültig',
    );
    if ($in->payoutOnly) {
        $msg = 'Im System  nur für Guthaben hinterlegen';
    } else {
        $msg = 'Im System hinterlegen';
    }

    $interaction->options[] = new IntentOption(
        type: 'enter_into_system',
        name: $msg,
        recommended: true,
    );
}

// Return result
echo json_encode($interaction);

// Format IBAN for readability
function formatIban(string $iban): string
{
    $iban = str_replace(' ', '', $iban);

    // Split the input string into 4-character chunks for readability
    $formatted_iban = '';
    $chunk_size = 4;
    $num_chunks = ceil(strlen($iban) / $chunk_size);
    for ($i = 0; $i < $num_chunks; ++$i) {
        $chunk = substr($iban, $i * $chunk_size, $chunk_size);
        $formatted_iban .= $chunk . ' ';
    }

    // Trim any trailing spaces and return the formatted IBAN
    return trim($formatted_iban);
}

// Check integrity of IBAN (modified from original source: https://stackoverflow.com/questions/20983339/validate-iban-php)
function validateIbanChecksum(string $iban): bool
{
    $iban = strtolower(str_replace(' ', '', $iban));
    $Countries = ['al' => 28, 'ad' => 24, 'at' => 20, 'az' => 28, 'bh' => 22, 'be' => 16, 'ba' => 20, 'br' => 29, 'bg' => 22, 'cr' => 21, 'hr' => 21, 'cy' => 28, 'cz' => 24, 'dk' => 18, 'do' => 28, 'ee' => 20, 'fo' => 18, 'fi' => 18, 'fr' => 27, 'ge' => 22, 'de' => 22, 'gi' => 23, 'gr' => 27, 'gl' => 18, 'gt' => 28, 'hu' => 28, 'is' => 26, 'ie' => 22, 'il' => 23, 'it' => 27, 'jo' => 30, 'kz' => 20, 'kw' => 30, 'lv' => 21, 'lb' => 28, 'li' => 21, 'lt' => 20, 'lu' => 20, 'mk' => 19, 'mt' => 31, 'mr' => 27, 'mu' => 30, 'mc' => 27, 'md' => 24, 'me' => 22, 'nl' => 18, 'no' => 15, 'pk' => 24, 'ps' => 29, 'pl' => 28, 'pt' => 25, 'qa' => 29, 'ro' => 24, 'sm' => 27, 'sa' => 24, 'rs' => 22, 'sk' => 24, 'si' => 19, 'es' => 24, 'se' => 24, 'ch' => 21, 'tn' => 24, 'tr' => 26, 'ae' => 23, 'gb' => 22, 'vg' => 24];
    $Chars = ['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 'i' => 18, 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, 's' => 28, 't' => 29, 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35];

    if (!array_key_exists(substr($iban, 0, 2), $Countries)) {
        return false;
    }

    if (strlen($iban) == $Countries[substr($iban, 0, 2)]) {
        $MovedChar = substr($iban, 4) . substr($iban, 0, 4);
        $MovedCharArray = str_split($MovedChar);
        $NewString = '';

        foreach ($MovedCharArray as $key => $value) {
            if (!is_numeric($MovedCharArray[$key])) {
                $MovedCharArray[$key] = $Chars[$MovedCharArray[$key]];
            }
            $NewString .= $MovedCharArray[$key];
        }

        if (bcmod($NewString, '97') == 1) {
            return true;
        }
    }

    return false;
}

// Check formatting of IBAN
function validateIbanFormatting(string $str): bool
{
    // Note: German IBAN definition allows for slack
    // Correct IBAN definition: DE[0-9]{2}\s?([0-9]{4}\s?){4}([0-9]{2})\s?
    if (str_starts_with($str,'DE')) {
        $re = 'DE\s?[0-9]{2}\s?([0-9]{4}\s?){4}([0-9]{2})\s?';
        // Find all IBAN addresses
        if (preg_match_all('/' . $re . '/', $str, $m)) {
            return true;
        } else {
            return false;
        }
    } else {
        // International IBANs are validated in the validateIbanChecksum() function
        return true;
    }
}