> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enneo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Bankdaten-Agent

> Automatisierte Bearbeitung von Bankverbindungsänderungen

<Tip>
  Der **Bankdaten-Agent** ist ein spezialisierter KI-Agent für die **effiziente Bearbeitung** von Änderungswünschen zu Bankverbindungen. Er **validiert** und **verarbeitet** Kundenanfragen zur Aktualisierung von Bankdaten **automatisch** und sorgt für eine **nahtlose Integration** in bestehende Zahlungssysteme. Durch intelligente Prüfmechanismen und strukturierte Prozesse werden Bankdatenänderungen präzise, sicher und mit minimalem manuellem Aufwand durchgeführt.
</Tip>

### Zentrale Funktionen

Der Bankdaten-Agent übernimmt den kompletten Prozess der Bankdatenaktualisierung und bietet dabei folgende Kernfunktionalitäten:

* **IBAN-Validierung:** Automatische Prüfung der Format- und Prüfsummenkonformität neuer Bankverbindungen
* **Identitätsprüfung:** Abgleich des Kontoinhabers mit Vertragsinformationen zur Sicherstellung der Legitimität
* **Systemintegration:** Nahtlose Aktualisierung der Bankdaten in allen relevanten Systemen
* **Kundenkommunikation:** Automatisierte Bestätigungen über erfolgreiche Änderungen oder Rückfragen bei Unklarheiten

### Prozessablauf

Der Agent arbeitet nach einem klar definierten Prozess, der maximale Sicherheit bei gleichzeitig hoher Benutzerfreundlichkeit gewährleistet:

1. **Extraktion relevanter Daten** aus der Kundenanfrage (neue IBAN, Kontoinhaber, gewünschtes Datum)
2. **Validierung der neuen Bankverbindung** anhand internationaler Standards
3. **Prüfung der Berechtigung** zur Änderung der Bankdaten
4. **Durchführung der Änderung** in allen verknüpften Systemen
5. **Bestätigung an den Kunden** mit den relevanten Informationen zur neuen Bankverbindung

Durch die regelbasierte Logik werden alle Eingaben systematisch geprüft und verarbeitet, sodass Fehler minimiert und gleichzeitig der manuelle Bearbeitungsaufwand deutlich reduziert wird.

<CodeGroup>
  ```php PHP BankdataAgent.php icon="php" lines expandable theme={null}

  <?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;
      }
  }
  ```

  ```python Python ApiAgent.py icon="python" lines expandable theme={null}
  import importlib.util
  import os
  import json
  import re
  from datetime import datetime, timedelta

  file_path = os.getenv('SDK', 'sdk.py')
  spec = importlib.util.spec_from_file_location('sdk', file_path)
  sdk = importlib.util.module_from_spec(spec)
  spec.loader.exec_module(sdk)

  ######### 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)

  def parse_date_to_ymd(date_str):
      """
      Parse a textual date into yyyy-mm-dd format.
      Matches the PHP parseDateToYMD function.
      """
      if not date_str:
          return None

      # Try standard date parsing first
      try:
          parsed_date = datetime.strptime(date_str, '%Y-%m-%d')
          return parsed_date.strftime('%Y-%m-%d')
      except ValueError:
          pass

      # Try other common formats
      formats = ['%d.%m.%Y', '%d.%m.%y', '%d.%m.%Y', '%d.%m.%y']
      for fmt in formats:
          try:
              parsed_date = datetime.strptime(date_str, fmt)
              return parsed_date.strftime('%Y-%m-%d')
          except ValueError:
              continue

      return None

  # Format IBAN for readability
  def format_iban(iban):
      if not iban:
          return ''

      iban = str(iban).replace(' ', '')

      # Split the input string into 4-character chunks for readability
      formatted_iban = ''
      chunk_size = 4
      num_chunks = (len(iban) + chunk_size - 1) // chunk_size
      for i in range(num_chunks):
          chunk = iban[i * chunk_size:(i + 1) * chunk_size]
          formatted_iban += chunk + ' '

      # Trim any trailing spaces and return the formatted IBAN
      return formatted_iban.strip()

  # Check integrity of IBAN (modified from original source: https://stackoverflow.com/questions/20983339/validate-iban-php)
  def validate_iban_checksum(iban):
      if not iban:
          return False

      iban = str(iban).lower().replace(' ', '')
      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 iban[:2] not in countries:
          return False

      if len(iban) == countries[iban[:2]]:
          moved_char = iban[4:] + iban[:4]
          new_string = ''

          for char in moved_char:
              if char.isdigit():
                  new_string += char
              else:
                  new_string += str(chars[char])

          if int(new_string) % 97 == 1:
              return True

      return False

  # Check formatting of IBAN
  def validate_iban_formatting(iban_str):
      if not iban_str:
          return False

      iban_str = str(iban_str)
      # 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 iban_str.startswith('DE'):
          pattern = r'DE\s?[0-9]{2}\s?([0-9]{4}\s?){4}([0-9]{2})\s?'
          if re.match(pattern, iban_str):
              return True
          else:
              return False
      else:
          # International IBANs are validated in the validate_iban_checksum() function
          return True

  input_data = sdk.load_input_data()

  # Initialize variables
  if input_data.get('date'):
      input_data['date'] = parse_date_to_ymd(input_data['date'])
  else:
      input_data['date'] = (datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d')

  interaction = sdk.Interaction(data=input_data)
  interaction.data['oldIBAN'] = format_iban(input_data.get('oldIBAN', ''))
  interaction.data['newIBAN'] = format_iban(input_data.get('newIBAN', ''))

  # Handle actual change of bank data in the backend system
  if input_data.get('_action') == 'enter_into_system':
      response = sdk.Api.call(
          method='POST',
          url='https://echo.enneo.ai',
          params=json.loads(json.dumps(interaction.model_dump()))
      )
      print(json.dumps(interaction.model_dump()))
      exit()

  # Business logic
  if input_data.get('oldIBAN') == input_data.get('newIBAN') and input_data.get('newIBAN'):
      interaction.infos.append(
          sdk.IntentInfo(
              type='warning',
              message='IBAN ist schon im System'
          )
      )
      interaction.options.append(
          sdk.IntentOption(
              type='iban_already_in_system',
              name='Kunden darauf hinweisen',
              recommended=True
          )
      )
  elif not validate_iban_formatting(input_data.get('newIBAN', '')):
      interaction.infos.append(
          sdk.IntentInfo(
              type='warning',
              message='IBAN nicht im korrekten Format'
          )
      )
      interaction.options.append(
          sdk.IntentOption(
              type='ask_for_verification_iban_invalid_format',
              name='Kunden darauf hinweisen',
              recommended=True
          )
      )
  elif not validate_iban_checksum(input_data.get('newIBAN', '')):
      interaction.infos.append(
          sdk.IntentInfo(
              type='warning',
              message='IBAN-Prüfsumme ungültig'
          )
      )
      interaction.options.append(
          sdk.IntentOption(
              type='ask_for_verification_iban_invalid_checksum',
              name='Kunden darauf hinweisen',
              recommended=True
          )
      )
  else:
      if input_data['date'] < datetime.now().strftime('%Y-%m-%d'):
          # If the customer wants to use an IBAN effective in the past, we default to tomorrow
          interaction.infos.append(
              sdk.IntentInfo(
                  type='warning',
                  message=f'Gewünschtes Gültigkeitsdatum {sdk.Helpers.format_date(input_data["date"])} liegt in der Vergangenheit. Gültigkeit auf morgen gesetzt'
              )
          )
          interaction.data['date'] = (datetime.now() + timedelta(days=1)).strftime('%Y-%m-%d')

      interaction.infos.append(
          sdk.IntentInfo(
              type='success',
              message='IBAN gültig'
          )
      )
      if input_data.get('payoutOnly'):
          msg = 'Im System  nur für Guthaben hinterlegen'
      else:
          msg = 'Im System hinterlegen'

      interaction.options.append(
          sdk.IntentOption(
              type='enter_into_system',
              name=msg,
              recommended=True
          )
      )

  # Return result
  print(json.dumps(interaction.model_dump()))
  ```
</CodeGroup>
