Skip to content

Handling Errors

Everything in the package throws a single exception type, RaastQrException, carrying a machine-readable code.

php
use RealRashid\RaastQr\Exceptions\RaastQrException;
use RealRashid\RaastQr\Facades\RaastQr;

try {
    $qr = RaastQr::iban($request->iban)->amount($request->amount)->toSvg();
} catch (RaastQrException $e) {
    return response()->json([
        'code' => $e->reason(),      // 'INVALID_AMOUNT'
        'message' => $e->getMessage(),
    ], 422);
}

The Codes

CodeCause
INVALID_OPTIONSNo IBAN was supplied and none is configured
INVALID_IBANWrong shape, wrong country, or failed checksum
INVALID_AMOUNTNot positive, too many decimals, or too long
INVALID_EXPIRYNot a real calendar date, or the wrong format
INVALID_CRC_INPUTInternal — the checksum received a non-string
INVALID_QR_OPTIONSBad colour, bad format, bad error correction, missing logo
RENDERER_FAILUREThe image layer could not draw the code

Branching on the Enum

$e->reason() gives you the string. $e->errorCode gives you the enum, which is what you want in a match:

php
use RealRashid\RaastQr\Enums\ErrorCode;

try {
    return RaastQr::iban($vendor->iban)->amount($order->total)->format('svg');
} catch (RaastQrException $e) {
    return match ($e->errorCode) {
        ErrorCode::InvalidIban => back()->withErrors([
            'iban' => 'This vendor\'s account number needs updating.',
        ]),
        ErrorCode::InvalidAmount => back()->withErrors([
            'amount' => 'This total cannot be encoded in a QR code.',
        ]),
        default => throw $e,
    };
}

Note the default => throw $e

Rethrowing anything you didn't plan for keeps genuine bugs visible instead of swallowing them into a generic 422.

$code vs $errorCode

RaastQrException extends InvalidArgumentException, so it inherits $code from PHP's Exception — an integer, always 0 here. The enum lives on its own property:

php
$e->code;        // 0, inherited from \Exception — ignore it
$e->errorCode;   // ErrorCode::InvalidAmount
$e->reason();    // 'INVALID_AMOUNT'

Catch Early, Not Late

The exception is thrown when you ask for output, not when you set a value:

php
$qr = RaastQr::iban('nonsense')->amount('also nonsense');   // fine, nothing computed
$qr->toPng();                                               // throws here

That means a builder returned from a controller throws during response rendering, which is a worse place to handle it. If the input came from a user, validate first:

php
$request->validate([
    'iban' => ['required', new PakistaniIban],
    'amount' => ['required', 'numeric', 'min:0.01', 'max:9999999.99', 'decimal:0,2'],
]);

See Validating IBANs.

Reporting

RaastQrException extends InvalidArgumentException, so Laravel reports it like any other exception. If bad IBANs are routine user input rather than bugs, tell the handler not to bother:

php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->dontReport(RaastQrException::class);
})

Only do this once you're validating input properly, or you'll lose sight of real failures.


Made with ❤️ from Pakistan