Why fuzzy matching fails EU Verification of Payee, and what to do about Greek, Cyrillic and Arabic names
Since 9 October 2025, every bank in the euro area has to check the payee's name before a euro credit transfer leaves your account. It's called Verification of Payee (VoP), and it comes from the EU Instant Payments Regula
Since 9 October 2025, every bank in the euro area has to check the payee's name before
a euro credit transfer leaves your account. It's called Verification of Payee (VoP),
and it comes from the EU Instant Payments Regulation. Bulgarian payment providers join
on 1 January 2027; banks in Czechia, Denmark, Hungary, Poland, Romania and Sweden on
9 July 2027.
The idea is simple. You type a name and an IBAN, and the payee's bank answers one of
four things: Match, Close Match (with the real name, so you can decide), No
Match, or Verification not possible. A Close Match on a misspelled name saves a
payment; a No Match on a fraudster's IBAN saves your money.
The European Payments Council standardised the API between banks in detail. What it did
not standardise is the matching itself. Each responding bank chooses its own
algorithm and carries the liability for it. So I spent the last few weeks building an
open-source one, nameproof, and this post
is about what I learned.
The problem with a fuzzy score
The obvious approach is a fuzzy string similarity: normalise both names, compute a score
from 0 to 100, and pick thresholds for Match and Close Match. Here's what a typical
order-insensitive fuzzy score (token-sort ratio, with accents folded) gives on real cases:
| Payer types | Account holds | Same entity? | Fuzzy score |
|---|---|---|---|
| John Smith | John Michael Smith | yes | 71 |
| Siemens AG | Siemens Energy AG | no | 74 |
| Jon Smyth | John Smith | yes (typo) | 84 |
| Maria Garcia | Mario Garcia | no | 92 |
| Giorgos Papadopoulos | ΞΞΉΟΟΞ³ΞΏΟ Ξ Ξ±ΟΞ±Ξ΄ΟΟΞΏΟ Ξ»ΞΏΟ | yes | 5 |
| Volodymyr Zelenskyi | ΠΠΎΠ»ΠΎΠ΄ΠΈΠΌΠΈΡ ΠΠ΅Π»Π΅Π½ΡΡΠΊΠΈΠΉ | yes | 5 |
Look at the first and last few rows. The same person scores lower than a different
person or a different company, so no single threshold works: set it high and you reject
real customers; set it low and Maria matches Mario. Fuzzy matching also scores
5/100 for names stored in another script, which across Greece, Cyprus, Bulgaria and
every bank with Ukrainian, Russian or Arabic-speaking customers is not an edge case.
A false No Match costs a customer and maybe a payment. A false Match is worse: it's
exactly the fraud VoP exists to stop. A score can't tell those apart; rules can.
What the EPC actually recommends
The EPC publishes non-binding Recommendations for the Matching Processes
(EPC288-23, version 2.0 from November 2026). In short:
- Clean up first: ignore case, remove punctuation and titles, and convert the stored name into Latin characters before comparing.
- Match only when the first and last name (or the company name) are identical after clean-up. Matching one holder of a joint account is enough.
- Close Match for small deviations: a spelling mistake, two swapped letters, an initial instead of the first name, a sound-alike letter, and, for companies, abbreviations or words in a different order.
- No Match for everything else.
That's a much better foundation than a threshold. "Identical after clean-up" is
explainable to a customer, an auditor and a regulator. The hard part is making the
clean-up good enough.
How nameproof does it
from nameproof import match
match("Giorgos Papadopoulos", ["ΞΞΉΟΟΞ³ΞΏΟ Ξ Ξ±ΟΞ±Ξ΄ΟΟΞΏΟ
λοΟ"])
# MATCH, explanation=('transliterated',)
match("Jon Smyth", ["John Smith"])
# CLOSE_MATCH, suggested_name='John Smith', explanation=('fuzzy_token',)
match("Maria Garcia", ["Mario Garcia"])
# CLOSE_MATCH: a spelling difference is never a Match
Every name is expanded into the spellings worth comparing, each with a trust level:
- Official romanisations can Match. Greek uses ELOT 743, the scheme in Greek passports. Cyrillic uses ICAO 9303, BGN/PCGN, the Bulgarian and Ukrainian national systems and Serbian Latin, chosen by the letters present. ΠΠΎΠ²Π°ΠΊ ΠΠΎΠΊΠΎΠ²ΠΈΡ becomes Novak ΔokoviΔ and then Djokovic.
- Informal romanisations only Close Match. Yannis for ΞΞΉΞ¬Ξ½Ξ½Ξ·Ο is a common spelling, but it isn't a standard, so the payer sees the real name.
-
Arabic never Matches. Arabic usually leaves short vowels unwritten, so nameproof
compares consonant skeletons: Mohammed, Muhammad and Ω
ΨΩ
Ψ― all reduce to
mhmd. That's useful, but lossy: Mohamed and Mahmoud share a skeleton too. So Arabic gives at most a Close Match, and Ahmed vs ΨΨ§Ω Ψ― (Hamid) is still No Match.
On top of that come the details that make names hard: diacritics (WaΕΔsa), letter
expansions (MΓΌller = Mueller), word order (Hungarian puts the surname first),
particles (van, de, al), hyphenated names, titles, generational suffixes, joint
accounts, patronymics, and about 1,400 company legal forms from the GLEIF ISO 20275
list. GmbH is recognised as the same legal form as Gesellschaft mit beschrΓ€nkter
Haftung (a Close Match under the EPC rules, since it's written differently), while
GmbH vs AG is flagged as a different kind of company.
Every verdict comes with stable reason codes (transliterated, fuzzy_token,
missing_name_partβ¦) that never contain any part of a name, and every policy has a
version, so the same input always gives the same answer.
Measuring it in public
I built nameproof-bench: 6,000 labelled name pairs from Wikidata across Greek,
Cyrillic, Arabic, seven Latin-script languages and companies. For privacy it uses only
people who have died. Variations include typos, initials, reordered and ASCII-typed
names; negatives include different people who share a surname.
| nameproof | token-sort fuzzy | Levenshtein | exact | |
|---|---|---|---|---|
| Same person wrongly rejected | 9.5% | 63.8% | 76.5% | 89.8% |
| Same person matched automatically | 41.7% | 26.2% | 11.3% | 10.2% |
| Different people falsely matched | 0.0% | 0.0% | 0.0% | 0.0% |
Everything nameproof neither matches nor rejects is a Close Match, which is by design
for typos and initials. I kept the hard cases in rather than filtering them out:
exonyms (John Paul II / Jan PaweΕ II) and company names that were translated rather
than transliterated. Removing them would have flattered my own numbers.
The review that humbled me
Before making the repository public I asked for an adversarial review: try to make
nameproof say Match for two different parties, or reveal an account holder's name. It
found 17 real problems, and six of them were false Matches:
- Siemens AG vs Siemens Energy AG was a Match: the extra word passed as a "middle name".
- Hassan Ibn Ali vs Hassan Abu Ali (son of vs father of) was a Match, because particles were dropped on both sides.
- Smith vs Smith AG was a Match, because removing the legal form left one word on each side.
- Sending just the word "GmbH" returned the account holder's full company name as a Close Match. Anyone with an IBAN could use that to look up names.
- One crafted request took 20 seconds of CPU.
My benchmark showed 0.0% false matches throughout, because its negatives weren't
adversarial. The lesson I took: every safety check has to look at the name as it was
before you removed anything. Titles, legal forms, particles and the other holders of a
joint account all make a name shorter, and a shorter name looks more like everything.
nameproof now counts words before stripping, needs a real name word (not a particle,
title or legal form) to justify revealing a name, and caps the work per request. All 17
cases are permanent regression tests.
Try it
- In your browser, where nothing you type leaves the page: nadaboulaares.github.io/nameproof
-
As a library:
pip install nameproof -
As an EPC VoP responder (EPC103-24 v1.1.1, tested against the EPC reference test
cases):
docker pull ghcr.io/nadaboulaares/nameproof
It's Apache-2.0 and pure Python, and it runs entirely on your own infrastructure.
If you work on VoP, Confirmation of Payee or KYC name screening, I'd love to hear where
you'd draw the line between Match and Close Match, and which names break your current
matcher. I'm also looking for two or three banks or payment institutions to pilot it
before their deadline: details here.
nameproof is a matching tool, not legal or compliance advice.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.