Replaced mollie
All checks were successful
Deploy / build-deploy (push) Successful in 3m47s

This commit is contained in:
Jorijn van der Graaf 2026-08-20 20:15:47 +02:00
commit df91762271
29 changed files with 3079 additions and 838 deletions

View file

@ -359,4 +359,66 @@ std::string ReferenceFromToken(std::string_view token) {
return out;
}
namespace {
// ISO 7064 mod-97-10 over an alphanumeric string, the same arithmetic that
// checks an IBAN: letters become two digits (A=10 … Z=35), everything is read
// as one long decimal number, and the remainder mod 97 is taken. Folded
// incrementally so no big-integer type is needed — the running value never
// exceeds 97*100+35, which fits an int comfortably.
//
// Returns nullopt on any character that is not [0-9A-Z], because silently
// skipping one would make two different references check out identically.
std::optional<int> Mod97(std::string_view s) {
int rem = 0;
for (const char c : s) {
if (c >= '0' && c <= '9') {
rem = (rem * 10 + (c - '0')) % 97;
} else if (c >= 'A' && c <= 'Z') {
const int v = c - 'A' + 10;
rem = (rem * 100 + v) % 97;
} else {
return std::nullopt;
}
}
return rem;
}
// The body an RF reference carries: the CC- reference with its hyphen dropped,
// because ISO 11649 permits only alphanumerics. "CC-2B6457" -> "CC2B6457".
std::string ReferenceBody(std::string_view token) {
const std::string human = ReferenceFromToken(token);
std::string body;
body.reserve(human.size());
for (const char c : human) {
if (c != '-') body += c;
}
return body;
}
} // namespace
std::string CreditorReferenceFromToken(std::string_view token) {
const std::string body = ReferenceBody(token);
// The check digits are computed over the body followed by "RF00" — the
// standard's rearrangement, prefix and placeholder moved to the end.
const std::optional<int> rem = Mod97(body + "RF00");
if (!rem) return {}; // unreachable for our own token alphabet
const int check = 98 - *rem;
return std::format("RF{:02}{}", check, body);
}
bool IsValidCreditorReference(std::string_view s) {
// "RF" + 2 check digits + 1..21 body characters.
if (s.size() < 5 || s.size() > 25) return false;
if (s[0] != 'R' || s[1] != 'F') return false;
if (s[2] < '0' || s[2] > '9' || s[3] < '0' || s[3] > '9') return false;
// Rearranged the same way the generator does it, then the whole thing must
// leave a remainder of exactly 1 — that is what mod-97-10 verification is.
std::string rearranged(s.substr(4));
rearranged += s.substr(0, 4);
const std::optional<int> rem = Mod97(rearranged);
return rem && *rem == 1;
}
} // namespace Catcrafts::Server