enan/pathao-courier
A Laravel wrapper for Pathao Courier that simplifies integrating delivery services into your app, with support for creating orders, calculating delivery charges, tracking parcels, and managing courier statuses via a clean, configurable API.
Install and discover the package
composer require enan/pathao-courier
Laravel should auto-discover the service provider. If not, register Enan\PathaoCourier\PathaoCourierServiceProvider and the facade alias manually in config/app.php.
Publish the config and set credentials
php artisan vendor:publish --tag=pathao-courier-config
Add your Pathao merchant credentials to .env:
PATHAO_CLIENT_ID=
PATHAO_CLIENT_SECRET=
PATHAO_USERNAME=
PATHAO_PASSWORD=
PATHAO_BASE_URL=https://api-hermes.pathao.com
Check the published config/pathao-courier.php for exact key names and sandbox/production URL support.
Look here first
README.md for the package’s documented facade methods.config/pathao-courier.php for credentials, base URL, and timeout options.src/PathaoCourierServiceProvider.php for bindings, singleton registration, and config merging.src/Facades/PathaoCourier.php and the underlying client class for the real API surface.src/ for request/response handling — this package is small, so the source is the fastest reference.First use case: authenticate and fetch cities
use Enan\PathaoCourier\Facades\PathaoCourier;
// Confirm exact method names in the client class.
$token = PathaoCourier::getAccessToken();
$cities = PathaoCourier::getCities();
// Then fetch zones/areas for a city ID before creating an order.
$zones = PathaoCourier::getZones($cities[0]['city_id']);
A good first milestone is: obtain a token, list cities/zones/areas, calculate a delivery price, and create one test order in Pathao’s sandbox if available.
Wrap the package in your own service class
Don’t scatter facade calls across controllers. Create App\Services\PathaoService that injects or resolves the package client, normalizes responses, and centralizes error handling. This makes it easy to swap or fork the package later.
Cache and refresh the access token
Pathao uses OAuth-style credentials. Avoid requesting a new token on every API call. Cache the token in Laravel’s cache with an expiry slightly shorter than the API’s TTL, and refresh only when expired or when a 401 occurs.
Create shipments from queued jobs
Order creation should not block a customer checkout request. Dispatch a job such as CreatePathaoShipment after payment is confirmed:
CreatePathaoShipment::dispatch($order)->onQueue('shipping');
In the job, validate city/zone/area IDs, calculate price, create the order, then persist pathao_consignment_id, tracking_code, and status on your local order.
Model the order lifecycle explicitly
Store Pathao fields on your orders table or a separate shipments table: consignment ID, merchant order ID, tracking code, delivery status, last synced timestamp. Use a scheduled command or webhook controller to update statuses rather than polling on every page load.
Use events/listeners for e-commerce integration
Example flow: OrderPaid → listener dispatches CreatePathaoShipment → job calls Pathao → ShipmentCreated event updates admin UI. Keep Pathao-specific logic behind events so your domain code stays clean.
Handle multiple stores/merchants
The package likely binds a singleton client from one config. If you need multiple Pathao accounts, instantiate the client manually with per-store credentials or build a small factory that resolves clients from your stores table. Do not mutate global config at runtime unless the package explicitly supports it.
Test without hitting Pathao
Check whether the package uses Laravel’s Http facade. If so, use Http::fake() in tests. Otherwise, mock the facade or bind a fake client implementation in the container. Keep one integration test that runs against sandbox credentials and is excluded from normal CI.
Admin panel integration
Add a “Book with Pathao” action that dispatches the shipment job, and display tracking status from your local shipments table. This keeps admin pages fast and resilient when Pathao is slow or down.
Treat the package as low-support surface area
With 14 stars and zero dependents, documentation and API stability may be limited. Pin the version in composer.json, read the source before relying on a method, and keep your own wrapper so a package change does not ripple through your app.
Config caching bites
After changing .env credentials, run php artisan config:clear locally and ensure production deploys set env vars before config:cache. A stale cached config is a common source of “invalid credentials” errors.
Token expiry and 401 handling
Do not assume the token lasts forever. Catch authentication failures, clear the cached token, refresh once, and retry the request. Log the failure reason but redact client_secret, password, and access_token.
Address IDs are strict
Pathao requires valid city_id, zone_id, and area_id. Cache the city/zone/area lookups and validate them before order creation. A free-text address alone is not enough; mismatched IDs cause failed bookings.
Error responses may be arrays, not exceptions
Inspect how the client returns errors. If it returns response arrays without throwing, your job can silently “succeed.” Normalize responses in your wrapper and throw a domain exception when code/status indicates failure.
Set HTTP timeouts and retries
Pathao API calls can be slow. Configure a reasonable timeout in the package config if available, and use queued jobs with exponential backoff for 429 and 5xx responses. Avoid retrying validation errors.
Price calculation may not include everything Verify whether the package’s price method accounts for weight, COD, delivery type, and discounts. If not, treat the returned price as an estimate and confirm the final charge in your order records.
Webhooks are your responsibility If Pathao sends status webhooks, the package may not verify signatures or provide a route. Build a controller, validate the payload against your stored consignment ID, and update shipment status idempotently.
Extension points Publish the config first. If the package binds a concrete client with no contract, you can still wrap it, bind your own service, or fork the package under a local namespace. Prefer wrapping over editing vendor files.
How can I help you explore Laravel packages today?