Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Czqrpayment Laravel Package

rikudou/czqrpayment

PHP package for generating Czech QR payment codes (QR Platba) from account, amount, and payment details. Produces standards-compliant QR strings for banking apps, with support for IBAN, variable/specific/constant symbols, and Laravel-friendly integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require rikudou/czqrpayment:^5.3
# only if you want raster images (PNG) instead of SVG:
composer require endroid/qr-code

There is no Laravel service provider, facade, or config file — it's a plain, framework-agnostic library. You wire it up yourself in AppServiceProvider or, better, in a small app-level service class.

Where to look first

  1. README.md — usage block for CzechQrPayment (the only class you'll touch 95% of the time).
  2. The QrPayment interface — this is your contract; code against it, not the concrete class, so you can swap in SlovakQrPayment/EuropeanQrPayment or your own implementation later.
  3. The two output methods: getQrString() (the raw SPD payload, e.g. SPD*1.0*ACC:CZ65...*AM:1234.50*CC:CZK*X-VS:2024001234*MSG:...) and getQrCode() (a QR image object you can render as PNG/SVG).
  4. setOptions() — v5.3.x added/covered the instant payment option here (see Implementation Patterns #8). Czech instant payments are a real user-facing difference, so check the README for the exact option key/setter before you build on it.

Smallest possible first use case — a QR code on an invoice PDF:

use Rikudou\CzQrPayment\CzechQrPayment;

$payment = new CzechQrPayment();
$payment
    ->setAccount('CZ6508000000192000145399')
    ->setAmount(1234.50)
    ->setCurrency('CZK')
    ->setVariableSymbol('2024001234')
    ->setRecipientName('Acme s.r.o.')
    ->setMessage('Faktura 2024001234');

$spd = $payment->getQrString();      // log this — it's the whole point
$qr  = $payment->getQrCode();        // render/attach

Method names above follow the README as of the v5.3.1 release; confirm the exact setter names (setVariableSymbol vs setVariableSymbolString, etc.) and the setOptions() keys against the README before you build on them.

Assessment up front: the library's real output is a string; the QR encoding is delegated to bacon/bacon-qr-code / endroid/qr-code. So its value is SPD formatting plus validation of the Czech payment rules. 29 stars and 0 dependents means bus-factor risk, but the underlying format (Czech Banking Association "QR Platba" / SPD 1.0) is a frozen public standard — worst case you reimplement the ~60 lines of payload building and keep everything else. Treat it as vendorable, not load-bearing.

Release cadence note (v5.3.1): this was a patch release — test coverage for instant payment, a funding link, a docs link to a Hungarian QR generator, and a bump of the required QR code provider version. No new public API names, no deprecations. The only actionable item is the dependency floor: run composer update rikudou/czqrpayment --with-dependencies, then diff composer.lock and re-run your getQrCode() render test (see Gotchas).

Implementation Patterns

1. Wrap it — never call it from a controller. One service that takes a domain object and returns a DTO with { spd, payload }:

final class QrPaymentBuilder
{
    public function forInvoice(Invoice $invoice): QrPayload
    {
        $p = new CzechQrPayment();
        $p->setAccount(config('payments.iban'))
          ->setAmount($invoice->total_halere / 100)
          ->setCurrency('CZK')
          ->setVariableSymbol($invoice->variable_symbol)
          ->setRecipientName(config('payments.recipient_name'))
          ->setDueDate($invoice->due_at)          // YYYYMMDD in the payload
          ->setMessage("Faktura {$invoice->number}");

        return new QrPayload($p->getQrString(), $p->getQrCode());
    }
}

This is your swap point if you ever move to EPC QR (EUR) or a Slovak PAY by square format — the rest of the app never sees the library.

2. Bind per-resolution, not as a singleton. The object is mutable state; a shared instance will leak the previous invoice's amount into the next request under Octane/queues.

$this->app->bind(QrPaymentBuilder::class);
// or ->scoped() if you're on Octane and need per-request caching

3. Blade component emitting inline SVG. Best for PDFs (dompdf/snappy), retina screens, and emails — no GD dependency, no temp files:

{{-- resources/views/components/qr-payment.blade.php --}}
<svg {!! $qr->getSvgAttributes() !!}>
    {!! $qr->getSvgBody() !!}
</svg>

The exact SVG accessors come from the QR provider, not from this package — v5.3.1 bumped that provider's required version. If the properties/methods differ on your resolved version, fall back to $qr->writeString() into a data: URI or write the SVG to storage once per invoice.

For a downloadable/embeddable endpoint, a signed route is the cleanest:

Route::get('/pay/{invoice:uuid}/qr.svg', PaymentQrController::class)
    ->middleware('signed')
    ->name('pay.qr');

Cache the generated payload on the model and bust it with a hash of the mutable inputs (amount, VS, currency, due date, options), so a corrected invoice doesn't ship a stale QR.

4. Persist the SPD string on the invoice. Store spd_string + spd_hash. It's ~150 bytes, it makes the QR reproducible from the DB alone (no re-deriving business rules), and it's the single best debugging artifact you'll have. It also makes provider upgrades a non-event: existing invoices keep rendering, only new ones use the new builder.

5. Money handling. Keep amounts as integer haléře; convert with number_format($halere / 100, 2, '.', ''). Never feed a localized string — 1 234,50 will be misinterpreted or rejected.

6. Generate once, reuse in mail + PDF. Build the payload when the invoice is issued (a queued job or model observer), attach the SVG/PNG to the mailable, and let the PDF view read the stored string.

7. Test the string, not the pixels. QR PNGs are effectively unassertable; the SPD payload is not:

$this->assertStringContainsString('*AM:1234.50*', $payload->spd);
$this->assertStringContainsString('*X-VS:2024001234*', $payload->spd);

Decode a few real payloads manually in a bank app once during development, then never again.

8. Consider the instant-payment option explicitly. The 5.3.x line exposes an instant payment flag through setOptions(). Behaviour differs by bank — the QR still scans everywhere, but the payer's app may preselect an instant/express scheme. Make it a config flag rather than hardcoding it, so finance can toggle it without a deploy:

$p->setOptions([
    // check README for the exact instant-payment key on v5.3.x
]);

Include it in the hash you cache the payload against — flipping the flag must invalidate stored QRs, or you'll be shipping the old preference forever.

9. Upgrade smoke test. After any 5.x bump, generate one known-good payload in a feature test and assert the full string with assertSame(), then render the image once. That catches provider-bump regressions (SVG/PNG writing, encoding mode) without pinning you to pixel diffs.

Gotchas and Tips

  • Singleton trap. Covered above but it's the #1 production bug: a cached CzechQrPayment instance carries AM, X-VS, MSG, and options across requests.
  • No auto-discovery. composer require gives you nothing new in config/ or php artisan about. Don't hunt for a config file; publish your own config/payments.php with IBAN/recipient.
  • Dependency floor moved in v5.3.1. The required QR code provider version was bumped. If you pin endroid/qr-code (or an old bacon/bacon-qr-code) elsewhere, Composer may resolve an older pair or conflict outright. Upgrade both together (--with-dependencies) and re-run the render test; don't let a transitive QR-provider downgrade silently change your SVG output.
  • Diacritics. SPD allows a narrow charset; "Řeřicha s.r.o." must become "Rericha s.r.o.". The library strips it in some versions — do not rely on that. Run names and messages through Str::ascii() yourself so behaviour is identical across upgrades.
  • Hard length caps. MSG ~60 chars, RN ~35, VS/SS/KS ≤10 digits. Truncate in the builder and log when you do; a silently truncated message on 10,000 invoices is a support nightmare.
  • Reserved characters. * is the field separator — never let it into a message or recipient name. Validate with a regex on your builder inputs.
  • IBAN, not a Czech account number. 123456789/0100 must be converted to CZ…. Use rias/cz-iban or implement the mod-97 conversion once in a helper — don't do string surgery per call site.
  • CZK ≠ EUR. QR Platba is the Czech SPD format (mainly CZK). Cross-border EUR payments use EPC QR (BCD\n002\n1\nSCT…), a completely different payload that Czech/Slovak banking apps often won't scan the same way. If you need EUR, use a separate implementation and a separate QR code — do not stuff CC:EUR into SPD and hope. (The v5.3.1 docs link to a Hungarian generator; that's a different national format again — useful for reference, not for reuse.)
  • PNG needs GD/Imagick. CI containers and slim production images frequently lack it; getQrCode()->writeString() then dies at runtime. Prefer SVG, or add the extension to your Dockerfile and smoke-test the route.
  • Print sizing. Follow the Czech standard's recommended sizes and keep the quiet zone. Test a printed invoice with at least two bank apps (Fio + Air Bank are good canaries) before shipping — a QR that's 1.5 cm wide scans fine on screen and fails on paper.
  • Payable-state check. Don't render a QR for a paid/cancelled invoice; the customer's app has no idea and will happily send money twice.
  • VS collisions. Using the invoice number as VS is idiomatic and correct — until you exceed 10 digits or reuse numbers across series. Pick a numeric sequence explicitly for payments.
  • Instant payment ≠ instant settlement. The flag is a preference the payer's app may honour. Never mark an invoice paid based on it, and never assume the money arrives before your normal reconciliation runs.
  • Debugging recipe. Log getQrString(), paste it into any SPD/QR decoder, and compare field-by-field. Faster than reading the library source.
  • Extension point. If you need a different renderer (e.g. specific SVG markup or a cached CDN URL), implement the library's QrPaymentRenderer interface rather than post-processing bytes. Your own QrPayload DTO is the better long-term seam, though.
  • Pin the version. "rikudou/czqrpayment": "^5.3" — a single-maintainer package at ~29 stars can ship a breaking minor; don't float on *. Treat patch bumps as safe but still run your one-payload assertSame test, since patch releases can (and here did) move transitive dependencies.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core