@dz-delivery/core - TypeScript / Node.js的阿尔及利亚交付SDK
阿尔及利亚快递公司的一个API Yalidine, GuepEx, zreexpress, Maystro, Ecotrack (DHD, Anderson, World Express,…),NoEST, E-com Delivery, MDM, Elogistia. 创建包裹,跟踪货运,报价费用,并为每个承运人列出具有相同代码的村庄/公社/停止台。
🇩🇿SDK livraison alg -郁闷郁闷:郁闷郁闷,郁闷郁闷,郁闷郁闷,郁闷郁闷。 文档: https://developing-off.github.io/dz-delivery/ ·PHP / Laravel:
dz-delivery/sdk
中实现共享契约
docs/CONTRACT.md;包含特定于载波的映射
docs/PROVIDERS.md.
徽标是其各自所有者的商标,仅用于指示SDK与哪些运营商合作。Dz-delivery是一个独立的开源项目,不隶属于这些公司或得到这些公司的认可。
| 提供者 | 关键 | 状态 |
|---|---|---|
| ZRExpress | zrexpress | ✅合同统一(创建,跟踪,费用,地点,停止办公桌,列表,取消,健康)+ 临时演员 (散装,标签,webhook) |
| Yalidine | yalidine | ✅contrat unifi<e:1>(根据开发人员文档检查映射,参见PROVIDERS.md) |
| Maystro交付 | maystro | ✅createParcel, track, getFees, gettwilayas, getCommunes, getstoptables, listpackages, cancelParcel - extra getLabels (PDF格式 MaystroProvider |
| Ecotrack (DHD, Conexlog,…) | ecotrack | ✅contratunifi<e:1> -易趣平台每一名速递服务提供者(courier 或者 baseUrl) |
| 没有 | noest | ✅contrat unifi<e:1> - listParcels 抛出 NOT_SUPPORTED (没有列表端点) |
| E-com交付 | ecom | ✅合同统一,包括 listParcels |
| MDM表达 | mdm | ✅从MDM目录中获取统一的产品; storeId) - - - cancelParcel 抛出 NOT_SUPPORTED |
| Elogistia | elogistia | ✅createParcel, track, getFees, gettwilayas, getCommunes - getStopDesks / listParcels / cancelParcel 扔 NOT_SUPPORTED |
- ESM + CommonJS,绑定类型定义
- Node.js≥18(使用global
fetch),没有运行时依赖项
安装
npm install @dz-delivery/core
使用
import { createClient, createZRExpress, isShippingError } from '@dz-delivery/core';
const zr = createClient('zrexpress', {
apiKey: process.env.ZREXPRESS_API_KEY!,
tenant: process.env.ZREXPRESS_TENANT!,
});
// Extras ZR (bulk / labels / typed provider): createZRExpress({ apiKey, tenant })
// Webhooks: verifyZRExpressWebhook(rawBody, headers, secret) — see docs/ZREXPRESS.md
const yalidine = createClient('yalidine', {
apiId: process.env.YALIDINE_API_ID!,
apiToken: process.env.YALIDINE_API_TOKEN!,
fromWilayaId: 16, // origin wilaya, required by Yalidine to create parcels / quote fees
// GuepEx (same API): import { GUEPEX_DEFAULT_BASE_URL } and set baseUrl: GUEPEX_DEFAULT_BASE_URL
});
const maystro = createClient('maystro', {
apiToken: process.env.MAYSTRO_API_TOKEN!, // sent as `Authorization: Token <apiToken>`
});
// track / cancelParcel take Parcel.id or the tracking number. For Maystro webhooks:
// mapMaystroStatus(payload.status) → 'delivered' for 41
const dhd = createClient('ecotrack', {
token: process.env.ECOTRACK_TOKEN!, // sent as `Authorization: Bearer <token>`
courier: 'dhd', // or baseUrl: 'https://<tenant>.ecotrack.dz'
// autoValidate: true, // hand orders to the courier right after creation
});
// Locations
const wilayas = await zr.getWilayas(); // [{ id, name, code: '16', raw }]
const communes = await zr.getCommunes(16); // provider id or official code 1–58
// Fees
const quote = await zr.getFees({ toWilayaId: 16, isStopDesk: false });
// { deliveryFee: 400, stopDeskFee: 250, currency: 'DZD', raw }
// Create a parcel (ids, official codes or names are accepted)
const parcel = await zr.createParcel({
recipientName: 'Mohamed Amine',
phone: '0550 12 34 56', // normalized to the carrier's format
address: 'Cité 221 lots, n°98',
wilayaName: 'Alger',
communeName: 'Bab Ezzouar',
productName: 'T-shirt',
price: 2500, // amount collected from the recipient (DZD)
orderId: 'ORDER-1001',
});
// { id, trackingNumber: 'DZ-AL-…', status: 'pending', provider: 'zrexpress', raw }
// Track
const { parcel: current, events } = await zr.track(parcel.trackingNumber);
// events are sorted oldest first: [{ status: 'pending', label: 'Commande reçue', at: '2025-…+00:00' }, …]
// Cancel (tracking number or internal id)
await zr.cancelParcel(parcel.trackingNumber);
方法
每个提供者实现 ShippingProvider:
| 方法 | 返回 |
|---|---|
createParcel(input: ParcelInput) | Promise<Parcel> |
track(trackingNumber) | Promise<{ parcel, events }> |
getFees(query: FeeQuery) | Promise<FeeQuote> |
getWilayas() | Promise<Location[]> (每个客户端缓存) |
getCommunes(wilayaId) | Promise<Location[]> (每个客户端缓存) |
getStopDesks({ wilayaId, communeId? }) | Promise<StopDesk[]> - - - - - - id 是 stopDeskId 发送zreexpress集线器:首选wilaya-only; Yalidine中心;Maystro需要 communeId;Ecotrack有一个停车服务台)。可选字段: phone, openingHours, lat, lng, type. |
listParcels(query?) | Promise<ParcelListPage> -分页账户包裹+统一状态(zreexpress via) parcels/searchMaystro via GET /api/stores/orders/ - 20每页,过滤器应用客户端-,E-com通过 GET /colis、MDM via parcels/search;其他人 NOT_SUPPORTED 到目前为止)。没有事件历史记录-使用 track(). |
cancelParcel(trackingNumberOrId) | Promise<void> |
healthCheck() | Promise<{ ok: true, provider, latencyMs }> -验证连接探针(wilayas列表,绕过缓存) |
状态统一: pending | created | in_transit | delivered | returned | cancelled | unknown.
未映射的本机状态永远不会抛出——它映射到 unknown 原生标签保留了下来
TrackingEvent.label / raw.
选项
| 选项 | 默认的 | 描述 |
|---|---|---|
baseUrl | 承运人违约 | 覆盖API基URL(沙箱、代理)。 |
timeoutMs | 15000 | HTTP超时。 |
includeRaw | true | 将本机响应附加为 raw 在每个返回的模型上。 |
fetch | globalThis.fetch | 自定义获取实现(测试、代理)。 |
提供者凭证: zrexpress → apiKey, tenant; yalidine → apiId, apiToken
(+ fromWilayaId 或者 fromWilayaName); maystro → apiToken; ecotrack → token +恰好之一
courier (dhd, anderson, worldexpress, 48hrexpress, ecorapid, conexlog, msmgo, packers, rocketdelivery;别名 andersondelivery)或
baseUrl (任何租户, https 唯一的),可选 autoValidate; noest → apiToken, userGuid、可选 autoValidate; ecom → apiKey, apiToken、可选 autoValidate.
错误
每一次失败都是一次 ShippingError 用标准化的 code:
try {
await zr.track('DZ-UNKNOWN');
} catch (err) {
if (isShippingError(err)) {
err.code; // 'NOT_FOUND' | 'AUTH_FAILED' | 'VALIDATION_ERROR' | 'RATE_LIMITED' | 'NOT_SUPPORTED' | 'PROVIDER_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN'
err.httpStatus; // 404
err.details; // e.g. [{ field: 'phone', message: '…' }] for validation errors
err.raw; // native error body
}
}
在任何HTTP调用(VALIDATION_ERROR),而wilaya/公社将其命名为
无法解析抛出 NOT_FOUND.
开发
npm install
npm test # vitest — mocked fetch, no network
npm run typecheck
npm run build # tsup → dist/ (ESM + CJS + .d.ts)
测试在中重播共享的fixture docs/fixtures/ (也用于the
PHP包),因此两个实现对相同的本机响应产生相同的输出。
一个可运行的例子 examples/ts/quickstart.ts.







