contoweb/laravel-abacus-api
A Laravel API client for Abacus ERP. It simplifies connecting your application to Abacus web services, handling configuration and HTTP requests so you can query ERP data and integrate business records with clean, familiar Laravel code.
composer require contoweb/laravel-abacus-api
The package is auto-discovered by Laravel. If it ships a config file, publish it and put credentials in .env rather than in code:
php artisan vendor:publish --tag=abacus-api-config
Where to look first, in this order:
src/AbacusClient.php — the entry point; toException() is the status-code switchboard that decides which exception type you get back.src/Exceptions/ — the full exception hierarchy. As of v0.2.0 it contains AbacusNotFoundException (404) plus the existing mappings for 400, 403 and 429.config/abacus-api.php (if present) — base URL, credentials, timeouts.First use case: resolve the client out of the container and let it throw typed exceptions, then branch on them.
use Contoweb\AbacusApi\AbacusClient;
use Contoweb\AbacusApi\Exceptions\AbacusNotFoundException;
public function __construct(private AbacusClient $abacus) {}
public function show(string $id)
{
try {
return $this->abacus->customer($id);
} catch (AbacusNotFoundException $e) {
// record genuinely does not exist in Abacus
return null;
}
}
Inject, don't instantiate. Bind the client through the container (constructor injection or app(AbacusClient::class)) so config, HTTP client and fakes are interchangeable in tests. Avoid new AbacusClient(...) in application code.
Treat the exception type as the API contract. AbacusClient::toException() is the single place where HTTP status becomes a PHP type. Design your call sites around that mapping rather than inspecting status codes or message strings:
| Status | Meaning in practice | Typical handling |
|---|---|---|
400 |
Bad payload / rejected input | Do not retry. Validate upstream, surface to the user. |
403 |
Auth/permission problem | Do not retry. Alert — this is a config or credential bug. |
404 |
Record not found (new in v0.2.0) | Treat as "missing in Abacus", reconcile or create. |
429 |
Rate limited | Retry with backoff / $release() in a queued job. |
Wrap calls in a thin service layer. Put one class between your domain and AbacusClient that logs the method, payload and exception class. When a mapping changes between releases (as it did in v0.2.0), you only audit one file.
Queue-friendly pattern. In jobs, catch rate-limit exceptions and release, catch everything else and fail:
public function handle(AbacusClient $abacus): void
{
try {
$abacus->pushInvoice($this->invoice);
} catch (AbacusRateLimitException $e) { // maps 429
$this->release(60);
}
}
Global rendering for your own API. If you expose Abacus lookups through your own controllers, register a renderable in bootstrap/app.php so a missing upstream record becomes a clean 404 for your consumers instead of a 500:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(fn (AbacusNotFoundException $e) => response()->json(
['message' => 'Not found'], 404
));
})
Testing. Fake the underlying HTTP layer (Http::fake([...])) and assert the exception type per status. Add a test per mapped status code — that is exactly the kind of suite that catches a v0.2.0-style mapping change before production does.
Contoweb\AbacusApi\Exceptions\AbacusNotFoundException. The maintainers call it non-breaking (it is still an exception), but any code that branched on the class or message of the 404 case needs revisiting. Audit catch blocks and instanceof checks for 404 handling.AbacusNotFoundException and the other specific types before any broad catch (\Throwable) or base-package exception, otherwise the new type is dead code.403 or 429 turns a loud config/rate problem into silent data loss. Let unexpected statuses bubble to Laravel's handler and report() them..env, run php artisan config:cache (or config:clear in local dev) — a stale cached config is the most common "it authenticates in tinker but not in the app" cause.400; capture the request body and, where available, the response body in your wrapper.toException() — that method is the highest-leverage surface in the package.AbacusClient (or rebinding it in a service provider) over forking. Keeping the vendor class untouched makes upgrades trivial.^0.2 in composer.json and run your per-status tests after each update; with 0.x versions, minor bumps can carry behavioural changes like this one.How can I help you explore Laravel packages today?