Qora by Laayo
Get API keys

Errors

Every failure returns the same envelope with a stable machine-readable code, so you can branch on failures with the same confidence as on success.

The envelope

json
{
  "error": {
    "type": "validation_error",
    "code": "parameter_invalid",
    "message": "One or more parameters were invalid.",
    "errors": {
      "amount": ["Amount must be at least 1000 paisa (रु 10.00)."]
    },
    "doc_url": "https://qora.laayo.com.np/docs/errors"
  }
}
FieldUse it for
typeBroad category. Useful for logging and metrics.
codeBranch on this. Stable across releases.
messageHuman-readable. Safe to log; do not parse.
errorsPresent on validation failures only: field name to messages.

Status codes

StatusMeaningRetry?
200 / 201Success.—
400Malformed request.No — fix the request.
401Authentication failed.No — fix the key.
403Authenticated but not permitted.No.
404No such object for this key's mode.No.
409Conflicts with current state.Sometimes — read the code.
422Well-formed but invalid.No — fix the parameters.
429Rate limited.Yes, after backing off.
5xxOur problem, or Fonepay's.Yes, with the same idempotency key.

Codes you should handle

CodeStatusWhat it means and what to do
authentication_required401No key sent. Check your Authorization header.
invalid_api_key401Unknown, revoked, or a publishable key used server-side.
live_mode_unavailable403Live key used before verification. Finish KYC.
account_suspended403Contact support.
parameter_invalid422Read error.errors for the offending field.
credentials_missing422No Fonepay credentials for this mode. Add them in Settings.
gateway_unavailable502Fonepay declined or timed out. Retry with the same idempotency key.
gateway_unreachable503We could not reach Fonepay. Retry shortly.
payment_already_succeeded409Cannot cancel a paid payment.
idempotency_in_progress409A duplicate is in flight. Wait, then retry.
idempotency_key_reuse409Same key, different body. A bug in your caller.
rate_limit_exceeded429Back off. See rate limits.

Handling them

php
$response = $api->post('/v1/payments', ['amount' => $order->total_paisa]);

class="tok-key">if ($response->failed()) {
    $error = $response->json('error');

    match($error['code']) {
        // Ours to fix: alert someone, and fall back to another tender.
        'credentials_missing', 'invalid_api_key' => alert_ops($error['message']),

        // Transient: safe to retry with the same idempotency key.
        'gateway_unavailable', 'gateway_unreachable' => retry_later($order),

        'rate_limit_exceeded' => backoff_and_retry($order),

        default => show_cashier('Could not create the QR. Take payment another way.'),
    };
}

A 5xx never leaks internal detail — no table names, paths, or query fragments. If you need us to trace one, send the timestamp and the payment reference.