Skip to content

Setting the Expiry

A payload can carry an expiry, encoded as DDMMYYYYHHMM.

The Implicit Default

Whenever you set an amount and don't set an expiry, the package fills in the next calendar day:

php
RaastQr::iban($iban)->amount('2500')->toPayload();
// expiry is tomorrow at 23:59

This mirrors how one-off payment requests are normally used: an amount attached to a specific transaction shouldn't sit around indefinitely.

Setting It Yourself

Pass a string in YYYY-MM-DD or YYYY-MM-DDTHH:mm format:

php
RaastQr::make()->amount('2500')->expiry('2026-12-31');
RaastQr::make()->amount('2500')->expiry('2026-12-31T18:30');

Or any DateTimeInterface, including Carbon:

php
RaastQr::make()->amount('2500')->expiry(now()->addDays(3));
RaastQr::make()->amount($invoice->total)->expiry($invoice->due_at);

A date without a time is encoded at 23:59.

Removing It

An expiry only appears when there's an amount, so a static code has none:

php
RaastQr::iban($iban)->staticCode()->toPayload();
// no amount, no expiry

See Static QR Codes.

Keeping the Amount Without an Expiry

Sometimes you want a fixed figure that never goes stale — a donation code, a printed price list, a screenshot in documentation. withoutExpiry() keeps the amount and drops the date:

php
RaastQr::iban($iban)->amount('2500')->withoutExpiry()->toPayload();
// 0002020102120202000424…05032500…   dynamic, amount 2500, no tag 07

The code stays dynamic — the payer still types nothing — it simply carries no expiry field. ->expiry(false) does the same thing if you prefer it inline.

Note that null does not mean "no expiry"; it means "nothing specified", so the implicit default still applies:

php
RaastQr::iban($iban)->amount('2500')->expiry(null);   // expires tomorrow
RaastQr::iban($iban)->amount('2500')->expiry(false);  // no expiry at all

Do Not Rely On It

The expiry is not a security control

The expiry is only a field in the payload. Enforcing it is entirely up to the app doing the scanning, and some banking apps ignore it and will happily scan the code long after the date has passed.

Treat any QR you distribute as valid indefinitely. If a payment link genuinely must stop working, gate it in your own application:

php
Route::get('/invoices/{invoice}/qr', function (Invoice $invoice) {
    abort_if($invoice->isExpired(), 410);

    return RaastQr::make()->amount($invoice->total)->format('svg');
});

That way the code stops being served, which is something you actually control.

Validation

Impossible dates are rejected:

php
RaastQr::make()->amount('100')->expiry('2026-02-30')->toPayload();
// throws RaastQrException, reason() === 'INVALID_EXPIRY'

Checking the Default

php
RaastQr::defaultExpiry();   // '2026-09-04'

Because this uses Carbon, Carbon::setTestNow() works in your tests.


Made with ❤️ from Pakistan