Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Aftership Php Sdk Laravel Package

aftership/aftership-php-sdk

Official PHP SDK for the AfterShip tracking API. Manage shipments, couriers, and tracking statuses through a clean, framework-friendly client, with Laravel integration for configuration, service providers, and dependency injection.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and pin the version you have actually tested — 6.0.0 is a major bump whose only published note is "support 2024-01 version", so pin explicitly and don't float the constraint:

composer require aftership/aftership-php-sdk:^6.0

Because the release note is a single line, treat 6.0.0 as unverified until you've dumped one response. Upgrade path from 5.x: read the diff (vendor/aftership/aftership-php-sdk between tags) for changed method signatures, return envelopes, and exception classes before running it anywhere near production.

There is no Laravel service provider or facade in the package. Register it yourself in AppServiceProvider (or a small dedicated provider) and keep the key out of committed config:

// config/services.php
'aftership' => [
    'key'     => env('AFTERSHIP_API_KEY'),
    'version' => '2024-01', // 6.0.0 targets this API version; see Gotchas
],
// app/Providers/AfterShipServiceProvider.php
use AfterShip\AfterShip;

public function register(): void
{
    $this->app->singleton(AfterShip::class, fn () => new AfterShip(
        config('services.aftership.key')
    ));
}

Read these first, in order: the package README's method list for couriers, trackings, and last_checkpoint; then the AfterShip API docs for the 2024-01 version (not the older docs you may already have bookmarked) covering POST /trackings and the webhook payload, because the SDK only covers request/response, not push updates.

First use case — create a tracking the moment an order ships, then enrich your UI from GET /trackings/{slug}/{number}:

use AfterShip\AfterShip;

$tracking = app(AfterShip::class)->trackings->create([
    'tracking_number' => $order->tracking_number,
    'slug'            => $order->carrier_slug, // omit only if you trust auto-detect
    'order_id'        => (string) $order->id,
    'title'           => "Order #{$order->number}",
]);

$order->update(['aftership_id' => $tracking['id'] ?? null]);

Implementation Patterns

Wrap the SDK behind your own interface. This is the single most valuable pattern here, and a major release that changes API versions is exactly the reason:

interface ShipmentTracker
{
    public function createTracking(array $attributes): array;
    public function find(string $slug, string $number): ?array;
}

The concrete class wraps AfterShip and translates SDK arrays into your own DTOs / value objects. When the SDK lags or breaks you swap one class for a Laravel Http client pointed at /2024-01 and nothing else changes.

Multi-tenant keys need a factory, not a singleton. If you're a SaaS where each tenant supplies their own AfterShip key, bind a factory that caches clients per key rather than overriding the singleton at runtime:

$this->app->bind(AfterShipFactory::class, fn ($app) => new AfterShipFactory());
// ->for($tenant->aftership_api_key)

Queue tracking creation. Every write should be a job with $tries, $backoff, and retryUntil, so a 429 or 5xx from AfterShip is retried with delay instead of failing a checkout or shipment action:

class CreateAfterShipTracking implements ShouldQueue
{
    public int $tries = 5;
    public array $backoff = [5, 15, 60, 300];

    public function handle(ShipmentTracker $tracker): void { /* ... */ }
}

Hook creation to domain events, not controllers. A listener on ShipmentDispatched (or an observer on Order transitioning to shipped) keeps carrier logic out of HTTP layers. Guard with if ($order->tracking_number && $order->carrier_slug) and store the returned id for later reads.

Webhooks are the read path; polling is the fallback. AfterShip pushes updates; don't poll trackings->get() on page load. Set up a single unauthenticated route, verify the HMAC signature (AfterShip signs the raw body with your webhook secret), store the raw payload before processing, and dispatch an event:

Route::post('/webhooks/aftership', AfterShipWebhookController::class)
    ->withoutMiddleware(['csrf']);
$raw = $request->getContent();
$expected = hash_hmac('sha256', $raw, config('services.aftership.webhook_secret'));

abort_unless(hash_equals($expected, $request->header('aftership-hmac-sha256', '')), 401);

WebhookReceipt::create([
    'event_id' => $request->header('aftership-event-id'),
    'payload'  => json_decode($raw, true),
]);

Add an idempotency layer. aftership-event-id and individual checkpoint IDs both work as dedupe keys. A unique index on the receipt table plus insertOrIgnore is enough. Process the receipt in a queued job — return 200 fast.

Map tag to your own enum. Values include Pending, InfoReceived, InTransit, OutForDelivery, AttemptFail, Delivered, AvailableForPickup, Exception, Expired. With the 2024-01 bump, re-check that list against current docs rather than trusting an enum you copied from a 2022 blog post. Don't let raw strings leak into blade templates or analytics.

Cache reads. Tracking detail rarely changes minute-to-minute; Cache::remember("aftership:{$slug}:{$number}", 300, ...) dramatically cuts your call volume and sidesteps rate-limit pain.

Gotchas and Tips

  • 6.0.0 is a major bump with a one-line changelog. "Support 2024-01 version" tells you nothing about whether method signatures, the data/meta envelope, or exception class names changed. Diff the tags and run a smoke test against a sandbox key before upgrading from 5.x. Pin ^6.0 (or an exact version) and treat future releases as unverified until read.
  • API version is now 2024-01. Anything you hand-rolled against an older /v4 or /v5 path is not the same contract as what the SDK talks to. If you fall back to a Http::withHeaders(['aftership-api-key' => $key]) client, target the matching version prefix and reconcile field names before switching traffic over.
  • The archive status is stale advice — re-verify it. The package shipped a new major, so older "frozen forever, replace it" guidance may no longer hold. Check the repo's current activity and release cadence yourself; even if maintained again, a single API-version bump after dormancy is not a maintenance guarantee. Keep the wrapper either way.
  • Http::fake() will not intercept this SDK. It uses its own transport, not Laravel's HTTP client, so your test suite must fake at your ShipmentTracker interface boundary — another reason to wrap it.
  • Inspect the return shape once, early — especially now. A version bump is exactly when the envelope changes (data only vs. full body with meta). Dump one create() and one get() response in tinker and write a small normalizer.
  • Catch the SDK's exception taxonomy, not \Exception. There are distinct types for bad request, not found, unauthorized, rate limit, and internal error — re-confirm the namespace/hierarchy after upgrading to 6.0.0. Handle 429 separately and rethrow after $this->release($delay); immediate retries make it worse.
  • Duplicates are a normal outcome. Re-creating a slug + tracking_number pair fails with a 400-class error. On that specific failure, fall back to trackings->get($slug, $number) and treat it as success. This makes your job naturally retry-safe.
  • Slug detection is unreliable enough to be a UX problem. If you don't know the carrier, call couriers->detect with the number and let the user pick from the returned candidates — a wrong slug creates a tracking that will never update, and there is no "delete" you should rely on for cleanup.
  • Never log the client or the exception stack wholesale. The AfterShip instance holds the API key, and SDK error messages can echo request context. Log meta.code/meta.message and your own correlation ID.
  • Timestamp handling. Checkpoints arrive as UTC ISO-8601 strings and can be out of order in webhooks. Sort by checkpoint_time (or checkpoint_id) before displaying, and ->utc()/->tz() them for the user's locale.
  • Set an explicit timeout. A slow AfterShip call inside a request cycle will hold a PHP-FPM worker. Any synchronous call should be wrapped in a timeout or moved to a queue (prefer the queue).
  • Extension points are minimal. There's no pluggable HTTP handler, retry policy, or event system — the only real extension point is your own wrapper class. Keep that wrapper thin but complete: it's your upgrade path, and it just earned its keep.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log