sabitahmad / laravel-steadfast
Laravel package for SteadFast Courier API integration with order creation, tracking, returns, and optional bulk processing
Fund package maintenance!
Requires
- php: ^8.1
- illuminate/console: ^10.0||^11.0||^12.0||^13.0
- illuminate/contracts: ^10.0||^11.0||^12.0||^13.0
- illuminate/database: ^10.0||^11.0||^12.0||^13.0
- illuminate/http: ^10.0||^11.0||^12.0||^13.0
- illuminate/queue: ^10.0||^11.0||^12.0||^13.0
- illuminate/support: ^10.0||^11.0||^12.0||^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Laravel integration for the SteadFast Courier API with typed DTOs for orders, tracking, returns, balance checks, optional bulk processing, bulk lifecycle events, caching, and database-backed request logging.
Features
- Create single and bulk orders
- Track shipments by consignment ID, invoice, or tracking code
- Create and fetch return requests
- Check account balance
- Fetch merchant payments and payment details with consignments
- Fetch delivery coverage zones / police stations with caching
- Typed request and response DTOs
- Queue-backed bulk processing
- Bulk lifecycle events
- Configurable caching for balance, status, and zone lookups
- Optional fraud checking through the SteadFast merchant panel
- Optional request logging and usage statistics
- Built-in artisan commands for testing, statistics, and cleanup
Installation
composer require sabitahmad/laravel-steadfast php artisan steadfast:install
Add your credentials to .env:
STEADFAST_API_KEY=your_api_key_here STEADFAST_SECRET_KEY=your_secret_key_here
Optional settings:
STEADFAST_BASE_URL=https://portal.packzy.com/api/v1 STEADFAST_TIMEOUT=30 STEADFAST_CONNECT_TIMEOUT=10 STEADFAST_BULK_QUEUE=true STEADFAST_BULK_CHUNK_SIZE=500 STEADFAST_QUEUE_NAME=default STEADFAST_QUEUE_CONNECTION= STEADFAST_BULK_MAX_ATTEMPTS=3 STEADFAST_BULK_BACKOFF=60 STEADFAST_RETRY_TIMES=3 STEADFAST_RETRY_SLEEP=1000 STEADFAST_CACHE_ENABLED=false STEADFAST_CACHE_TTL=300 STEADFAST_CACHE_PREFIX=steadfast STEADFAST_CACHE_STORE= STEADFAST_LOGGING=true STEADFAST_LOG_REQUESTS=false STEADFAST_LOG_RESPONSES=true STEADFAST_CLEANUP_LOGS=true STEADFAST_KEEP_LOGS_DAYS=30 STEADFAST_FRAUD_CHECKER_ENABLED=false STEADFAST_FRAUD_CHECKER_EMAIL=your-merchant-email@example.com STEADFAST_FRAUD_CHECKER_PASSWORD=your-merchant-password
Verify the setup:
php artisan steadfast:test
Usage
Create an Order
use SabitAhmad\SteadFast\DTO\OrderRequest; use SabitAhmad\SteadFast\SteadFast; $steadfast = new SteadFast(); $order = new OrderRequest( invoice: 'INV-2025-001', recipient_name: 'John Doe', recipient_phone: '01712345678', recipient_address: 'House 1, Road 2, Dhanmondi, Dhaka-1209', cod_amount: 1500.00, note: 'Handle with care' ); $response = $steadfast->createOrder($order); echo $response->getTrackingCode();
You can also use optional order fields when needed:
$order = new OrderRequest( invoice: 'INV-2025-002', recipient_name: 'Jane Doe', recipient_phone: '01812345678', recipient_address: 'Banani, Dhaka', cod_amount: 2200, alternative_phone: '01912345678', recipient_email: 'jane@example.com', item_description: 'Skin care products', total_lot: 2, delivery_type: 0 );
Use the Facade
use SabitAhmad\SteadFast\Facades\SteadFast; $balance = SteadFast::getBalance();
Bulk Orders
$orders = [ new OrderRequest('INV-001', 'John Doe', '01712345678', 'Address 1', 1000), new OrderRequest('INV-002', 'Jane Smith', '01787654321', 'Address 2', 1500), OrderRequest::fromArray([ 'invoice' => 'INV-003', 'recipient_name' => 'Bob Wilson', 'recipient_phone' => '01611111111', 'recipient_address' => 'Address 3', 'cod_amount' => 2000, 'delivery_type' => 1, ]), ]; $queued = $steadfast->bulkCreate($orders, true); $processed = $steadfast->bulkCreate($orders, false);
Bulk responses include helper methods:
echo $processed->getSuccessRate(); if ($processed->hasErrors()) { $failedOrders = $processed->getFailedOrders(); }
Track an Order
$status = $steadfast->checkStatusByTrackingCode('ABC123XYZ'); if ($status->isDelivered()) { echo 'Delivered'; } echo $status->getStatusDescription();
You can also track by invoice or consignment ID:
$steadfast->checkStatusByInvoice('INV-001'); $steadfast->checkStatusByConsignmentId(12345);
Return Requests
use SabitAhmad\SteadFast\DTO\ReturnRequest; $request = ReturnRequest::byInvoice('INV-001', 'Customer requested return'); $response = $steadfast->createReturnRequest($request); $single = $steadfast->getReturnRequest(123); $all = $steadfast->getReturnRequests();
You can also create return requests by consignment ID or tracking code:
ReturnRequest::byConsignmentId(12345, 'Damaged item'); ReturnRequest::byTrackingCode('ABC123', 'Wrong item delivered');
Balance
$balance = $steadfast->getBalance(); echo $balance->getFormattedBalance();
Payments
Fetch merchant payment / disbursement history or inspect an individual payment with all associated consignment orders:
// Get all payments (supports optional pagination: $steadfast->getPayments(page: 1)) $payments = $steadfast->getPayments(); foreach ($payments as $payment) { echo "Payment ID: {$payment->id}, Amount: {$payment->amount} BDT, Status: {$payment->status}\n"; } // Get single payment with consignments list $payment = $steadfast->getPayment(123); if ($payment->isCompleted()) { echo "Payment of {$payment->amount} BDT was cleared.\n"; } // Inspect linked consignments foreach ($payment->consignments as $consignment) { echo "Consignment: {$consignment['tracking_code']} - {$consignment['cod_amount']} BDT\n"; }
Police Stations (Coverage Zones)
Retrieve the list of police stations / thanas supported by Steadfast Courier. This response is automatically cached to prevent unnecessary network requests:
// Retrieve police stations (cached automatically) $policeStations = $steadfast->getPoliceStations(); foreach ($policeStations as $station) { echo "{$station->name} ({$station->district})\n"; } // Force-refresh the cache if needed $freshStations = $steadfast->getPoliceStations(forceRefresh: true);
Response Helpers
The DTOs expose helper methods so your application code stays clean:
$orderResponse->getConsignmentId(); $orderResponse->getTrackingCode(); $orderResponse->getInvoice(); $status->isPending(); $status->isCancelled(); $status->isOnHold(); $returnResponse->isPending(); $returnResponse->isCompleted();
Fraud Check
This feature logs into the SteadFast merchant panel and is slower than the normal API calls. Enable it only if you need it.
try { $fraud = $steadfast->checkFraud('01712345678'); echo $fraud->success; echo $fraud->cancel; echo $fraud->getRiskLevel(); } catch (\SabitAhmad\SteadFast\Exceptions\SteadfastException $e) { report($e); }
Accepted phone formats are normalized automatically, including 01712345678, 8801712345678, +8801712345678, and values with spaces or dashes.
Fraud responses also include helper methods:
$fraud->getSuccessRate(); $fraud->getCancelRate(); $fraud->isRisky(); $fraud->getRiskDescription();
Events
Bulk processing dispatches these events:
SabitAhmad\SteadFast\Events\BulkOrderStartedSabitAhmad\SteadFast\Events\BulkOrderCompletedSabitAhmad\SteadFast\Events\BulkOrderFailed
Example listener registration:
use Illuminate\Support\Facades\Event; use SabitAhmad\SteadFast\Events\BulkOrderCompleted; use SabitAhmad\SteadFast\Events\BulkOrderFailed; use SabitAhmad\SteadFast\Events\BulkOrderStarted; Event::listen(BulkOrderStarted::class, function (BulkOrderStarted $event) { logger()->info('Bulk order processing started', [ 'order_count' => count($event->orders), 'unique_id' => $event->uniqueId, ]); }); Event::listen(BulkOrderCompleted::class, function (BulkOrderCompleted $event) { logger()->info('Bulk order processing completed', [ 'success_count' => $event->response->success_count, 'error_count' => $event->response->error_count, 'unique_id' => $event->uniqueId, ]); }); Event::listen(BulkOrderFailed::class, function (BulkOrderFailed $event) { logger()->error('Bulk order processing failed', [ 'error' => $event->exception->getMessage(), 'unique_id' => $event->uniqueId, ]); });
Caching
Balance and status lookups can be cached:
STEADFAST_CACHE_ENABLED=true STEADFAST_CACHE_TTL=300 STEADFAST_CACHE_PREFIX=steadfast STEADFAST_CACHE_STORE=redis
Clear cached entries when needed:
$steadfast->clearCache('balance'); $steadfast->clearCache();
Error Handling
use SabitAhmad\SteadFast\Exceptions\SteadfastException; try { $steadfast->createOrder($order); } catch (SteadfastException $e) { match ($e->getCode()) { 401 => report('Invalid credentials'), 422 => report($e->getContext()['validation_errors'] ?? []), 429 => report('Rate limited'), 503 => report('Service unavailable'), default => report($e->getMessage()), }; }
Logging and Statistics
If logging is enabled, requests are stored in the steadfast_logs table.
Useful scopes on the SteadfastLog model:
use SabitAhmad\SteadFast\Models\SteadfastLog; $recentErrors = SteadfastLog::errors()->recent(24)->get(); $successful = SteadfastLog::successful()->recent(24)->get(); $bulkOperations = SteadfastLog::bulkOperations()->get(); $stats = SteadfastLog::getStats(24);
Artisan Commands
Available commands:
php artisan steadfast:test php artisan steadfast:stats php artisan steadfast:stats --hours=168 php artisan steadfast:cleanup php artisan steadfast:cleanup --force
What they do:
steadfast:test: validates config and checks API connectivitysteadfast:stats: shows request volume, success rate, bulk usage, and top endpointssteadfast:cleanup: removes old log records fromsteadfast_logs
API Coverage
| Endpoint | Method | Purpose |
|---|---|---|
/create_order |
POST |
Create a single order |
/create_order/bulk-order |
POST |
Create bulk orders |
/status_by_cid/{id} |
GET |
Check status by consignment ID |
/status_by_invoice/{invoice} |
GET |
Check status by invoice |
/status_by_trackingcode/{code} |
GET |
Check status by tracking code |
/get_balance |
GET |
Get account balance |
/create_return_request |
POST |
Create a return request |
/get_return_request/{id} |
GET |
Get one return request |
/get_return_requests |
GET |
Get all return requests |