aiarmada / jnt
J&T Express Malaysia API Integration Package
v1.0.0
2026-03-21 04:17 UTC
Requires
- php: ^8.4
- composer-runtime-api: ^2.1
- ext-intl: *
Requires (Dev)
- akaunting/laravel-money: ^6.0
- anourvalar/eloquent-serialize: ^1.2
- chillerlan/php-qrcode: ^5.0
- filament/blueprint: ^2.0
- filament/filament: ^5.0
- filament/spatie-laravel-media-library-plugin: ^5.0
- filament/support: *
- guzzlehttp/guzzle: ^7.0
- larastan/larastan: ^3.0
- laravel/boost: ^2.0
- laravel/cashier: ^16.0
- laravel/framework: ^12.0|^13.0
- laravel/pint: ^1.0
- laravel/prompts: ^0.3.5
- league/csv: ^9.27
- league/flysystem-aws-s3-v3: ^3.0
- lorisleiva/laravel-actions: ^2.9
- nunomaduro/essentials: ^1.0
- nunomaduro/termwind: ^2.0
- openspout/openspout: ^4.23
- orchestra/testbench: ^10.0|^11.0
- owen-it/laravel-auditing: ^14.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phiki/phiki: ^2.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan: ^2.1
- pragmarx/google2fa: ^8.0
- pragmarx/google2fa-qrcode: ^3.0
- rector/rector: ^2.0
- spatie/browsershot: ^5.0
- spatie/laravel-activitylog: ^4.10
- spatie/laravel-data: ^4.18
- spatie/laravel-health: ^1.34
- spatie/laravel-medialibrary: ^11.0
- spatie/laravel-model-states: ^2.8
- spatie/laravel-package-tools: ^1.92
- spatie/laravel-pdf: ^2.4
- spatie/laravel-permission: ^7.2
- spatie/laravel-ray: ^1.29
- spatie/laravel-settings: ^3.6
- spatie/laravel-sluggable: ^3.7
- spatie/laravel-tags: ^4.2
- spatie/laravel-webhook-client: ^3.4
- staudenmeir/belongs-to-through: ^2.5
- staudenmeir/eloquent-has-many-deep: ^1.7
- symplify/monorepo-builder: ^12.5
README
Laravel 12 integration for J&T Express Malaysia Open API – orders, tracking, waybills, and real-time webhooks.
Why this package?
- Complete API coverage – create orders, track parcels, print waybills, cancel orders, batch operations.
- Clean API naming – use
orderIdinstead oftxlogisticId,trackingNumberinstead ofbillCode. - Type-safe enums –
ExpressType::DOMESTICinstead of magic strings like'EZ'. - First-class Laravel DX – facades, fluent builders, data objects, events, Artisan commands.
- Production ready – PHP 8.4 / Laravel 12, PHPStan level 6, Pest test suite.
- Webhooks included – automatic signature verification and event dispatching.
Installation
composer require aiarmada/jnt
Publish configuration and migrations:
php artisan vendor:publish --tag="jnt-config" php artisan vendor:publish --tag="jnt-migrations" php artisan migrate
Environment Variables
JNT_ENVIRONMENT=testing # or 'production' JNT_CUSTOMER_CODE=your_customer_code JNT_PASSWORD=your_password # Production only (testing uses J&T's public sandbox credentials): JNT_API_ACCOUNT=your_api_account JNT_PRIVATE_KEY=your_private_key # Optional JNT_LOGGING_ENABLED=true JNT_WEBHOOKS_ENABLED=true
Note: When
JNT_ENVIRONMENT=testing, the package automatically uses J&T's official sandbox credentials. You only needJNT_CUSTOMER_CODEandJNT_PASSWORD.
Usage
Create an Order
use AIArmada\Jnt\Facades\JntExpress; use AIArmada\Jnt\Data\{AddressData, ItemData, PackageInfoData}; use AIArmada\Jnt\Enums\{ExpressType, ServiceType, PaymentType, GoodsType}; $sender = new AddressData( name: 'John Sender', phone: '60123456789', address: 'No 32, Jalan Kempas 4', postCode: '81930', countryCode: 'MYS', state: 'Johor', city: 'Johor Bahru', ); $receiver = new AddressData( name: 'Jane Receiver', phone: '60987654321', address: '4678, Laluan Sentang 35', postCode: '31000', countryCode: 'MYS', state: 'Perak', city: 'Batu Gajah', ); $item = new ItemData( itemName: 'Basketball', quantity: 2, weight: 10, unitPrice: 50.00, ); $packageInfo = new PackageInfoData( quantity: 1, weight: 10, declaredValue: 50, goodsType: GoodsType::PACKAGE, ); $order = JntExpress::createOrderBuilder() ->orderId('ORDER-' . time()) ->expressType(ExpressType::DOMESTIC) ->serviceType(ServiceType::DOOR_TO_DOOR) ->paymentType(PaymentType::PREPAID_POSTPAID) ->sender($sender) ->receiver($receiver) ->addItem($item) ->packageInfo($packageInfo) ->build(); $result = JntExpress::createOrderFromArray($order); echo "Tracking: " . $result->trackingNumber;
Track a Parcel
// By your order ID $tracking = JntExpress::trackParcel(orderId: 'ORDER-123'); // Or by J&T tracking number $tracking = JntExpress::trackParcel(trackingNumber: 'JT630002864925'); foreach ($tracking->details as $detail) { echo "{$detail->scanTime}: {$detail->description}\n"; }
Cancel an Order
JntExpress::cancelOrder( orderId: 'ORDER-123', reason: 'Customer requested cancellation', trackingNumber: 'JT630002864925', );
Print Waybill
$label = JntExpress::printOrder( orderId: 'ORDER-123', trackingNumber: 'JT630002864925', ); $pdfUrl = $label['urlContent'];
Enums
Type-safe enums prevent invalid values:
// Express Type ExpressType::DOMESTIC // Standard delivery ExpressType::NEXT_DAY // Express next day ExpressType::FRESH // Cold chain delivery // Service Type ServiceType::DOOR_TO_DOOR // Pickup from sender ServiceType::WALK_IN // Drop-off at counter // Payment Type PaymentType::PREPAID_POSTPAID // Prepaid by merchant PaymentType::COLLECT_CASH // Cash on delivery // Goods Type GoodsType::DOCUMENT // Documents GoodsType::PACKAGE // Parcels
Webhooks
Receive real-time tracking updates from J&T.
Setup
-
Enable webhooks in
.env:JNT_WEBHOOKS_ENABLED=true
-
Create a listener:
namespace App\Listeners; use AIArmada\Jnt\Events\TrackingStatusReceived; class UpdateOrderTracking { public function handle(TrackingStatusReceived $event): void { $order = Order::where('tracking_number', $event->trackingNumber)->first(); $order?->update([ 'tracking_status' => $event->lastStatus, 'tracking_time' => $event->scanTime, ]); } }
-
Register the listener:
// EventServiceProvider.php protected $listen = [ \AIArmada\Jnt\Events\TrackingStatusReceived::class => [ \App\Listeners\UpdateOrderTracking::class, ], ];
-
Configure J&T Dashboard with your webhook URL:
https://yourdomain.com/webhooks/jnt/status
Artisan Commands
# Check configuration php artisan jnt:config:check # Health check php artisan jnt:health # Track parcel php artisan jnt:order:track --order-id=ORDER-123 # Cancel order php artisan jnt:order:cancel --order-id=ORDER-123 --reason="Out of stock" # Print waybill php artisan jnt:order:print --order-id=ORDER-123 --tracking-number=JT123456
Documentation
- API Reference – Complete method reference
- Batch Operations – Process multiple orders efficiently
- Webhooks – Webhook integration guide
- Testing Credentials – Auto-configuration for sandbox
Property Name Mapping
The package translates clean names to J&T's API format automatically:
| Your Code | J&T API | Description |
|---|---|---|
orderId |
txlogisticId |
Your order reference |
trackingNumber |
billCode |
J&T tracking number |
state |
prov |
State/province |
quantity |
number |
Item quantity |
chargeableWeight |
packageChargeWeight |
Billable weight |
Testing
vendor/bin/pest tests/src/Jnt
Contributing
- Fork & clone the repository
- Install dependencies:
composer install - Run tests:
vendor/bin/pest - Format code:
vendor/bin/pint
License
Released under the MIT License. See LICENSE for details.