Skip to content
Constaia

How to validate a bank transfer receipt

Which fields to check on a transfer receipt, how to validate an IBAN with mod 97, how to match it to a registration and why a PDF does not prove payment.

By Constaia team5 min read

Also in: Español

Many clubs, schools and event organisers in Europe still take payments by bank transfer. The flow is familiar: the person pays from their bank, downloads the receipt or takes a screenshot, and uploads it with their registration. Someone, often a volunteer, opens it and decides whether it "looks right".

This article explains what to look at on a receipt, how to validate the IBAN with the official algorithm (with code you can copy), how to match it to the registration and, above all, what a receipt does not prove.

Which fields to check

A useful transfer receipt has at least:

FieldWhat to check
AmountMatches the fee (and the currency is the expected one).
DateWithin the registration period. Tell the order date and value date apart if both appear.
PayerWho paid. Not always the participant: parents, a club, a company.
BeneficiaryYour organisation, with the right name.
Beneficiary IBANYour account, not a similar one.
Payer IBANUseful for refunds and reconciliation.
Description or referenceIdentifies the registration (name, bib number, the reference you gave).

The description is the most overlooked field and the most useful. If your form gives a unique reference per registration (for example REG-2026-00123) and asks people to put it in the transfer description, reconciliation becomes much easier.

Validating the IBAN: ISO 13616 and mod 97

The IBAN is an international standard, ISO 13616, with SWIFT as the registration authority. It has up to 34 characters:

  • a 2-letter country code (ES, FR, DE…),
  • 2 check digits,
  • the national account number (BBAN), whose length depends on the country.

A Spanish IBAN, for example, has 24 characters: ES + 2 check digits + 4-digit bank code + 4-digit branch code + 2 national check digits + 10-digit account number.

The check digits use the MOD 97-10 method from ISO/IEC 7064. The algorithm:

  1. Remove spaces and convert to upper case.
  2. Move the first 4 characters to the end.
  3. Replace each letter with two digits: A = 10, B = 11 … Z = 35.
  4. Compute the remainder of that number divided by 97.
  5. If the remainder is 1, the IBAN is valid.

According to the same source, this method catches all single-character substitution errors and all or nearly all transpositions of two adjacent characters. In other words, it catches typos and misreadings.

JavaScript code

The resulting number is too long for a Number, so the remainder is computed digit by digit:

iban.ts
const IBAN_LENGTHS: Record<string, number> = { ES: 24, PT: 25, FR: 27, DE: 22, IT: 27 };

export function isValidIban(input: string): boolean {
  const iban = input.replace(/\s+/g, "").toUpperCase();
  if (!/^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$/.test(iban)) return false;

  const expected = IBAN_LENGTHS[iban.slice(0, 2)];
  if (expected && iban.length !== expected) return false;

  const rearranged = iban.slice(4) + iban.slice(0, 4);
  let remainder = 0;
  for (const ch of rearranged) {
    const code = ch.charCodeAt(0);
    const digits = code >= 65 ? String(code - 55) : ch; // A=10 … Z=35
    for (const d of digits) remainder = (remainder * 10 + Number(d)) % 97;
  }
  return remainder === 1;
}

isValidIban("GB82 WEST 1234 5698 7654 32"); // true
isValidIban("ES91 2100 0418 4502 0005 1332"); // true
isValidIban("ES91 2100 0418 4502 0005 1333"); // false: one digit changed

The length table in the example only covers a few countries; in production, use the full per-country length registry.

If you just want to check a single IBAN, try our free IBAN validator, which runs this calculation in your browser.

What mod 97 does not tell you

A valid IBAN is well formed. It does not mean the account exists, is open, or belongs to the person named on the receipt.

Matching it to the expected registration

Validating the IBAN is only the first step. The real question is: does this receipt belong to this registration? To answer it, compare against what you expect:

  • Expected amount: the exact fee. Watch out for discounts, T-shirt options or one-day insurance that change the total.
  • Expected beneficiary IBAN: yours. A receipt to another account, even a valid one, is not a payment to you.
  • Expected reference: the one you gave on the form, searched for in the description.
  • Date: within the period.

With these four comparisons you can sort almost every case into three groups: matches, doesn't match, or needs a look (for example, the right amount but no reference in the description).

A receipt does not prove the money arrived

This is the most important point in the article. A PDF or a screenshot from a banking app shows that someone created a transfer order, or an image that looks like one. It does not prove that:

  • the order was not cancelled or returned afterwards,
  • the amount was credited to your account,
  • the document has not been edited.

The only proof of payment is your bank statement, that is, reconciliation: the transaction appears in your account with that amount and that reference. A receipt helps you get ahead (hold a provisional place, catch mistakes early), but it does not replace reconciliation.

A sensible flow:

  1. The person uploads the receipt with their registration.
  2. Automatic validation: document type, IBAN, amount, reference, date.
  3. If it matches, the place is provisional.
  4. When the statement is reconciled, the place becomes confirmed. If the payment does not show up within your deadline, you follow up.

Signs of editing or screen photos

Some receipts are edited: the amount, date or beneficiary is changed in a PDF or a screenshot. Some tools look for signals (mixed fonts, retouched areas, a photo of a screen instead of the original document). In Constaia those signals appear as warnings:

  • edited_suspected: signs that the document has been modified.
  • screen_photo_suspected: it looks like a photo of a screen.

They are signals, not proof. A receipt without warnings can still be edited, and one with warnings can be perfectly legitimate (plenty of people photograph their computer screen). Use them to send a case to review, not to reject anyone automatically. And again: the proof is the statement.

How Constaia does it

The payment_receipt type extracts amount, currency, date, payer_name, payer_iban, beneficiary_name, beneficiary_iban, concept and reference, validates the IBANs and lets you compare against what you expect with expected_amount, expected_iban and expected_reference:

curl https://api.constaia.com/v1/analyze \
  -H "Authorization: Bearer $CONSTAIA_API_KEY" \
  -F file=@receipt.pdf \
  -F 'options={
    "expect": "payment_receipt",
    "checks": {
      "expected_amount": 45.00,
      "expected_iban": "ES9121000418450200051332",
      "expected_reference": "REG-2026-00123"
    },
    "metadata": { "registration_id": "00123" },
    "storage": "none",
    "language": "en"
  }'

You get a valid, invalid or review verdict with reasons, the extracted fields and the warnings. What it does not do: it does not query your bank or confirm the payment. You still need reconciliation for that.

More in checks, verdicts and the document type catalogue.

In short

  • Check amount, date, payer, beneficiary, IBAN and description.
  • Validate the IBAN with mod 97 and the country length. A valid IBAN does not prove the account exists.
  • Match against what you expect: amount, your IBAN and a unique reference per registration.
  • A receipt gets you ahead, but payment is only confirmed by the bank statement.
  • edited_suspected and screen_photo_suspected are reasons to review, not to reject.

If you want to automate the first pass over transfer receipts, create a free account: 250 credits a month and test keys that don't use credits.

Sources

  1. 01Wikipedia — International Bank Account Number (IBAN)