NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
QR for Laravel

SEPA credit transfer (EPC)

EpcPayment implements EPC069-12 v3.1, the SEPA credit-transfer QR code (often called GiroCode). Amounts are money-for-laravel Money values — exact minor units, no floating-point rounding:

use RoundlyConsulting\Money\Money;
use RoundlyConsulting\Qr\Enums\{EpcCharset, EpcVersion};
use RoundlyConsulting\Qr\Payloads\Payments\Epc\EpcPayment;

$svg = Qr::epc(new EpcPayment(
    name: 'Roundly Consulting s.r.o.',
    iban: 'SK46 1100 0000 0029 4714 7960',
    bic: 'TATRSKBX',                        // optional for EEA IBANs in version 002
    amount: Money::ofMinor(12550, 'EUR'),   // EUR only, 0.01 – 999 999 999.99
    purpose: 'GDDS',
    reference: 'RF18539007547034',          // ISO 11649 structured reference …
    // text: 'Invoice 2026-0042',           // … or free text (not both)
    information: 'Thank you',
    version: EpcVersion::V002,
    charset: EpcCharset::Utf8,
    strictCharset: false,
))->svg();

Arguments

ArgumentRule
nameBeneficiary name, 1–70 characters.
ibanString or Iban; must be a valid IBAN.
bicOptional. Required in version 001, and in 002 for IBANs outside the EEA.
amountOptional Money, EUR only, 0.01 – 999 999 999.99.
purposeOptional purpose code, 1–4 uppercase letters or digits (e.g. GDDS).
referenceStructured reference, 1–35 characters; an RF… value must pass ISO 11649.
textUnstructured remittance text, 1–140 characters — not together with reference.
informationBeneficiary-to-originator information, 1–70 characters.
version / charset / strictCharsetnull takes the payments.epc.* config values through Qr::…; a direct toQrString() uses the standard’s defaults (002, UTF-8, not strict).

What the standard fixes

  • LF-separated elements without a trailing separator, at most 331 bytes in the declared character set — longer payloads throw PaymentPayloadTooLongException.
  • Error correction M, at most version 13, byte segmentation, no ECI and no boost — these options are locked.
  • Version 001 requires a BIC; version 002 without a BIC requires an EEA IBAN (EU 27 + IS, LI, NO).
  • Eight character sets (UTF-8 and ISO-8859-1, -2, -4, -5, -7, -10, -15); a character the charset can’t represent throws an unrepresentable error naming the field.
  • strictCharset restricts name, text, information and reference to the SEPA Latin subset.
  • Text is NFC-normalised before length checks when ext-intl is available.

Parsing a scanned code

$payment = EpcPayment::fromString($scannedPayload);   // parse (LF or CRLF)

$carried = Qr::epc($payment)->payload()->toQrString(); // the exact string the code carries (config applied)
$standard = $payment->toQrString();                    // standard defaults for null fields — ignores config

The serialised form follows the standard — this is the example from EPC069-12 §2.3, reproduced byte for byte in the package’s tests:

BCD
001
1
SCT
BHBLDEHHXXX
Franz Mustermänn
DE71110220330123456789
EUR12.3
GDDS
RF18539007547034

EPC codes default to Personal sensitivity; lower it to Public for a code printed on a public invoice page.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.