Laravel integration for the Nepal Can Move (NCM) courier and shipping API. Manage shipments, track deliveries, calculate rates, handle COD payments, and process webhooks — all with idiomatic Laravel patterns.
Built on top of Nepal Can PHP SDK.
NepalCan) for clean, expressive syntaxOmniCargo\NepalCan\Client in any class| Dependency | Version |
|---|---|
| PHP | ^8.1 |
| Laravel | 10.x, 11.x, or 12.x |
composer require pralhadstha/nepalcan-laravel
The service provider and facade are auto-discovered. No manual registration needed.
php artisan vendor:publish --tag=nepalcan-config
This creates config/nepalcan.php in your application.
Add these variables to your .env file:
NEPALCAN_API_TOKEN=your-api-token-here
NEPALCAN_ENVIRONMENT=sandbox
| Variable | Description | Default |
|---|---|---|
NEPALCAN_API_TOKEN |
Your NCM API token from the dashboard | "" |
NEPALCAN_ENVIRONMENT |
sandbox or production |
sandbox |
NEPALCAN_BASE_URL |
Override the API base URL entirely | null |
NEPALCAN_WEBHOOK_VALIDATE_UA |
Validate webhook User-Agent header | true |
NEPALCAN_WEBHOOK_PATH |
Webhook endpoint path | /nepalcan/webhook |
Set NEPALCAN_ENVIRONMENT=production when you're ready to go live. This switches the base URL from demo.nepalcanmove.com to nepalcanmove.com.
use OmniCargo\NepalCan\Laravel\Facades\NepalCan;
$order = NepalCan::shipments()->create([
'receiver_name' => 'Ram Shrestha',
'receiver_phone' => '9801234567',
'receiver_address' => 'Kathmandu',
'product_name' => 'Electronics',
'cod_charge' => '1500',
'quantity' => 1,
]);
echo $order->orderId;
// By order ID
$statuses = NepalCan::tracking()->getStatusHistory(12345);
// By tracking ID
$detail = NepalCan::tracking()->track('NCM-123456');
echo $detail->lastDeliveryStatus;
// Bulk status check
$bulk = NepalCan::tracking()->getBulkStatuses([12345, 67890]);
use OmniCargo\NepalCan\Services\RateService;
$rate = NepalCan::rates()->calculate('Kathmandu', 'Pokhara');
echo $rate->charge;
// Specify delivery type
$rate = NepalCan::rates()->calculate(
'Kathmandu',
'Pokhara',
RateService::TYPE_D2B, // Door to Branch
);
Available delivery types: TYPE_PICKUP_COLLECT (Door2Door), TYPE_SEND (Branch2Door), TYPE_D2B (Door2Branch), TYPE_B2B (Branch2Branch).
$branches = NepalCan::branches()->list();
foreach ($branches as $branch) {
echo "{$branch->name} - {$branch->district}";
}
use OmniCargo\NepalCan\Services\TicketService;
// Create a ticket
$ticket = NepalCan::tickets()->create(
TicketService::TYPE_GENERAL,
'Need help with order #12345',
);
// Request COD transfer
$ticket = NepalCan::tickets()->createCodTransfer(
bankName: 'Nepal Bank',
accountName: 'Ram Shrestha',
accountNumber: '1234567890',
);
// Close a ticket
NepalCan::tickets()->close($ticket->ticketId);
$result = NepalCan::staff()->list(search: 'ram', page: 1, pageSize: 10);
foreach ($result['results'] as $staff) {
echo "{$staff->name} - {$staff->email}";
}
You can type-hint the SDK client directly in your controllers, jobs, or any service:
use OmniCargo\NepalCan\Client;
class ShippingController extends Controller
{
public function __construct(private readonly Client $client)
{
}
public function show(int $orderId)
{
$order = $this->client->shipments->find($orderId);
$history = $this->client->tracking->getStatusHistory($orderId);
return view('shipping.show', compact('order', 'history'));
}
}
By default, the package registers a POST route at /nepalcan/webhook. Incoming webhook payloads are parsed and dispatched as Laravel events.
Make sure to exclude this path from CSRF verification. In Laravel 10:
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'nepalcan/webhook',
];
In Laravel 11+:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'nepalcan/webhook',
]);
})
To disable the automatic route, set NEPALCAN_WEBHOOK_PATH to an empty value or set webhook.path to null in the config.
The package validates that incoming webhook requests have a User-Agent header starting with NCM-Webhook/. This prevents unauthorized requests from reaching your event listeners. Disable this with:
NEPALCAN_WEBHOOK_VALIDATE_UA=false
Register listeners in your EventServiceProvider or use Event::listen():
use OmniCargo\NepalCan\Laravel\Events\DeliveryCompleted;
use OmniCargo\NepalCan\Laravel\Events\NepalCanWebhookReceived;
// Listen for a specific event
Event::listen(DeliveryCompleted::class, function (DeliveryCompleted $event) {
$orderId = $event->webhook->orderId;
// Update your order status, notify customer, etc.
});
// Listen for ALL webhook events
Event::listen(NepalCanWebhookReceived::class, function (NepalCanWebhookReceived $event) {
Log::info("NCM webhook: {$event->webhook->event}", [
'order_id' => $event->webhook->orderId,
'status' => $event->webhook->status,
]);
});
Every webhook dispatches the generic NepalCanWebhookReceived event. Additionally, a specific event is dispatched based on the webhook type:
| Webhook Event | Laravel Event Class |
|---|---|
pickup_completed |
OmniCargo\NepalCan\Laravel\Events\PickupCompleted |
sent_for_delivery |
OmniCargo\NepalCan\Laravel\Events\SentForDelivery |
order_dispatched |
OmniCargo\NepalCan\Laravel\Events\OrderDispatched |
order_arrived |
OmniCargo\NepalCan\Laravel\Events\OrderArrived |
delivery_completed |
OmniCargo\NepalCan\Laravel\Events\DeliveryCompleted |
All event classes carry a public readonly Webhook $webhook property with the parsed payload data.
composer test
Or run individual suites:
vendor/bin/phpunit --testsuite=Unit
vendor/bin/phpunit --testsuite=Feature
The MIT License (MIT). See LICENSE for details.
How can I help you explore Laravel packages today?