Reference

Errors

Every error Harbour can answer with, and what to do about each one.

Every error Harbour answers with carries a machine-readable code and a sentence a person can read. The sentences are written to be shown to a business owner, not decoded by one, so validation messages are safe to put on screen as they are.

The shape of an error

JSON
{
  "error": {
    "code": "validation_failed",
    "message": "Amount must be at least ₦100.",
    "fields": { "amount_kobo": "Amount must be at least ₦100." }
  }
}

In the SDK this becomes a typed exception. Everything at runtime extends Nordaxiz\HarbourConnect\Exception\HarbourException, so one catch can be your backstop.

The exceptions

ExceptionHTTP · codeWhat happened, and what to do
AuthenticationException401 unauthorizedMissing, wrong or revoked key. Check which environment you are in; do not retry.
PermissionException403 forbiddenThe key may not do this. Approving a refund, for example, is never a key's to do.
PermissionException403 email_unverifiedThe account has not confirmed its email, so live actions are held. The business fixes this in the dashboard.
NotFoundException404 not_foundNo such reference in this mode. A live reference with a test key looks exactly like this.
IdempotencyConflictException409 idempotency_conflictThe same key was used with a different body. Use a new key for a genuinely new request.
ValidationException422 validation_failedA field is wrong. getFieldErrors() says which, in plain language.
ValidationException422 no_providerThe business has no provider connected in this mode. They connect one in Providers.
RateLimitException429 rate_limitedToo many requests. getRetryAfterSeconds() says how long to wait.
HarbourException502 provider_unavailableThe provider (Paystack, Flutterwave or OPay) is down or refusing. Harbour keeps polling; try again shortly.
HarbourException500 server_errorOur fault, and logged. Safe to retry with the same idempotency key.
ApiConnectionException— no responseDNS, TLS, timeout. The request may still have happened: retry with the same idempotency key.
SignatureVerificationException— webhookA delivery failed its signature or timestamp check. Answer 400 and look at which secret you used.

Mistakes Harbour catches before anything is sent — a float amount, an unknown option, a public key used as a secret key, a plain-HTTP base — throw Nordaxiz\HarbourConnect\Exception\InvalidArgumentException, which extends PHP's own. Those are bugs in the calling code, not runtime conditions.

Handling them well

PHP
SDK language
  • PHP
  • Node.jsComing soon
  • PythonComing soon
  • JavaComing soon
  • GoComing soon
  • .NETComing soon
Any language can call the API today
<?php

use Nordaxiz\HarbourConnect\Exception\ApiConnectionException;
use Nordaxiz\HarbourConnect\Exception\HarbourException;
use Nordaxiz\HarbourConnect\Exception\RateLimitException;
use Nordaxiz\HarbourConnect\Exception\ValidationException;

try {
    $checkout = Harbour::charge($order->total_kobo, $order->email, ['reference' => $order->reference]);
} catch (ValidationException $e) {
    // something about the request is wrong and will stay wrong: show it, do not retry
    return back()->withErrors($e->getFieldErrors());
} catch (RateLimitException $e) {
    return $this->retryAfter($e->getRetryAfterSeconds() ?? 60);
} catch (ApiConnectionException $e) {
    // no answer at all. The charge may or may not exist: retry with the SAME idempotency key
    report($e);
    return back()->with('error', 'We could not reach payments. Try again in a moment.');
} catch (HarbourException $e) {
    report($e->getErrorCode() . ': ' . $e->getMessage());
    return back()->with('error', 'Payments are having a moment. Try again shortly.');
}

Every HarbourException has getHttpStatus() and getErrorCode(): log the code, not the message, so a reworded sentence never breaks a dashboard you built on top of your logs.

Showing validation errors

PHP
SDK language
  • PHP
  • Node.jsComing soon
  • PythonComing soon
  • JavaComing soon
  • GoComing soon
  • .NETComing soon
Any language can call the API today
<?php

try {
    Harbour::charge($amountKobo, $email);
} catch (ValidationException $e) {
    $e->getFieldErrors();   // ['amount_kobo' => 'Amount must be at least ₦100.']
    $e->getMessage();       // the same sentence, for one-line forms
}