Files
mareike/app/Support/CreditorId.php

67 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support;
/**
* Zustandslose Helfer für die SEPA-Gläubiger-Identifikationsnummer (Creditor Identifier).
*
* Aufbau nach EPC262-08: Ländercode, zwei Prüfziffern, drei Zeichen Geschäftsbereichskennung, dann die
* nationale Kennung. In Deutschland vergibt die Bundesbank sie mit 18 Zeichen (`DE98ZZZ09999999999`).
*
* Die Prüfziffer ist der Teil, auf den es ankommt: Eine Lastschriftdatei mit falscher Gläubiger-ID lehnt
* die Bank als Ganzes ab -- und zwar erst beim Hochladen, wenn die Vorabankündigungen schon verschickt
* sind.
*/
final class CreditorId
{
/** Länge der Gläubiger-ID je Ländercode, soweit sie national festgelegt ist. */
private const array LENGTHS = [
'DE' => 18,
];
/** Leerzeichen raus, Großbuchstaben -- die kanonische Form, die gespeichert wird. */
public static function normalize(string $creditorId): string
{
return strtoupper(preg_replace('/\s+/', '', $creditorId) ?? '');
}
public static function isValid(string $creditorId): bool
{
$creditorId = self::normalize($creditorId);
if (preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{3}[A-Z0-9]{1,28}$/', $creditorId) !== 1) {
return false;
}
$expected = self::LENGTHS[substr($creditorId, 0, 2)] ?? null;
if ($expected !== null && strlen($creditorId) !== $expected) {
return false;
}
return self::checksum($creditorId) === 1;
}
/**
* Mod 97-10 wie bei der IBAN, aber ohne die Geschäftsbereichskennung (Stellen 5 bis 7): Sie darf
* der Gläubiger frei wählen, ohne dass sich die Prüfziffer ändert. Geprüft wird deshalb die nationale
* Kennung, gefolgt von Ländercode und Prüfziffern; Buchstaben zählen als Position + 9 (A = 10 … Z = 35).
*/
private static function checksum(string $creditorId): int
{
$rearranged = substr($creditorId, 7) . substr($creditorId, 0, 4);
$remainder = 0;
foreach (str_split($rearranged) as $char) {
$value = ctype_digit($char) ? $char : (string) (ord($char) - 55);
foreach (str_split($value) as $digit) {
$remainder = ($remainder * 10 + (int) $digit) % 97;
}
}
return $remainder;
}
}