erikwang2013 / global-logistics
Unified facade for domestic (China) express and international logistics tracking APIs
Package info
github.com/erikwang2013/global-logistics
Type:yii2-extension
pkg:composer/erikwang2013/global-logistics
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.7
- psr/http-client: ^1.0
- psr/http-message: ^1.1|^2.0
Requires (Dev)
- illuminate/config: ^10.0|^11.0|^12.0
- illuminate/container: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- phpunit/phpunit: ^10.5
- topthink/framework: ^8.0
Suggests
- hyperf/framework: Hyperf ConfigProvider 配置发布(extra.hyperf.config)
- illuminate/support: Laravel 服务提供者自动注册(extra.laravel.providers)
- topthink/framework: ThinkPHP 8 服务自动注册(extra.think.services)
- workerman/webman-framework: Webman 插件自动拷贝配置(src/Install.php)
- yiisoft/yii2: Yii 2 引导自动注册(type=yii2-extension + extra.bootstrap)
README
全球物流聚合是统一门面的国内快递 / 国际物流轨迹查询 composer 包(PHP 8.2+,PSR-4,不绑定框架)。
项目介绍
global-logistics 把全球 209 家快递 / 邮政承运商的轨迹查询统一收敛为一个门面:业务方只需传入单号,自动识别国内 / 国际通道与承运商,无需关心各家协议差异(签名、OAuth2、XML/JSON、状态映射)。
| 指标 | 数值 |
|---|---|
| 已接入承运商 | 209 家(国内 45 + 国际 164) |
| 单号自动识别规则 | 187 条(顺序敏感,优先命中) |
| 国际覆盖 | 四大快递(DHL / FedEx / UPS / USPS)+ 各国邮政 S10(欧洲、拉美加勒比、非洲中东、亚太四区域) |
| 统一状态语义 | TrackStatus 7 种(含异常 / 退回) |
| 测试 | 1663 个用例 / 6662 断言,全绿 |
| 环境 | PHP 8.2+、PSR-4 / PSR-18,无框架绑定,Laravel / ThinkPHP / Hyperf / Webman / Yii 2 即装即用 |
项目说明
面向电商、仓储、ERP 等业务系统,把「国内快递 + 国际物流」的官方 API 统一收敛为一个门面:
- 一条入口:
Logistics::track($trackingNo)自动识别国内 / 国际通道与承运商,无需关心单号归属 - 一套数据模型:所有承运商返回统一的
Tracking/TrackingEvent结构,业务层只对接一种形状 - 一种状态语义:承运商五花八门的原始状态映射为统一的
TrackStatus枚举(7 种) - 全球覆盖:国际通道 164 家,含 DHL / FedEx / UPS / USPS 与各国邮政 S10 系统(欧洲、拉美加勒比、非洲中东、亚太四区域)
- 密钥零硬编码:各家密钥全部经配置注入,代码与密钥完全分离
功能说明
已接入承运商(209 家)
国内:顺丰、中通、圆通、极兔、韵达、申通、京东、EMS、百世、德邦、跨越、安能、菜鸟速递、中国邮政、苏宁物流、优速、壹米滴答、宅急送、天天快递、中通快运、丹鸟(菜鸟直送)、中铁快运、顺心捷达、速尔、信丰物流、联昊通、日日顺、丰网速运、百世快运、韵达快运、圆通快运、增益速递、民航快递、天地华宇、佳吉快运、龙邦速递、全一快递、速腾物流、中铁物流、中邮物流、增益速递、全峰快递、国通快递、远成快运、新邦物流
国际:DHL、FedEx、UPS、USPS、皇家邮政、加拿大邮政、澳大利亚邮政、日本邮政、Aramex、GLS、DPD、PostNL、菜鸟国际、巴西邮政、Evri、递四方、香港邮政、嘉里快递、韩国邮政、法国邮政、新西兰邮政、意大利邮政、俄罗斯邮政、新加坡邮政、瑞士邮政、Yodel、云途物流、燕文物流、顺丰国际、TNT、ONTRAQ、Purolator、bpost(比利时邮政)、Correos(西班牙邮政)、Delhivery(印度)、InPost(波兰包裹柜)、Omniva(爱沙尼亚)、Posti(芬兰)、Bring(挪威)、奥地利邮政、泰国邮政、中华邮政(台湾)、PostNord(瑞典/丹麦)、CTT(葡萄牙)、An Post(爱尔兰)、Poczta Polska(波兰)、India Post(印度)、Pos Malaysia(马来西亚)、Emirates Post(阿联酋)、Magyar Posta(匈牙利)、Česká pošta(捷克)、ELTA(希腊)、Viettel Post(越南)、中通国际、圆通国际、极兔国际、万邑通、Ukrposhta(乌克兰)、Turkey PTT(土耳其)、Israel Post(以色列)、Egypt Post(埃及)、Saudi Post(沙特)、South African Post(南非)、Correos de México(墨西哥)、Correo Argentino(阿根廷)、Correos de Chile(智利)、Pos Indonesia(印尼)、PHLPost(菲律宾)、Pakistan Post(巴基斯坦)、Kazpost(哈萨克斯坦)、Poșta Română(罗马尼亚)、Hrvatska pošta(克罗地亚)、Slovak Post(斯洛伐克)、Pošta Slovenije(斯洛文尼亚)、Pošta Srbije(塞尔维亚)、Bulgarian Posts(保加利亚)、Lietuvos paštas(立陶宛)、Latvijas Pasts(拉脱维亚)、Íslandspóstur(冰岛)、MaltaPost(马耳他)、POST Luxembourg(卢森堡)、Cyprus Post(塞浦路斯)、Poșta Moldovei(摩尔多瓦)、Posta Shqiptare(阿尔巴尼亚)、Belpochta(白俄罗斯)、Makedonska Pošta(北马其顿)、BH Pošta(波黑)、Deutsche Post(德国)、Montenegro Post(黑山)、Andorra Post(安道尔)、La Poste Monaco(摩纳哥)、Liechtenstein Post(列支敦士登)、Poste San Marino(圣马力诺)、Poste Vaticane(梵蒂冈)、Royal Gibraltar Post(直布罗陀)、Jersey Post(泽西)、Guernsey Post(根西)、Isle of Man Post(马恩岛)、Posta Faroe Islands(法罗群岛)、Post Greenland(格陵兰)、Post Åland(奥兰群岛)、4-72(哥伦比亚)、Serpost(秘鲁)、Correo Uruguayo(乌拉圭)、Correo Paraguayo(巴拉圭)、Correos de Bolivia(玻利维亚)、Correos del Ecuador(厄瓜多尔)、Ipostel(委内瑞拉)、Correos de Costa Rica(哥斯达黎加)、Correos de Panamá(巴拿马)、INPOSDOM(多米尼加)、Correo de Guatemala(危地马拉)、HonduCorreo(洪都拉斯)、Correos de El Salvador(萨尔瓦多)、Correos de Nicaragua(尼加拉瓜)、Correos de Cuba(古巴)、Jamaica Post(牙买加)、TTPOST(特立尼达和多巴哥)、Barbados Post(巴巴多斯)、Bahamas Post(巴哈马)、Suriname Post(苏里南)、Guyana Post(圭亚那)、Barid Al-Maghrib(摩洛哥)、Algérie Poste(阿尔及利亚)、La Poste Tunisienne(突尼斯)、Posta Kenya(肯尼亚)、NIPOST(尼日利亚)、Ethiopia Post(埃塞俄比亚)、Ghana Post(加纳)、Tanzania Post(坦桑尼亚)、Uganda Post(乌干达)、Rwanda Post(卢旺达)、Zampost(赞比亚)、Zimpost(津巴布韦)、Mozambique Post(莫桑比克)、Correios de Angola(安哥拉)、La Poste Sénégalaise(塞内加尔)、La Poste de Côte d'Ivoire(科特迪瓦)、Cameroon Post(喀麦隆)、Mauritius Post(毛里求斯)、Qatar Post(卡塔尔)、Kuwait Post(科威特)、Bahrain Post(巴林)、Bangladesh Post(孟加拉)、Nepal Post(尼泊尔)、Sri Lanka Post(斯里兰卡)、Myanmar Post(缅甸)、Cambodia Post(柬埔寨)、Laos Post(老挝)、Mongolia Post(蒙古)、Georgian Post(格鲁吉亚)、Azərpoçt(阿塞拜疆)、HayPost(亚美尼亚)、Uzbekistan Post(乌兹别克斯坦)、Kyrgyz Post(吉尔吉斯斯坦)、Tajikistan Post(塔吉克斯坦)、Turkmenistan Post(土库曼斯坦)、Afghanistan Post(阿富汗)、Bhutan Post(不丹)、Maldives Post(马尔代夫)、Brunei Post(文莱)、Papua New Guinea Post(巴布亚新几内亚)、Fiji Post(斐济)、Samoa Post(萨摩亚)
统一状态枚举(GlobalLogistics\Support\TrackStatus)
PENDING(待揽收)→ IN_TRANSIT(运输中)→ OUT_FOR_DELIVERY(派送中)→ DELIVERED(已签收);异常归 EXCEPTION,退回归 RETURNED,无法识别归 UNKNOWN。
核心能力
- 单号自动检测(187 条正则规则,顺序敏感,优先命中国内规则)
- 统一轨迹查询(
Logistics::track())与显式通道调用(domestic()/international()) - 统一异常体系(认证失败 / 单号不存在 / 网络错误 / 承运商未注册 / 接口错误)
- HTTP 基础设施:PSR-18 客户端、OAuth2 token 自动获取与缓存、失败自动重试
- 框架自动发现:Laravel / ThinkPHP 8 / Hyperf / Webman / Yii 2 即装即用
- 回调签名验证(顺丰示例:
verifyCallbackSignature())
使用说明
安装
composer require erikwang2013/global-logistics
配置
<?php use GlobalLogistics\Logistics; Logistics::configure([ // 国内 'sf' => ['partner_id' => '...', 'checkword' => '...'], 'zto' => ['company_id' => '...', 'secret' => '...'], 'yto' => ['app_key' => '...', 'app_secret' => '...'], 'jt' => ['api_key' => '...', 'secret' => '...'], 'yd' => ['app_key' => '...', 'app_secret' => '...'], 'sto' => [], 'jd' => [], 'ems' => ['app_id' => '...'], 'ht' => ['partner_id' => '...', 'token' => '...'], 'debon' => ['app_key' => '...', 'app_secret' => '...'], 'ky' => ['app_key' => '...', 'app_secret' => '...'], 'ane' => ['app_key' => '...'], // 国际 'dhl' => ['client_id' => '...', 'client_secret' => '...'], 'fedex' => ['client_id' => '...', 'client_secret' => '...'], 'ups' => ['client_id' => '...', 'client_secret' => '...'], 'usps' => ['user_id' => '...'], 'royal-mail' => ['client_id' => '...', 'client_secret' => '...'], 'canada-post' => ['customer_number' => '...', 'api_key' => '...'], 'australia-post' => ['api_key' => '...'], 'japan-post' => [], 'aramex' => ['user_name' => '...', 'password' => '...', 'account_number' => '...'], 'gls' => ['api_key' => '...'], 'dpd' => ['user_name' => '...', 'password' => '...'], 'postnl' => ['api_key' => '...'], // 可选:自定义 PSR-18 HTTP 客户端(默认自动构建 Guzzle) 'http_client' => null, // 可选:失败重试次数(默认 2) 'max_retries' => 2, ]);
框架项目(Laravel 等)可直接使用
config/logistics.php模板,见「框架集成」。未调用
configure()时门面会自动以空配置初始化(只支持无需密钥的承运商)。
配置项详解
顶层选项
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
http_client |
PSR-18 客户端 或 null |
null |
自定义 HTTP 客户端;null 自动构建 Guzzle |
max_retries |
int | 2 |
单次请求失败后的重试次数 |
registry |
array | 内置 209 家注册表 | 自定义「通道 → 代码 → 适配器类」注册表(进阶) |
detector_rules |
array | 内置 187 条规则 | 自定义单号识别规则(进阶) |
密钥字段速查
各家接口认证方式不同,字段命名因此各异,常见对应关系如下:
| 字段 | 含义 | 典型承运商 |
|---|---|---|
partner_id + checkword |
顺丰式签名账号与校验码 | sf、sf-international |
company_id + secret |
中通式公司账号与密钥 | zto、zto-freight、zto-intl |
app_key + app_secret |
通用签名密钥 | yto、yd、jt、debon、ky 等 |
ebusiness_id + app_key |
快递鸟接口(电子面单) | lht、rrs、sure、xf 及各家快运 |
client_id + client_secret |
OAuth2 客户端凭据(token 自动获取与刷新) | dhl、fedex、ups、royal-mail、swiss-post、yodel |
user_id / api_key / key |
各家分配的账号或密钥 | usps、postnl、gls、saudi-post 等 |
endpoint |
自定义 API 端点(可选,留空 '' 使用内置官方地址) | 支持各家 |
每个承运商需要哪些字段,以
config/logistics.php中对应条目的键为准;无需密钥的承运商留空数组即可。
密钥获取
- 国内快递:联系承运商商务或对接人申请「轨迹查询 / 电子面单接口」账号(顺丰丰桥、中通开放平台等)
- 国际快递:DHL / FedEx / UPS 等在官网开发者门户注册开发者账号获取 OAuth2 凭据;USPS、PostNL 等官网申请 API Key
- 各国邮政:多数提供公开轨迹查询 API 无需密钥;需要密钥的在邮政官网开发者中心申请
无需密钥的承运商
以下承运商直接可用,配置留空数组即可:国内 sto(申通)、jd(京东);国际 japan-post(日本邮政)及多数邮政(turkey-post、israel-post、egypt-post、south-african-post、phl-post、pakistan-post、kazpost 等),详见 config/logistics.php。
密钥安全
密钥请通过环境变量或框架 .env 注入,避免硬编码进代码仓库:
Logistics::configure([ 'sf' => [ 'partner_id' => getenv('SF_PARTNER_ID'), 'checkword' => getenv('SF_CHECKWORD'), ], ]);
查询轨迹
// 自动识别通道(国内/国际)与承运商 $tracking = Logistics::track('SF1234567890'); // 显式指定(单号规则无法覆盖时) $tracking = Logistics::domestic('sf')->queryTrack('SF1234567890'); $tracking = Logistics::international('dhl')->queryTrack('DHL1234567890'); echo $tracking->status->name; // DELIVERED echo $tracking->latestDescription; // 快件已签收 echo $tracking->carrierCode; // sf echo $tracking->deliveredAt?->format('Y-m-d H:i:s'); // 签收时间(已签收时) foreach ($tracking->events as $event) { echo $event->occurredAt?->format('Y-m-d H:i:s'), ' ', $event->location, ' ', $event->description, PHP_EOL; }
错误处理
所有异常继承 GlobalLogistics\Exceptions\LogisticsException,可统一捕获:
| 异常 | 场景 |
|---|---|
CarrierNotFoundException |
单号无法识别承运商 |
TrackingNotFoundException |
单号合法但承运商查无轨迹 |
AuthException |
认证失败(密钥错误等) |
NetworkException |
HTTP 网络错误(实现 PSR-18 NetworkExceptionInterface) |
LogisticsException |
其他接口/解析错误 |
use GlobalLogistics\Exceptions\LogisticsException; try { $tracking = Logistics::track('SF1234567890'); } catch (LogisticsException $e) { // 记录 $e->getMessage(),其中含承运商代码与原始错误码,如 "[SF A1001] 必传参数不可为空" }
回调验签(订阅推送)
承运商开放订阅接口后,回调处理示例(以顺丰为例):
use GlobalLogistics\Logistics; $carrier = Logistics::domestic('sf'); if (!$carrier->verifyCallbackSignature((string) file_get_contents('php://input'), (string) $_SERVER['HTTP_DIGEST'])) { http_response_code(401); exit('signature mismatch'); } // 验签通过,处理轨迹推送……
架构设计
目录结构
global-logistics/
├── src/
│ ├── Carriers/
│ │ ├── Domestic/ # 国内 45 家适配器(顺丰、中通、圆通、…)
│ │ └── International/ # 国际 164 家适配器(DHL、FedEx、UPS、各国邮政 S10、…)
│ ├── Exceptions/ # 异常体系(LogisticsException + 4 个细分场景异常)
│ ├── Framework/ # 框架自动发现(Laravel / ThinkPHP / Hyperf / Webman / Yii 2)
│ ├── Http/ # PSR-18:OAuthTokenClient、RetryingClient、HttpClientFactory
│ ├── Models/ # Tracking / TrackingEvent / Order / OrderRequest / Label
│ ├── Resources/ # carrier-registry.php(209 家注册表)、detector-rules.php(187 条规则)
│ ├── Support/ # TrackStatus 等支持类
│ ├── CarrierFactory.php # 注册表 → 适配器实例化
│ ├── CarrierInterface.php # 适配器统一契约
│ ├── Channel.php # 国内 / 国际通道枚举
│ ├── Config.php # 点号键配置读取
│ ├── Detection.php # 单号检测结果
│ ├── Detector.php # 单号规则检测
│ ├── Install.php # 安装引导
│ └── Logistics.php # 静态门面
├── config/
│ └── logistics.php # 配置模板(209 家密钥占位)
├── docs/
│ ├── images/ # 架构图 / 设计时序图
│ └── superpowers/ # 设计规格与实施计划
├── tests/
│ ├── Carriers/ # 每承运商 7 个用例(共 539 个)
│ ├── Unit/ # 检测器、注册表冒烟等
│ └── fixtures/ # 每家 track / empty / error 夹具
├── composer.json
└── README.md
各层职责
Logistics(src/Logistics.php):静态门面,持有全局配置、检测器与工厂;未配置时自动以空配置初始化Detector(src/Detector.php+src/Resources/detector-rules.php):正则规则表按顺序首次命中,返回Detection(通道 + 承运商代码);规则顺序敏感(如 77 开头申通须先于纯 13 位数字规则)CarrierFactory(src/CarrierFactory.php+src/Resources/carrier-registry.php):按「通道 → 代码 → 适配器类」注册表实例化适配器,统一注入Config与 HTTP 客户端- 承运商适配器(
src/Carriers/):实现CarrierInterface,负责各家协议差异(签名、OAuth2、XML/JSON、状态映射);同一模板结构(ENDPOINT常量 +STATUS_MAP+mapEvent()),便于按模板新增承运商 - HTTP 层(
src/Http/):OAuthTokenClient为 PSR-18 装饰器,懒获取 token、进程内缓存(提前 60s 过期)、401 时刷新重试一次;RetryingClient按max_retries重试失败请求 - 模型层(
src/Models/):Tracking/TrackingEvent为不可变对象;Order/OrderRequest/Label为下单、面单能力预留 Config(src/Config.php):点号键取值($config->get('dhl.client_id'))- 异常体系(
src/Exceptions/):LogisticsException为基类,细分 4 个场景异常
扩展新承运商
- 新建适配器类(参照
src/Carriers/Domestic/Yto.php模板):实现CarrierInterface,在mapEvent()中做状态映射 - 注册表:
src/Resources/carrier-registry.php增加「通道 → 代码 → 类」 - 单号规则:
src/Resources/detector-rules.php增加正则(注意顺序敏感,国内规则优先) - 补 fixture 与适配器测试(mock HTTP,无需真实密钥)
框架集成
composer require erikwang2013/global-logistics 后按框架自动发现,无需手工注册;配置模板统一为承运商代码为顶层键的数组(见 config/logistics.php,结构与 Logistics::configure() 入参一致)。
Laravel
- 自动注册:composer
extra.laravel.providers包发现,无需配置 - 发布配置:
php artisan vendor:publish --tag=global-logistics(生成config/logistics.php) - 使用:
\GlobalLogistics\Logistics::track('SF1234567890')
ThinkPHP 8
- 自动注册:composer
extra.think.services(安装时生成vendor/services.php,也可php think service:discover重新生成) - 配置:应用
config/logistics.php返回同结构数组(覆盖包内默认值) - 使用:
\GlobalLogistics\Logistics::track('SF1234567890')
Hyperf
- 自动发现:composer
extra.hyperf.config指向 ConfigProvider - 发布配置:
php bin/hyperf.php vendor:publish(发布到config/autoload/logistics.php) - 使用:
\GlobalLogistics\Logistics::track('SF1234567890')
Webman
- 自动安装:webman 项目模板自带
post-package-install等 composer 钩子,安装/更新时自动把配置拷贝到config/plugin/erikwang2013/global-logistics/,卸载时自动删除 - 读取配置:
config('plugin.erikwang2013.global-logistics.app.sf.partner_id') - 使用:
\GlobalLogistics\Logistics::track('SF1234567890')
Yii 2
- 自动注册:包类型
yii2-extension+ composerextra.bootstrap,应用每次引导时执行 - 配置:应用配置
params中加'logistics' => [...同结构数组...] - 使用:
\GlobalLogistics\Logistics::track('SF1234567890') - 若
vendor/yiisoft/extensions.php未出现本包条目(罕见),运行composer dump-autoload重建
开发
composer install
composer test
无需真实密钥即可跑全量测试(适配器测试走 mock HTTP + fixture;框架集成测试使用真实框架类)。