laravel-steadfast-courier maintained by ahraihan
Steadfast Courier (API + Webhook) for Laravel
A complete, battle-tested, and developer-friendly Laravel package for Steadfast Courier API (all 18 endpoints) and Webhook notifications with HMAC-SHA256 signature verification and Idempotency handling.
🚀 Features
- Full API Coverage (All 18 Official Endpoints):
- Connectivity check (
ping) - Single order booking (
create_order) - Bulk orders booking (
create_order/bulk-order) - Extended bulk orders with detailed per-field validation (
create_order/bulk-order/extended) - Status tracking by Consignment ID, Tracking Code, and Merchant Invoice
- Detailed chronological tracking history timeline
- Return requests creation & pagination
- Pickup rider requests
- Real-time account balance & payout reconciliation
- Thanas / Police Stations directory
- Customer delivery record & fraud check score
- Connectivity check (
- Full Webhook Support:
- Automatic webhook route registration
- Secure HMAC-SHA256 (
X-Signature) verification with timingSafeEqual comparison Idempotency-Keytracking to eliminate duplicate event processing on Steadfast retries- Dispatches strongly-typed Laravel Events for each notification type (
delivery_status,tracking_update,consignment_update,return_list_accepted,payment_request,cancel_request,return_request,pickup_request,user_update)
- Smart Sanitization:
- Automatically strips restricted characters (
{,},;,<,>,$) according to Steadfast docs - Automatically formats Bangladeshi phone numbers (
01...) and trims fields to max lengths
- Automatically strips restricted characters (
- Clean Laravel Ergonomics:
- Facade support (
Steadfast::...andSteadfastCourier::...) - Multi-tenant / Dynamic credentials support (
withConfig(...)) - 100% backward compatibility with legacy Steadfast Courier package methods
- Facade support (
📦 Requirements
- PHP
^8.2,^8.3, or^8.4 - Laravel
10.x,11.x,12.x, or13.x
🛠 Installation
Install the package via Composer:
composer require ahraihan/laravel-steadfast-courier
The package uses Laravel package auto-discovery, so its Service Provider and Facade aliases are registered automatically.
Publish Configuration
Publish the config file:
php artisan vendor:publish --tag=steadfast-config
This creates config/steadfast.php.
⚙️ Configuration
Add your Steadfast credentials to your .env file:
# Steadfast Courier API
STEADFAST_BASE_URL=https://portal.packzy.com/api/v1
STEADFAST_API_KEY=your_steadfast_api_key
STEADFAST_SECRET_KEY=your_steadfast_secret_key
STEADFAST_TIMEOUT=30
# Steadfast Webhook
STEADFAST_WEBHOOK_ENABLED=true
STEADFAST_WEBHOOK_PATH=steadfast/webhook
STEADFAST_WEBHOOK_TOKEN=your_webhook_auth_token_from_steadfast
STEADFAST_WEBHOOK_VERIFY_SIGNATURE=true
STEADFAST_WEBHOOK_IDEMPOTENCY=true
📖 API Usage
Import the Facade into your controllers, jobs, or services:
use AhRaihan\SteadfastCourier\Facades\Steadfast;
(Note: use AhRaihan\SteadfastCourier\Facades\SteadfastCourier; is also available and 100% interchangeable).
1. Service Check (/ping)
Verify that the Steadfast server is reachable (requires no credentials).
$response = Steadfast::ping();
// ['status' => 200, 'message' => 'pong', 'service' => 'steadfast-api', 'version' => 'v1', ...]
2. Book Single Parcel (/create_order)
$response = Steadfast::createOrder([
'invoice' => 'ORD-10231', // Required. Alphanumeric, dash, underscore only (max 100)
'recipient_name' => 'Jahid Hasan', // Required (max 100)
'recipient_phone' => '01712345678', // Required. 11 digits starting 01
'recipient_address' => 'House 17/1, Dhanmondi', // Required (max 490)
'cod_amount' => 1060, // Required. BDT (0 for prepaid)
'note' => 'Call before 3 PM', // Optional (max 480)
'alternative_phone' => '01812345678', // Optional. 11 digits starting 01
'recipient_email' => 'customer@example.com', // Optional
'item_description' => 'Books and Stationery', // Optional (max 255)
'total_lot' => 1, // Optional (default: 1)
'delivery_type' => 0, // Optional. 0 = Home Delivery (default), 1 = Point Delivery
]);
$consignmentId = $response['consignment']['consignment_id'] ?? null;
$trackingCode = $response['consignment']['tracking_code'] ?? null;
$trackingLink = $response['consignment']['tracking_link'] ?? null;
3. Bulk Order Booking (/create_order/bulk-order)
Book up to 500 parcels in a single call.
$orders = [
[
'invoice' => 'ORD-10231',
'recipient_name' => 'Jahid Hasan',
'recipient_phone' => '01712345678',
'recipient_address' => 'Dhanmondi, Dhaka',
'cod_amount' => 1060,
],
[
'invoice' => 'ORD-10232',
'recipient_name' => 'Nusrat Jahan',
'recipient_phone' => '01812345678',
'recipient_address' => 'Uttara, Dhaka',
'cod_amount' => 450,
],
];
$response = Steadfast::createBulkOrders($orders);
4. Extended Bulk Orders (/create_order/bulk-order/extended)
Preferred bulk booking endpoint: returns clear, human-readable per-field error sentences.
$response = Steadfast::createBulkOrdersExtended($orders);
5. Check Status by Consignment ID (/status_by_cid/{id})
$response = Steadfast::getStatusByConsignmentId(1424107);
// ['status' => 200, 'delivery_status' => 'delivered']
6. Check Status with Return Status (/status_with_return_status_by_cid/{id})
Essential for stock reconciliation: reveals whether a returned parcel is back with you.
$response = Steadfast::getStatusWithReturnStatusByConsignmentId(1424107);
// ['status' => 200, 'delivery_status' => 'cancelled_return_received']
7. Check Status by Invoice ID (/status_by_invoice/{invoice})
$response = Steadfast::getStatusByInvoice('ORD-10231');
8. Check Status by Tracking Code (/status_by_trackingcode/{code})
$response = Steadfast::getStatusByTrackingCode('15BAEB8A');
9. Detailed Tracking Steps History (/trackings_by_invoice/{invoice})
Shows every milestone the parcel has been through.
$response = Steadfast::getTrackingsByInvoice('ORD-10231');
foreach ($response['tracking'] as $step) {
echo $step['text'].' at '.$step['created_at'];
}
10. Request Rider Pickup (/create_pickup_request)
$response = Steadfast::createPickupRequest([
'address_id' => 42,
'police_station_id' => 17,
'address' => 'House 17/1, Road 3/A, Dhanmondi',
'contact_number' => '01712345678',
'estim_qty' => 25,
'note' => 'Pick up after 2 PM',
]);
11. Request Parcel Return (/create_return_request)
$response = Steadfast::createReturnRequest([
'invoice' => 'ORD-10231', // or 'consignment_id' or 'tracking_code'
'reason' => 'Customer changed mind before delivery',
]);
12. List Return Requests (/get_return_requests)
$response = Steadfast::getReturnRequests(page: 1);
13. Get Single Return Request (/get_return_request/{id})
$response = Steadfast::getReturnRequest(1);
14. Get Merchant Current Balance (/get_balance)
$balance = Steadfast::getCurrentBalance();
$amount = $balance['current_balance']; // e.g. 12450
15. List Payments / Payouts (/payments)
$payments = Steadfast::getPayments(page: 1);
16. Get Payment Detail with Settled Parcels (/payments/{id})
$payout = Steadfast::getPayment('SFC-88213'); // Accepts with or without SFC- prefix
17. List Police Stations / Thanas (/police_stations)
$stations = Steadfast::getPoliceStations();
18. Customer Fraud Check & Delivery Record (/fraud_check/score/{phone})
$record = Steadfast::checkFraud('01712345678');
$deliveryRatio = $record['delivery_ratio']; // Percentage delivered (e.g. 92)
$cancellationRatio = $record['cancellation_ratio']; // Percentage cancelled (e.g. 7)
$volumeBand = $record['volume_band']; // low, medium, high, very_high
$deliveredCount = $record['delivered_count']; // Exact count (if available)
$cancelledCount = $record['cancelled_count']; // Exact count (if available)
🔀 Dynamic / Multi-Store Credentials
If your system supports multiple merchant accounts or stores:
$client = Steadfast::withConfig(
apiKey: 'another_api_key',
secretKey: 'another_secret_key',
baseUrl: 'https://portal.packzy.com/api/v1',
webhookToken: 'another_webhook_token'
);
$balance = $client->getCurrentBalance();
🔔 Webhook Integration Guide
Steadfast posts webhook events to your application when a parcel's status updates, a payout is requested, or tracking progress occurs.
1. Set Up Webhook Endpoint in Steadfast Dashboard
- Log in to your Steadfast Courier Dashboard.
- Go to Webhook settings.
- In Callback URL, enter:
https://your-domain.com/steadfast/webhook. - Click Generate next to Auth token.
- Copy the generated token and save it into your
.env:STEADFAST_WEBHOOK_TOKEN=your_generated_token_here - Click Save webhook.
2. Exclude Webhook Route from CSRF Protection
Because Steadfast posts directly to your webhook URL, exclude it from CSRF verification:
Laravel 11 & 12 (bootstrap/app.php):
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'steadfast/webhook',
]);
})
Laravel 10 (app/Http/Middleware/VerifyCsrfToken.php):
protected $except = [
'steadfast/webhook',
];
3. Listen to Webhook Events
When Steadfast triggers the webhook, the package automatically verifies the HMAC-SHA256 signature and fires the corresponding Laravel event.
Register event listeners in your EventServiceProvider or AppServiceProvider:
use AhRaihan\SteadfastCourier\Events\SteadfastDeliveryStatusChanged;
use AhRaihan\SteadfastCourier\Events\SteadfastTrackingUpdated;
use AhRaihan\SteadfastCourier\Events\SteadfastWebhookReceived;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
// Listen for parcel delivery status changes
Event::listen(SteadfastDeliveryStatusChanged::class, function (SteadfastDeliveryStatusChanged $event) {
$invoice = $event->invoice; // 'ORD-10231'
$consignmentId = $event->consignmentId; // 12345
$status = $event->status; // 'delivered', 'cancelled', 'partial_delivered', etc.
$codAmount = $event->codAmount; // 1500.0
$deliveryCharge = $event->deliveryCharge; // 100.0
$message = $event->trackingMessage; // 'Your package has been delivered successfully.'
$updatedAt = $event->updatedAt; // '2026-03-02 12:45:30'
if ($event->isDelivered()) {
// Update order in database to completed / mark paid
} elseif ($event->isCancelled()) {
// Restock items or notify support
}
});
// Listen to all webhook events (generic)
Event::listen(SteadfastWebhookReceived::class, function (SteadfastWebhookReceived $event) {
Log::info('Steadfast Webhook: ' . $event->notificationType, $event->payload);
});
}
Available Webhook Events
Notification Type (notification_type) |
Laravel Event Dispatched | Description |
|---|---|---|
* (any event) |
SteadfastWebhookReceived |
General event containing raw payload & signature |
delivery_status |
SteadfastDeliveryStatusChanged |
Parcel delivery status updated |
tracking_update |
SteadfastTrackingUpdated |
New tracking step or transit milestone |
consignment_update |
SteadfastConsignmentUpdated |
Consignment updated (address, COD, etc.) |
return_list_accepted |
SteadfastReturnListAccepted |
Return list receipt confirmed |
payment_request |
SteadfastPaymentRequested |
Payout requested |
cancel_request |
SteadfastCancelRequested |
Cancellation request raised/decided |
return_request |
SteadfastReturnRequested |
Return request raised |
pickup_request |
SteadfastPickupRequested |
Pickup requested |
user_update |
SteadfastUserUpdated |
Account details updated |
🧪 Testing
Run package tests:
composer test
# or
vendor/bin/pest
🚀 How to Publish to GitHub and Packagist
To publish this package so that anyone (or your other projects) can install it via composer require ahraihan/laravel-steadfast-courier:
-
Initialize Git inside package directory:
cd packages/ahraihan/laravel-steadfast-courier git init git add . git commit -m "feat: initial release of Steadfast Courier Laravel package" -
Push to GitHub:
- Create a new public repository on GitHub:
https://github.com/dev-ahraihan/laravel-steadfast-courier
git remote add origin https://github.com/dev-ahraihan/laravel-steadfast-courier.git git branch -M main git push -u origin main - Create a new public repository on GitHub:
-
Tag a Release:
git tag -a v1.0.0 -m "Release v1.0.0" git push origin v1.0.0 -
Submit to Packagist:
- Visit packagist.org and log in.
- Click Submit.
- Enter your repository URL:
https://github.com/dev-ahraihan/laravel-steadfast-courier. - Click Check and then Submit.
- Set up the GitHub Webhook on Packagist for automatic release updates!
📄 License
The MIT License (MIT). Please see LICENSE.md for more information.
👤 Author
AH-Raihan
- Email: dev.ahraihan@gmail.com
- GitHub: @dev-ahraihan