„Pecunia non olet“
Titus Flavius Vespasianus
Nach den Beiträgen zu der eher unbekannten Leitweg-ID und den geläufigen Telefonnummern wird sich dieser Beitrag mit den beiden bekanntesten IDs aus dem Zahlungswesen beschäftigen, der IBAN und dem BIC.
Seit der SEPA Einführung werden für Überweisungen und Lastschriften nicht mehr Kontonummer und Bankleitzahl, sondern IBAN und BIC verwendet. SEPA steht für Single Euro Payments Area und standardisiert den Zahlungsverkehr innerhalb der Euro Zone.
Der Bank Identifier Code (BIC) ist ein international standardisierter Code für die eindeutige Identifikation von Banken und die International Bank Account Number (IBAN) ist eine international standardisierte Bankkontonummer.
Da sowohl BIC und IBAN international standardisiert sind, kann ihr Format leicht überprüft werden.
Der BIC besteht aus einem vierstelligen Bankcode, einem zweistelligen Ländercode, einem zweistelligen Code des Ortes und einer optionalen dreistelligen Kennzeichnung der Filiale. So ist die achtstellige BIC der Deutsche Bundesbank Frankfurt am Main etwa MARKDEFF
. MARK
ist der Bankcode für die Deutsche Bundesbank, DE
der Ländercode für Deutschland und FF
der Code für Frankfurt. Es gibt auch einen elfstelligen BIC MARKDEFFXXX
mit dem Kennzeichen für den Hauptsitz XXX
.
Ein Regulärer Ausdruck zum Prüfen eines BIC ist beispielsweise ([A-Z0-9]{4})([A-Z]{2})([A-Z0-9]{2})([A-Z0-9]{3})?
. Ein Identifier, der auf diesen Regulären Ausdruck nicht passt, kann schon einmal keine BIC sein.
Die IBAN ist ein wenig länger als der BIC, hat aber einen ähnlich strukturierten Aufbau. Sie startet mit einer zweistelligen Länderkennung, einer zweistelligen Prüfsumme, eine meist achtstelligen Bank-Identifikationsnummer und der Kontonummer. Die Bank-Identifikationsnummer ist in Deutschland die achtstellige Bankleitzahl und die Kontonummer ist in Deutschland maximal 10
Zeichen lang. Daher ist die IBAN in Deutschland 22
Zeichen lang.
Die Erwähnung von Deutschland macht schon einmal klar, die Länge der IBAN ist länderspezifisch, aber maximal 34 Zeichen.
Ein Regulärer Ausdruck zum Prüfen der IBAN ist beispielsweise ([A-Z]{2})([0-9]{2})([A-Z0-9]{11-30})
. Die 11
im Ausdruck ergibt sich aus der länge der norwegischen IBAN, sie ist die kürzeste weltweit mit 15
Zeichen.
Mit diesen Informationen kann eine rein syntaktische Validierung implementiert werden.
public class BicValidator implements ConstraintValidator<BIC, String> { public static final Pattern PATTERN = Pattern.compile("([A-Z0-9]{4})([A-Z]{2})([A-Z0-9]{2})([A-Z0-9]{3})?"); @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if (value == null) { return true; } Matcher matcher = PATTERN.matcher(value); return matcher.matches(); } }
Der erste Version des BicValidator
prüft den übergebenen String auf null
oder eine Übereinstimmung mit dem Pattern. In beiden Fällen ist die Validierung erfolgreich.
public class IbanValidator implements ConstraintValidator<IBAN, String> { public static final Pattern PATTERN = Pattern.compile("([A-Z]{2})([0-9]{2})([A-Z0-9]{11,30})"); @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if (value == null) { return true; } Matcher matcher = PATTERN.matcher(value); return matcher.matches(); } }
Die erste Version des IbanValidator
unterscheidet sich nur im verwendeten Pattern und einer anderen Validation-Annotation.
Eine rein syntaktische Validierung ist aber recht langweilig, denn ein String kann sehr leicht wie eine IBAN ausschauen, aber dennoch keine IBAN sein. Der erste Schritt zur Verbesserung ist die Nutzung der Prüfsumme. Die Prüfsumme wird mit dem MOD97-10 Algorithmus berechnet, der auch bei der Leitweg-ID Verwendung findet.
public class IbanValidator implements ConstraintValidator<IBAN, String> { public static final Pattern PATTERN = Pattern.compile("([A-Z]{2})([0-9]{2})([A-Z0-9]{11,30})"); private static final BigInteger VALUE_97 = BigInteger.valueOf(97); private static final int[] CHAR_VALUES = charValues(); private static int[] charValues() { String characterValues = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int[] result = new int['Z' + 1]; for (int i = 0; i < characterValues.length(); i++) { result[characterValues.charAt(i)] = i; } return result; } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if (value == null) { return true; } Matcher matcher = PATTERN.matcher(value); if (!matcher.matches()) { return false; } String countryCode = matcher.group(1); String basicBankAccountNumber = matcher.group(3); int checkDigit = Integer.parseInt(matcher.group(2)); StringBuilder builder = new StringBuilder(basicBankAccountNumber.length()); basicBankAccountNumber.chars().mapToObj(c -> CHAR_VALUES[c]) .map(String::valueOf).forEach(builder::append); countryCode.chars().mapToObj(c -> CHAR_VALUES[c]) .map(String::valueOf).forEach(builder::append); return checkDigit == 98 - new BigInteger(builder.append("00").toString()).mod(VALUE_97).intValue()) { } }
Zuerst werden Bankinformationsnummer, Kontonummer und Ländercode und 00
zu einer Zahl zusammengefügt und dann durch 97
geteilt. Das Ergebnis subtrahiert von 98
ergibt die Prüfsumme. Passt das Ergebnis nicht auf die Prüfsumme, dann ist die IBAN nicht valide. Passt die Prüfsumme, dann kann es sich um eine IBAN handeln.
Eine Kleinigkeit muss noch erwähnt werden. Bankinformationsnummer, Kontonummer können Buchstaben enthalten und der Ländercode enthält nur Buchstaben. Daher müssen die Buchstaben ersetzt werden. Der Buchstabe A
durch die Zahl 10
bis hin zum Z
durch die Zahl 35
.
Damit ist die Validierung der IBAN schon recht gut aber im nächsten Beitrag wird die Validierung, insbesondere für deutsche IBAN und BIC, noch weiter verbessert.