PHPackages                             graystackit/laravel-ship24-api - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. graystackit/laravel-ship24-api

ActiveLibrary[API Development](/categories/api)

graystackit/laravel-ship24-api
==============================

Laravel package for the Ship24 shipment tracking API, built on Saloon 4

00PHP

Since Apr 8Pushed 3w agoCompare

[ Source](https://github.com/GraystackIT/laravel-ship24-api)[ Packagist](https://packagist.org/packages/graystackit/laravel-ship24-api)[ RSS](/packages/graystackit-laravel-ship24-api/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (2)Used By (0)

graystackit/laravel-ship24-api
==============================

[](#graystackitlaravel-ship24-api)

Laravel package for the [Ship24](https://www.ship24.com/) shipment tracking API, built on [Saloon 4](https://docs.saloon.dev/).

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 11, 12, or 13

Installation
------------

[](#installation)

```
composer require graystackit/laravel-ship24-api
```

Publish the config file:

```
php artisan vendor:publish --tag=ship24-config
```

Publish and run the migration (required for local tracking storage):

```
php artisan vendor:publish --tag=ship24-migrations
php artisan migrate
```

Add your API key to `.env`:

```
SHIP24_API_KEY=your-api-key-here
```

Configuration
-------------

[](#configuration)

KeyEnvDefault`api_key``SHIP24_API_KEY`—`base_url``SHIP24_BASE_URL``https://api.ship24.com/public/v1``tracking_mode``SHIP24_TRACKING_MODE``latest``webhook.enabled``SHIP24_WEBHOOK_ENABLED``true``webhook.path``SHIP24_WEBHOOK_PATH``ship24/webhook``webhook.secret``SHIP24_WEBHOOK_SECRET`—### Tracking modes

[](#tracking-modes)

ValueBehaviour`latest`Overwrites the local record with the most recent status only`history`Stores the full event array in the `events` JSON column---

API Client
----------

[](#api-client)

Inject or resolve `Ship24Client` from the container:

```
use GraystackIT\Ship24\Ship24Client;

$client = app(Ship24Client::class);
```

---

### Create a tracker

[](#create-a-tracker)

```
$tracker = $client->createTracker('1Z999AA10123456784', 'ORDER-001');

echo $tracker->trackerId;       // trk_abc123
echo $tracker->trackingNumber;  // 1Z999AA10123456784
echo $tracker->isTracked;       // true
```

---

### List all trackers (with pagination)

[](#list-all-trackers-with-pagination)

```
$result = $client->listTrackers(page: 1, limit: 50);

foreach ($result['trackers'] as $tracker) {
    echo $tracker->trackerId . ': ' . $tracker->trackingNumber;
}

echo $result['total']; // total tracker count
echo $result['page'];  // 1
echo $result['limit']; // 50
```

**Parameters:**

ParameterTypeDefaultDescription`$page``int``1`Page number (min 1)`$limit``int``20`Results per page (1–500)`$sort``int|null``null`Sort by `createdAt`: `1` = asc, `-1` = desc---

### Bulk create trackers (up to 100)

[](#bulk-create-trackers-up-to-100)

```
use GraystackIT\Ship24\Data\BulkCreateResult;

$result = $client->bulkCreateTrackers([
    ['trackingNumber' => '1Z999AA10123456784', 'shipmentReference' => 'ORDER-001'],
    ['trackingNumber' => 'JD014600006228974097'],
    ['trackingNumber' => 'RA123456789DE', 'originCountryCode' => 'DE'],
]);

echo $result->status;       // success | partial | error
echo $result->requested;    // 3
echo $result->successCount; // 3
echo $result->errorCount;   // 0

foreach ($result->items as $item) {
    if ($item->success) {
        echo $item->tracker->trackerId;
    } else {
        echo $item->errorCode . ': ' . $item->errorMessage;
    }
}
```

Throws `\InvalidArgumentException` if the array is empty or has more than 100 items.

---

### Create and track (combined — single API call)

[](#create-and-track-combined--single-api-call)

Creates a tracker and immediately returns its tracking results without a second request:

```
$result = $client->createAndTrack(
    trackingNumber:        '1Z999AA10123456784',
    shipmentReference:     'ORDER-001',       // optional
    originCountryCode:     'US',              // optional
    destinationCountryCode: 'DE',             // optional
    destinationPostCode:   '10115',           // optional
    shippingDate:          '2024-06-01',      // optional (YYYY-MM-DD)
    courierCode:           ['ups'],           // optional (max 3)
);

echo $result->tracker->trackerId;
echo $result->shipment->statusMilestone; // delivered, in_transit, etc.
echo $result->latestEvent()?->status;

// Statistics (if returned by the API)
print_r($result->statistics);
```

---

### Update an existing tracker

[](#update-an-existing-tracker)

```
$tracker = $client->updateTracker('trk_abc123', [
    'isSubscribed'          => false,
    'originCountryCode'     => 'US',
    'destinationCountryCode'=> 'DE',
    'destinationPostCode'   => '10115',
    'shippingDate'          => '2024-06-01',
    'courierCode'           => ['ups'],
]);

echo $tracker->isSubscribed; // false
```

Throws `\InvalidArgumentException` if the updates array is empty.

---

### Get tracking results by tracker ID

[](#get-tracking-results-by-tracker-id)

```
$results = $client->getTrackingResults('trk_abc123');

foreach ($results as $result) {
    echo $result->shipment->statusMilestone;
    echo $result->latestEvent()?->status;
}
```

---

### Get tracking results by tracking number

[](#get-tracking-results-by-tracking-number)

For when you have the tracking number but not the tracker ID:

```
$results = $client->getTrackingResultsByTrackingNumber('1Z999AA10123456784');

foreach ($results as $result) {
    echo $result->tracker->trackerId;
    echo $result->shipment->currentCourierName;
    echo $result->shipment->originCountryCode;

    foreach ($result->events as $event) {
        echo $event->datetime . ' — ' . $event->status . ' — ' . $event->location;
    }
}
```

---

### Search by tracking number (per-call plan)

[](#search-by-tracking-number-per-call-plan)

One-shot search without creating a persistent tracker. No tracker ID required:

```
$results = $client->searchByTrackingNumber('JD014600006228974097');

foreach ($results as $result) {
    echo $result->shipment->currentCourierName;
    echo $result->shipment->originCountryCode;

    foreach ($result->events as $event) {
        echo $event->datetime . ' - ' . $event->status . ' - ' . $event->location;
    }
}
```

> **Note:** This uses the `/tracking/search` per-call endpoint — it does not create a persistent tracker and response time may be up to 1 minute.

---

Trackable Models
----------------

[](#trackable-models)

Add the `IsTrackableByShip24` trait to any Eloquent model to attach Ship24 tracking data directly to it.

```
use GraystackIT\Ship24\Traits\IsTrackableByShip24;

class Order extends Model
{
    use IsTrackableByShip24;
}
```

### Start tracking

[](#start-tracking)

Creates a Ship24 tracker and immediately stores the initial tracking result in a local `ship24_trackings` record linked to the model.

```
$tracking = $order->startTracking('1Z999AA10123456784', 'ORDER-001');

echo $tracking->status_milestone; // in_transit
echo $tracking->carrier_name;     // UPS
```

### Sync tracking

[](#sync-tracking)

Fetches the latest data from Ship24 and updates (or creates) the local record.

```
$tracking = $order->syncTracking('1Z999AA10123456784');
```

### Read stored tracking data

[](#read-stored-tracking-data)

```
// All tracking records linked to this model
foreach ($order->trackings as $t) {
    echo $t->tracking_number . ': ' . $t->status_milestone;
}

// Most recently updated record
$latest = $order->latestTrackingStatus();
echo $latest?->status_milestone;  // delivered
echo $latest?->carrier_name;      // UPS
echo $latest?->latest_event_at;   // 2024-06-10 12:00:00
```

---

Webhook
-------

[](#webhook)

Ship24 can push tracking updates to your application in real-time. The package registers a POST endpoint automatically (disable via `SHIP24_WEBHOOK_ENABLED=false`).

**Default URL:** `POST /ship24/webhook`

Configure this URL in the [Ship24 dashboard](https://app.ship24.com/).

### Webhook secret validation

[](#webhook-secret-validation)

Set `SHIP24_WEBHOOK_SECRET` to enable request validation. Ship24 sends your secret as a Bearer token in the `Authorization` header on every webhook request — the package validates it with a timing-safe comparison.

```
SHIP24_WEBHOOK_SECRET=your-webhook-secret
```

The webhook handler automatically:

1. Validates the `Authorization: Bearer ` header (if a secret is configured)
2. Iterates the `trackings` array in the payload
3. Finds all `ship24_trackings` records matching each item's tracking number / tracker ID
4. Updates them according to the configured `tracking_mode`

---

Artisan command
---------------

[](#artisan-command)

Manually refresh tracking data from the API (useful as a fallback when webhooks are unavailable):

```
# Refresh all ship24_trackings records
php artisan ship24:refresh

# Refresh a single record by its primary key
php artisan ship24:refresh 42
```

---

Data objects
------------

[](#data-objects)

ClassKey properties`Tracker``trackerId`, `trackingNumber`, `shipmentReference`, `isSubscribed`, `isTracked`, `createdAt``Shipment``shipmentId`, `trackingNumber`, `statusCode`, `statusCategory`, `statusMilestone`, `originCountryCode`, `destinationCountryCode`, `courierIds``TrackingEvent``eventId`, `trackingNumber`, `datetime`, `status`, `statusCode`, `statusCategory`, `statusMilestone`, `location``TrackingResult``tracker`, `shipment`, `events[]`, `statistics`, `latestEvent()``BulkCreateResult``status`, `requested`, `successCount`, `errorCount`, `items[]``BulkCreateItem``success`, `tracker`, `errorCode`, `errorMessage`### `ship24_trackings` table columns

[](#ship24_trackings-table-columns)

ColumnTypeDescription`trackable_id` / `trackable_type`polymorphicThe linked Eloquent model`tracking_number`stringParcel tracking number`tracker_id`string|nullShip24 internal tracker ID`carrier_id` / `carrier_name`string|nullActive carrier`status_code` / `status_category` / `status_milestone`string|nullCurrent status`latest_event_at`datetime|nullTimestamp of the most recent event`latest_event_status` / `latest_event_location`string|nullLatest event detail`events`json|nullFull event array (history mode only)`raw_shipment`json|nullRaw shipment payload from Ship24---

Error handling
--------------

[](#error-handling)

All API errors throw `GraystackIT\Ship24\Exceptions\Ship24ApiException`:

```
use GraystackIT\Ship24\Exceptions\Ship24ApiException;

try {
    $results = $client->getTrackingResultsByTrackingNumber('INVALID');
} catch (Ship24ApiException $e) {
    logger()->error('Ship24 error: ' . $e->getMessage());
    // $e->getCode() returns the HTTP status code
}
```

Input validation errors (empty bulk array, empty update fields, bulk &gt; 100) throw `\InvalidArgumentException`.

---

API endpoints reference
-----------------------

[](#api-endpoints-reference)

Client methodHTTPEndpoint`createTracker()`POST`/trackers``listTrackers()`GET`/trackers``bulkCreateTrackers()`POST`/trackers/bulk``createAndTrack()`POST`/trackers/track``updateTracker()`PATCH`/trackers/{trackerId}``getTrackingResults()`GET`/trackers/{trackerId}/results``getTrackingResultsByTrackingNumber()`GET`/trackers/search/{trackingNumber}/results``searchByTrackingNumber()`POST`/tracking/search`---

Testing
-------

[](#testing)

```
# API client tests (no database required)
vendor/bin/pest tests/Feature

# Integration tests (require pdo_sqlite or pdo_mysql)
vendor/bin/pest tests/Integration
```

Feature tests use Saloon's `MockClient` — no real API calls are made.

License
-------

[](#license)

MIT

###  Health Score

21

—

LowBetter than 18% of packages

Maintenance62

Regular maintenance activity

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 87.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/ee65c1290d44c83f1c44c965c311a0150c50b4a8eae9e94635973a8a5d245d55?d=identicon)[bharb](/maintainers/bharb)

---

Top Contributors

[![Parvesh96](https://avatars.githubusercontent.com/u/96613912?v=4)](https://github.com/Parvesh96 "Parvesh96 (7 commits)")[![bharb82](https://avatars.githubusercontent.com/u/159435438?v=4)](https://github.com/bharb82 "bharb82 (1 commits)")

### Embed Badge

![Health badge](/badges/graystackit-laravel-ship24-api/health.svg)

```
[![Health](https://phpackages.com/badges/graystackit-laravel-ship24-api/health.svg)](https://phpackages.com/packages/graystackit-laravel-ship24-api)
```

###  Alternatives

[facebook/php-business-sdk

PHP SDK for Facebook Business

90923.5M35](/packages/facebook-php-business-sdk)[hubspot/api-client

Hubspot API client

24015.5M18](/packages/hubspot-api-client)[botman/driver-telegram

Telegram driver for BotMan

93452.6k6](/packages/botman-driver-telegram)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
