Bankkarte: IBAN pruefen + BIC/Bank offline aus Bundesbank-BLZ ausfuellen

Neuer Button "BIC & Bank aus IBAN abrufen" im Bankkarten-Modal fuellt BIC +
Banknamen automatisch und validiert dabei die IBAN-Pruefziffer (mod-97).
Leeres IBAN-Feld -> OK-Messagebox statt Anfrage.

Datenschutzfreundlich/offline: kein Dritt-Dienst. Nachschlag im eigenen
Backend ueber die Bundesbank-Bankleitzahlendatei (bankdata-germany) +
ibantools fuer die Pruefziffer. Die IBAN verlaesst nie den Server; zurueck
kommen nur oeffentliche Bankverzeichnis-Daten.

Endpoint: POST /api/bank-cards/iban-lookup (nur eingeloggt).
Wartung: bankdata-germany/ibantools ~quartalsweise per npm update ziehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 12:32:53 +02:00
co-authored by Claude Opus 4.8
parent aff3f7e84a
commit 73bd72bd9f
7 changed files with 196 additions and 6 deletions
@@ -0,0 +1,54 @@
import { Response } from 'express';
import { isValidIBAN } from 'ibantools';
import { bankDataByIBAN } from 'bankdata-germany';
import { ApiResponse, AuthRequest } from '../types/index.js';
/**
* IBAN-Lookup: prüft die Prüfziffer (mod-97, offline über ibantools) und liefert
* für deutsche IBANs BIC und Banknamen aus der Bundesbank-Bankleitzahlen-
* datei (offline über bankdata-germany). Die IBAN verlässt NICHT den Server; es
* findet KEIN externer Request statt. Zurückgegeben werden nur öffentliche
* Bankverzeichnis-Daten (BIC/Name), keine kundenbezogenen Informationen.
*
* Antwortformen (immer HTTP 200, sofern eine IBAN übergeben wurde):
* { valid: false } → Prüfziffer falsch (Tippfehler)
* { valid: true, bic, bankName, blz } → gültig + DE-Bank gefunden
* { valid: true, bic: null, bankName: null } → gültig, aber keine DE-Bankdaten
* (Nicht-DE-IBAN oder unbekannte BLZ)
*/
export async function lookupIban(req: AuthRequest, res: Response): Promise<void> {
try {
const raw = typeof req.body?.iban === 'string' ? req.body.iban : '';
const iban = raw.replace(/\s+/g, '').toUpperCase();
if (!iban) {
res.status(400).json({ success: false, error: 'Feld IBAN ist erforderlich.' } as ApiResponse);
return;
}
if (!isValidIBAN(iban)) {
res.json({ success: true, data: { valid: false, iban } } as ApiResponse);
return;
}
const country = iban.slice(0, 2);
const bank = country === 'DE' ? bankDataByIBAN(iban) : null;
res.json({
success: true,
data: {
valid: true,
iban,
country,
bic: bank?.bic ?? null,
bankName: bank?.bankName ?? null,
blz: bank?.blz ?? null,
},
} as ApiResponse);
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Fehler beim IBAN-Nachschlagen',
} as ApiResponse);
}
}