API ZR Express en Node.js et PHP
Créez, suivez, listez et annulez vos colis ZRExpress avec le SDK dz-delivery — plus les étiquettes PDF, la création en masse et la vérification des webhooks.
Installer
npm install @dz-delivery/core
composer require dz-delivery/sdk
Node.js ≥ 18 (aucune dépendance) ou PHP ≥ 8.2 (Guzzle 7). Le même code fonctionne ensuite pour les autres transporteurs : seule la configuration du client change.
Identifiants ZRExpress
| Option | Header envoyé | Où la trouver |
|---|---|---|
apiKey | X-Api-Key | Portail ZRExpress (clé API secrète) |
tenant | X-Tenant | Portail ZRExpress (identifiant du tenant) |
Documentation officielle : docs.zrexpress.app.
ZRExpress en TypeScript / Node.js
import { createClient, isShippingError } from '@dz-delivery/core'; const zr = createClient('zrexpress', { apiKey: process.env.ZREXPRESS_API_KEY!, tenant: process.env.ZREXPRESS_TENANT!, }); const wilayas = await zr.getWilayas(); // [{ id, name, code: '16' }] const communes = await zr.getCommunes(16); // id ZR ou code officiel 1–58 const hubs = await zr.getStopDesks({ wilayaId: 16 }); // points relais const quote = await zr.getFees({ toWilayaId: 16, isStopDesk: false }); // { deliveryFee: 400, stopDeskFee: 250, currency: 'DZD' } try { const parcel = await zr.createParcel({ recipientName: 'Mohamed Amine', phone: '0550 12 34 56', // normalisé en +213… address: 'Cité 221 lots, n°98', wilayaName: 'Alger', communeName: 'Bab Ezzouar', productName: 'T-shirt', price: 2500, // montant encaissé, frais inclus (≤ 150 000 DZD) orderId: 'ORDER-1001', }); const { parcel: current, events } = await zr.track(parcel.trackingNumber); // Synchroniser les colis du compte (pull) const page = await zr.listParcels({ updatedSince: '2026-10-01T00:00:00+00:00', allPages: true }); await zr.cancelParcel(parcel.trackingNumber); // numéro de suivi ou UUID } catch (err) { if (isShippingError(err)) console.error(err.code, err.message); }
ZRExpress en PHP / Laravel
use DzDelivery\ClientFactory; use DzDelivery\Errors\ShippingError; $zr = ClientFactory::create('zrexpress', [ 'apiKey' => getenv('ZREXPRESS_API_KEY'), 'tenant' => getenv('ZREXPRESS_TENANT'), ]); $quote = $zr->getFees(['toWilayaId' => 16, 'isStopDesk' => false]); $hubs = $zr->getStopDesks(['wilayaId' => 16]); try { $parcel = $zr->createParcel([ 'recipientName' => 'Mohamed Amine', 'phone' => '0550123456', 'address' => 'Cité 221 lots, n°98', 'wilayaName' => 'Alger', 'communeName' => 'Bab Ezzouar', 'productName' => 'T-shirt', 'price' => 2500, 'orderId' => 'ORDER-1001', ]); $result = $zr->track($parcel->trackingNumber); $page = $zr->listParcels(['orderId' => 'ORDER-1001']); } catch (ShippingError $e) { echo "{$e->code}: {$e->getMessage()}"; }
Guide PHP complet (Laravel, erreurs, tests) : guide-php.md.
Méthodes supportées
| Méthode | Support | Notes |
|---|---|---|
createParcel | oui | Le SDK relit le colis créé pour renvoyer le numéro de suivi. |
track | oui | Statut unifié + historique des états. |
getFees | oui | Tarif domicile et point relais pour la wilaya. |
getWilayas / getCommunes | oui | Territoires ZR, code officiel ou nom acceptés. |
getStopDesks | oui | Hubs ZR avec point relais. |
listParcels | oui | Recherche paginée, filtres orderId, keyword, status, updatedSince. |
cancelParcel | oui | Numéro de suivi ou UUID. |
healthCheck | oui | Vérifie les identifiants (appel réel). |
Étiquettes, envoi en masse et webhooks ZRExpress
Ces fonctions propres à ZRExpress sont disponibles via createZRExpress() (TS) ou new ZRExpressProvider() (PHP).
import { createZRExpress, verifyZRExpressWebhook } from '@dz-delivery/core'; const zr = createZRExpress({ apiKey, tenant }); // Étiquettes PDF (max 250) const { fileUrl } = await zr.generateLabels(['16-AAA111-ZR', '16-BBB222-ZR']); // Création en masse (max 100, orderId obligatoire) const { successes, failures } = await zr.createParcelsBulk([ { ...parcelInput, orderId: 'ORD-1' }, { ...parcelInput, orderId: 'ORD-2' }, ]); // Webhooks signés (corps brut obligatoire) app.post('/webhooks/zrexpress', express.raw({ type: '*/*' }), (req, res) => { const event = verifyZRExpressWebhook(req.body, req.headers, process.env.ZREXPRESS_WEBHOOK_SECRET!); // event.type · event.trackingNumber · event.status (unifié) res.sendStatus(200); });
use DzDelivery\Providers\ZRExpressProvider; use DzDelivery\Providers\ZRExpress\Webhook; $zr = new ZRExpressProvider(['apiKey' => getenv('ZREXPRESS_API_KEY'), 'tenant' => getenv('ZREXPRESS_TENANT')]); $zr->createParcelsBulk([...]); $raw = file_get_contents('php://input'); $event = Webhook::verify($raw, getallheaders(), getenv('ZREXPRESS_WEBHOOK_SECRET'));
Détails : ZREXPRESS.md.
À savoir sur l’API ZRExpress
- Montant ≤ 150 000 DZD, frais de livraison inclus (
price). - Les téléphones sont envoyés au format international (
+213…) : passez-les comme vous les avez, le SDK normalise. - Sans changement d’état côté portail, le colis reste « Commande reçue » (
pending) jusqu’à être marqué prêt à expédier. - Stop desks = hubs ZR avec point relais ; filtrer par wilaya seule donne les meilleurs résultats.
- Produits :
stockTypevautnonepar défaut (pas de catalogue).local/warehousedemandentproductId+sku. - En production, préférez les webhooks pour suivre les statuts ;
listParcelsest l’alternative en pull.
Mapping natif détaillé (endpoints, champs, statuts) : PROVIDERS.md.
FAQ ZRExpress
Où trouver l’API key et le tenant ZRExpress ?
Dans le portail ZRExpress : la clé API va dans apiKey (header X-Api-Key) et l’identifiant du tenant dans tenant (header X-Tenant).
Comment imprimer les étiquettes ZR Express ?
generateLabels([...trackings]) renvoie l’URL d’un PDF regroupé, ou un PDF par colis avec { mode: 'individual' }.
Comment recevoir les changements de statut ZRExpress ?
Configurez un webhook dans le portail, puis vérifiez la signature avec verifyZRExpressWebhook (TS) ou Webhook::verify (PHP) sur le corps brut de la requête.
Puis-je annuler un colis avec son numéro de suivi ?
Oui : cancelParcel accepte le numéro de suivi ou l’UUID ZR.
Autres transporteurs
Même interface, mêmes statuts normalisés (pending, in_transit, delivered…).