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:
RaastQr::iban($iban)->amount('2500')->toPayload();
// expiry is tomorrow at 23:59This 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:
RaastQr::make()->amount('2500')->expiry('2026-12-31');
RaastQr::make()->amount('2500')->expiry('2026-12-31T18:30');Or any DateTimeInterface, including Carbon:
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:
RaastQr::iban($iban)->staticCode()->toPayload();
// no amount, no expirySee 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:
RaastQr::iban($iban)->amount('2500')->withoutExpiry()->toPayload();
// 0002020102120202000424…05032500… dynamic, amount 2500, no tag 07The 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:
RaastQr::iban($iban)->amount('2500')->expiry(null); // expires tomorrow
RaastQr::iban($iban)->amount('2500')->expiry(false); // no expiry at allDo 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:
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:
RaastQr::make()->amount('100')->expiry('2026-02-30')->toPayload();
// throws RaastQrException, reason() === 'INVALID_EXPIRY'Checking the Default
RaastQr::defaultExpiry(); // '2026-09-04'Because this uses Carbon, Carbon::setTestNow() works in your tests.
Made with ❤️ from Pakistan