# express-api **Repository Path**: msnail/express-api ## Basic Information - **Project Name**: express-api - **Description**: No description available - **Primary Language**: PHP - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-24 - **Last Updated**: 2026-02-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # kode/express-api 通用快递API集成包,支持多种快递公司接口,提供统一的调用方式,便于快速集成到各种PHP项目中。 ## 功能特性 - 支持多种快递公司API(目前已集成:EMS、顺丰SF、韵达、中通、申通、菜鸟网络) - 统一的接口调用方式,简化开发流程 - 灵活的面单布局管理,支持可视化编辑 - 完善的错误处理和响应标准化 - 支持PSR-12代码规范 - 支持PHP 7.4+ - 支持多语言字段配置 - 丰富的测试用例,测试覆盖率高 ## 安装 使用Composer安装: ```bash composer require kode/express-api ``` ## 配置说明 ### 获取API密钥 要使用各快递公司API,您需要在相应开放平台注册并获取API密钥: 1. **EMS**: - 访问[EMS开放平台](https://api.ems.com.cn/) - 注册开发者账号并完成企业认证 - 在API控制台选择需要的服务接口 - 获取API密钥(AppKey和AppSecret) 2. **顺丰SF**: - 访问顺丰开放平台 - 注册开发者账号并申请API权限 - 获取API密钥 3. **韵达**: - 访问韵达开放平台 - 注册开发者账号并完成认证 - 获取API密钥 4. **中通**: - 访问中通开放平台 - 注册开发者账号并完成认证 - 获取API密钥 5. **申通**: - 访问申通开放平台 - 注册开发者账号并完成认证 - 获取API密钥 6. **菜鸟网络**: - 访问菜鸟开放平台 - 注册开发者账号并完成认证 - 获取AppKey、AppSecret和PartnerId ### 环境配置 #### 生产环境 在生产环境中使用真实的EMS API端点: ```php $config = new \Kode\ExpressApi\EMS\Config([ 'app_key' => 'your_production_app_key', 'app_secret' => 'your_production_app_secret', 'sandbox' => false, // 默认值,可省略 ]); ``` #### 沙箱环境 在开发和测试阶段,建议使用沙箱环境: ```php $config = new \Kode\ExpressApi\EMS\Config([ 'app_key' => 'your_sandbox_app_key', 'app_secret' => 'your_sandbox_app_secret', 'sandbox' => true, ]); // 韵达配置示例 $yundaConfig = new \Kode\ExpressApi\Yunda\Config([ 'app_key' => 'your_yunda_app_key', 'app_secret' => 'your_yunda_app_secret', 'sandbox' => true, ]); // 中通配置示例 $ztoConfig = new \Kode\ExpressApi\ZTO\Config([ 'app_key' => 'your_zto_app_key', 'app_secret' => 'your_zto_app_secret', 'sandbox' => true, ]); // 申通配置示例 $stoConfig = new \Kode\ExpressApi\STO\Config([ 'app_key' => 'your_sto_app_key', 'app_secret' => 'your_sto_app_secret', 'sandbox' => true, ]); // 菜鸟网络配置示例 $cainiaoConfig = new \Kode\ExpressApi\Cainiao\Config([ 'app_key' => 'your_cainiao_app_key', 'app_secret' => 'your_cainiao_app_secret', 'partner_id' => 'your_cainiao_partner_id', 'sandbox' => true, ]); ``` ### 配置参数详解 #### app_key - **类型**: string - **必填**: 是 - **说明**: 应用Key,从各快递公司开放平台获取 #### app_secret - **类型**: string - **必填**: 是 - **说明**: 应用密钥,从各快递公司开放平台获取 #### partner_id - **类型**: string - **必填**: 否(仅菜鸟网络必需) - **说明**: 合作伙伴ID,从菜鸟开放平台获取 #### sandbox - **类型**: boolean - **默认值**: false - **说明**: 是否使用沙箱环境 - `true`: 使用沙箱环境,用于开发测试 - `false`: 使用生产环境,用于正式业务 #### timeout - **类型**: integer - **默认值**: 30 - **说明**: HTTP请求超时时间(秒) #### version - **类型**: string - **默认值**: 'v1' - **说明**: API版本号 ### 认证机制 EMS API使用OAuth 2.0客户端凭证模式进行认证: 1. 使用`app_key`和`app_secret`获取访问令牌 2. 在后续API请求中使用该令牌进行认证 ```php // 认证过程由SDK自动处理 $client = new \Kode\ExpressApi\EMS\Client($config); // 首次API调用时会自动获取并缓存访问令牌 $result = $client->queryOrder('123456'); ``` ### 网络要求 #### 生产环境端点 - **EMS**: `https://api.ems.com.cn` (端口: 443) - **顺丰SF**: `https://sfapi.sf-express.com` (端口: 443) - **韵达**: `https://api-yunda.kdniao.com` (端口: 443) - **中通**: `https://api-zto.kdniao.com` (端口: 443) - **申通**: `https://api-sto.kdniao.com` (端口: 443) - **菜鸟网络**: `https://api-cainiao.openapi.alibaba.com` (端口: 443) #### 沙箱环境端点 - **EMS**: `https://api-sandbox.ems.com.cn` (端口: 443) - **顺丰SF**: `https://sfapi-sandbox.sf-express.com` (端口: 443) - **韵达**: `https://api-sandbox-yunda.kdniao.com` (端口: 443) - **中通**: `https://api-sandbox-zto.kdniao.com` (端口: 443) - **申通**: `https://api-sandbox-sto.kdniao.com` (端口: 443) - **菜鸟网络**: `https://api-sandbox-cainiao.openapi.alibaba.com` (端口: 443) 确保您的服务器能够访问这些地址。 ## 快速开始 ### 初始化API客户端 ```php use Kode\ExpressApi\ExpressApiClient; // 创建EMS配置数组 $emsConfig = [ 'app_key' => 'YOUR_EMS_APP_KEY', 'app_secret' => 'YOUR_EMS_APP_SECRET', 'sandbox' => true, // 使用沙箱环境 ]; // 创建顺丰配置数组 $sfConfig = [ 'app_key' => 'YOUR_SF_APP_KEY', 'app_secret' => 'YOUR_SF_APP_SECRET', 'sandbox' => true, ]; // 创建韵达配置数组 $yundaConfig = [ 'app_key' => 'YOUR_YUNDA_APP_KEY', 'app_secret' => 'YOUR_YUNDA_APP_SECRET', 'sandbox' => true, ]; // 创建中通配置数组 $ztoConfig = [ 'app_key' => 'YOUR_ZTO_APP_KEY', 'app_secret' => 'YOUR_ZTO_APP_SECRET', 'sandbox' => true, ]; // 创建申通配置数组 $stoConfig = [ 'app_key' => 'YOUR_STO_APP_KEY', 'app_secret' => 'YOUR_STO_APP_SECRET', 'sandbox' => true, ]; // 创建菜鸟网络配置数组 $cainiaoConfig = [ 'app_key' => 'YOUR_CAINIAO_APP_KEY', 'app_secret' => 'YOUR_CAINIAO_APP_SECRET', 'partner_id' => 'YOUR_CAINIAO_PARTNER_ID', 'sandbox' => true, ]; // 方式1:使用工厂方法创建客户端 $emsClient = ExpressApiClient::create('ems', $emsConfig); $sfClient = ExpressApiClient::create('sf', $sfConfig); $yundaClient = ExpressApiClient::create('yunda', $yundaConfig); $ztoClient = ExpressApiClient::create('zto', $ztoConfig); $stoClient = ExpressApiClient::create('sto', $stoConfig); $cainiaoClient = ExpressApiClient::create('cainiao', $cainiaoConfig); // 方式2:直接创建客户端 $emsClient = new \Kode\ExpressApi\EMS\Client($emsConfig); $sfClient = new \Kode\ExpressApi\SF\Client($sfConfig); $yundaClient = new \Kode\ExpressApi\Yunda\Client($yundaConfig); $ztoClient = new \Kode\ExpressApi\Zto\Client($ztoConfig); $stoClient = new \Kode\ExpressApi\Sto\Client($stoConfig); $cainiaoClient = new \Kode\ExpressApi\Cainiao\Client($cainiaoConfig); // 检查快递公司是否支持 if (ExpressApiClient::isCourierSupported('ems')) { echo "EMS快递支持"; } // 获取支持的快递公司列表 $supportedCouriers = ExpressApiClient::getSupportedCouriers(); print_r($supportedCouriers); ``` ## API方法 ### 1. 发货通知 ```php // 发货数据 $shipmentData = [ 'order_no' => 'ORD' . date('YmdHis'), 'sender' => [ 'name' => '张三', 'phone' => '13800138000', 'province' => '广东省', 'city' => '深圳市', 'district' => '南山区', 'address' => '科技园南区8栋', ], 'recipient' => [ 'name' => '李四', 'phone' => '13900139000', 'province' => '北京市', 'city' => '北京市', 'district' => '朝阳区', 'address' => '建国路88号', ], 'items' => [ [ 'name' => '商品1', 'quantity' => 1, 'weight' => 0.5, ], ], 'weight' => 0.5, 'express_type' => 1, ]; // EMS发货通知 $response = $emsClient->sendShipment($shipmentData); // 顺丰发货通知 $response = $sfClient->sendShipment($shipmentData); // 韵达发货通知 $response = $yundaClient->sendShipment($shipmentData); // 中通发货通知 $response = $ztoClient->sendShipment($shipmentData); // 申通发货通知 $response = $stoClient->sendShipment($shipmentData); // 菜鸟网络发货通知 $response = $cainiaoClient->sendShipment($shipmentData); ``` ### 2. 批量发货 ```php // 批量发货数据 $batchShipmentData = [ [ 'order_no' => 'ORD001', // 其他发货数据... ], [ 'order_no' => 'ORD002', // 其他发货数据... ], ]; // EMS批量发货 $response = $emsClient->batchSendShipment($batchShipmentData); // 顺丰批量发货 $response = $sfClient->batchSendShipment($batchShipmentData); // 韵达批量发货 $response = $yundaClient->batchSendShipment($batchShipmentData); // 中通批量发货 $response = $ztoClient->batchSendShipment($batchShipmentData); // 申通批量发货 $response = $stoClient->batchSendShipment($batchShipmentData); // 菜鸟网络批量发货 $response = $cainiaoClient->batchSendShipment($batchShipmentData); ``` ### 3. 取件通知 ```php // 取件数据 $pickupData = [ 'order_no' => 'ORD001', 'pickup_time' => date('Y-m-d H:i:s', strtotime('+1 hour')), 'sender' => [ 'name' => '张三', 'phone' => '13800138000', 'address' => '广东省深圳市南山区科技园', ], ]; // EMS取件通知 $response = $emsClient->pickupNotice($pickupData); // 顺丰取件通知 $response = $sfClient->pickupNotice($pickupData); // 韵达取件通知 $response = $yundaClient->pickupNotice($pickupData); // 中通取件通知 $response = $ztoClient->pickupNotice($pickupData); // 申通取件通知 $response = $stoClient->pickupNotice($pickupData); // 菜鸟网络取件通知 $response = $cainiaoClient->pickupNotice($pickupData); ``` ### 4. 订单查询 ```php // 查询订单 $orderData = [ 'order_no' => 'ORD001', ]; // EMS订单查询 $response = $emsClient->queryOrder($orderData); // 顺丰订单查询 $response = $sfClient->queryOrder($orderData); // 韵达订单查询 $response = $yundaClient->queryOrder($orderData); // 中通订单查询 $response = $ztoClient->queryOrder($orderData); // 申通订单查询 $response = $stoClient->queryOrder($orderData); // 菜鸟网络订单查询 $response = $cainiaoClient->queryOrder($orderData); ``` ### 5. 批量查询订单 ```php // 批量查询订单 $batchOrderData = [ ['order_no' => 'ORD001'], ['order_no' => 'ORD002'], ]; // EMS批量查询订单 $response = $emsClient->batchQueryOrders($batchOrderData); // 顺丰批量查询订单 $response = $sfClient->batchQueryOrders($batchOrderData); // 韵达批量查询订单 $response = $yundaClient->batchQueryOrders($batchOrderData); // 中通批量查询订单 $response = $ztoClient->batchQueryOrders($batchOrderData); // 申通批量查询订单 $response = $stoClient->batchQueryOrders($batchOrderData); // 菜鸟网络批量查询订单 $response = $cainiaoClient->batchQueryOrders($batchOrderData); ``` ### 6. 取消订单 ```php // 取消订单 $cancelData = [ 'order_no' => 'ORD001', 'cancel_reason' => '客户取消', ]; // EMS取消订单 $response = $emsClient->cancelOrder($cancelData); // 顺丰取消订单 $response = $sfClient->cancelOrder($cancelData); // 韵达取消订单 $response = $yundaClient->cancelOrder($cancelData); // 中通取消订单 $response = $ztoClient->cancelOrder($cancelData); // 申通取消订单 $response = $stoClient->cancelOrder($cancelData); // 菜鸟网络取消订单 $response = $cainiaoClient->cancelOrder($cancelData); ``` ### 7. 轨迹查询 ```php // 查询轨迹 $trackingData = [ 'tracking_no' => 'SF1234567890', ]; // EMS轨迹查询 $response = $emsClient->queryTracking($trackingData); // 顺丰轨迹查询 $response = $sfClient->queryTracking($trackingData); // 韵达轨迹查询 $response = $yundaClient->queryTracking($trackingData); // 中通轨迹查询 $response = $ztoClient->queryTracking($trackingData); // 申通轨迹查询 $response = $stoClient->queryTracking($trackingData); // 菜鸟网络轨迹查询 $response = $cainiaoClient->queryTracking($trackingData); ``` ### 8. 批量轨迹查询 ```php // 批量查询轨迹 $batchTrackingData = [ ['tracking_no' => 'SF1234567890'], ['tracking_no' => 'SF0987654321'], ]; // EMS批量轨迹查询 $response = $emsClient->batchQueryTracking($batchTrackingData); // 顺丰批量轨迹查询 $response = $sfClient->batchQueryTracking($batchTrackingData); // 韵达批量轨迹查询 $response = $yundaClient->batchQueryTracking($batchTrackingData); // 中通批量轨迹查询 $response = $ztoClient->batchQueryTracking($batchTrackingData); // 申通批量轨迹查询 $response = $stoClient->batchQueryTracking($batchTrackingData); // 菜鸟网络批量轨迹查询 $response = $cainiaoClient->batchQueryTracking($batchTrackingData); ``` ### 9. 拦截件 ```php // 拦截件 $interceptData = [ 'tracking_no' => 'SF1234567890', 'reason' => '发错地址', ]; // EMS拦截件 $response = $emsClient->intercept($interceptData); // 顺丰拦截件 $response = $sfClient->intercept($interceptData); // 韵达拦截件 $response = $yundaClient->intercept($interceptData); // 中通拦截件 $response = $ztoClient->intercept($interceptData); // 申通拦截件 $response = $stoClient->intercept($interceptData); // 菜鸟网络拦截件 $response = $cainiaoClient->intercept($interceptData); ``` ### 10. 改件信息 ```php // 修改订单信息 $modifyData = [ 'tracking_no' => 'SF1234567890', 'new_address' => [ 'name' => '王五', 'phone' => '13700137000', 'province' => '上海市', 'city' => '上海市', 'district' => '浦东新区', 'address' => '陆家嘴金融中心', ], ]; // EMS改件信息 $response = $emsClient->modify($modifyData); // 顺丰改件信息 $response = $sfClient->modify($modifyData); // 韵达改件信息 $response = $yundaClient->modify($modifyData); // 中通改件信息 $response = $ztoClient->modify($modifyData); // 申通改件信息 $response = $stoClient->modify($modifyData); // 菜鸟网络改件信息 $response = $cainiaoClient->modify($modifyData); ``` ### 11. 面单打印 ```php // 面单打印 $printData = [ 'order_no' => 'ORD001', 'template_config' => [ // 面单配置... ], ]; // EMS面单打印 $response = $emsClient->printLabel($printData); // 顺丰面单打印 $response = $sfClient->printLabel($printData); // 韵达面单打印 $response = $yundaClient->printLabel($printData); // 中通面单打印 $response = $ztoClient->printLabel($printData); // 申通面单打印 $response = $stoClient->printLabel($printData); // 菜鸟网络面单打印 $response = $cainiaoClient->printLabel($printData); ``` ### 12. 批量面单打印 ```php // 批量面单打印 $batchPrintData = [ [ 'order_no' => 'ORD001', 'template_config' => [ // 面单配置... ], ], ]; // EMS批量面单打印 $response = $emsClient->batchPrintLabels($batchPrintData); // 顺丰批量面单打印 $response = $sfClient->batchPrintLabels($batchPrintData); // 韵达批量面单打印 $response = $yundaClient->batchPrintLabels($batchPrintData); // 中通批量面单打印 $response = $ztoClient->batchPrintLabels($batchPrintData); // 申通批量面单打印 $response = $stoClient->batchPrintLabels($batchPrintData); // 菜鸟网络批量面单打印 $response = $cainiaoClient->batchPrintLabels($batchPrintData); ``` ## 面单布局功能设计 ### 功能概述 面单布局功能用于生成和管理快递面单的打印布局配置,支持不同快递公司的面单模板。 ### 面单模板结构 ```php $template = [ 'id' => 'ems_standard_100x150', 'name' => 'EMS标准面单(100mm×150mm)', 'size' => [ 'width' => 100, // 宽度(mm) 'height' => 150, // 高度(mm) ], 'fields' => [ 'sender_name' => [ 'x' => 10, // X坐标(mm) 'y' => 20, // Y坐标(mm) 'width' => 50, // 宽度(mm) 'height' => 5, // 高度(mm) 'font_size' => 12, 'align' => 'left', ], 'receiver_name' => [ 'x' => 10, 'y' => 40, 'width' => 50, 'height' => 5, 'font_size' => 12, 'align' => 'left', ], // 更多字段... ], ]; ``` ### 布局管理器 ```php use Kode\ExpressApi\Label\LayoutManager; $layoutManager = new LayoutManager(); // 创建面单模板 $template = $layoutManager->createTemplate([ 'name' => 'EMS标准面单', 'size' => ['width' => 100, 'height' => 150], ]); // 添加字段 $template->addField('sender_name', [ 'x' => 10, 'y' => 20, 'width' => 50, 'height' => 5, ]); // 保存模板 $layoutManager->saveTemplate($template); ``` ### 获取和使用字段值 ```php // 读取模板 $template = $layoutManager->getTemplate('ems_standard'); // 获取所有字段 $fields = $template->getFields(); // 为字段设置实际值 if (isset($fields['sender_name'])) { $fields['sender_name']->setValue('张三'); } if (isset($fields['receiver_name'])) { $fields['receiver_name']->setValue('李四'); } // 获取字段值 $senderName = $fields['sender_name']->getValue(); $receiverName = $fields['receiver_name']->getValue(); // 转换为数组格式(用于API传输或存储) $fieldArray = $fields['sender_name']->toArray(); ``` ### 核心类设计 #### LayoutManager 负责面单布局的管理,包括创建、读取、更新、删除模板。 #### Template 表示一个面单模板,包含尺寸信息和字段定义。 ### 使用面单可视化编辑器 1. 启动PHP内置服务器: ```bash php -S localhost:8000 -t src/Label/Visualizer/ ``` 2. 访问 http://localhost:8000/ 进入面单可视化编辑器 3. 使用编辑器功能: - 拖拽调整元素位置 - 调整元素大小 - 修改元素属性(字体、颜色、边框等) - 添加文本、条形码、二维码元素 - 选择面单规格 - 预览和导出配置 ### 面单模板配置示例 ```php $templateConfig = [ 'size' => 'ems_default', 'dimensions' => [ 'width' => 100, 'height' => 140, ], 'fields' => [ [ 'id' => 'tracking_number', 'label' => '物流单号', 'type' => 'text', 'x' => 10, 'y' => 10, 'width' => 80, 'height' => 15, 'fontSize' => 10, 'fontFamily' => 'Arial', 'fontWeight' => 'bold', 'align' => 'center', 'showLabel' => true, 'labelPosition' => 'top', ], [ 'id' => 'tracking_barcode', 'label' => '条形码', 'type' => 'barcode', 'x' => 5, 'y' => 30, 'width' => 90, 'height' => 30, 'barcodeType' => 'code128', 'showLabel' => false, ], // 更多字段配置... ], ]; ``` ## 多语言支持 AdvancedLayoutManager 支持创建和管理多语言字段,使得面单模板可以在不同语言环境下使用。 ### MultilingualField 类 `MultilingualField` 类继承自 `Field` 类,提供了多语言标签的支持。 #### 构造函数 ```php new MultilingualField(string $id, array $config = []) ``` #### 配置参数 - `labels`: 关联数组,键为语言代码,值为对应语言的标签 - 所有其他 `Field` 类的配置参数同样适用 #### 方法 - `addLabel(string $language, string $label): self` - 添加语言标签 - `getLabelByLanguage(string $language): ?string` - 根据语言获取标签 - `getLabels(): array` - 获取所有语言标签 - `setLanguage(string $language): self` - 设置当前语言 - `getLanguage(): string` - 获取当前语言 - `setLabel(string $label): self` - 设置默认标签 - `getLabel(): string` - 获取当前语言的标签 #### 使用示例 ```php use Kode\ExpressApi\Label\MultilingualField; // 创建多语言字段 $field = new MultilingualField('product_name', [ 'labels' => [ 'zh' => '产品名称', 'en' => 'Product Name', 'ja' => '商品名', 'ko' => '제품명' ], 'x' => 10, 'y' => 30, 'width' => 50, 'height' => 5, 'font_size' => 10 ]); // 设置当前语言 $field->setLanguage('en'); // 显示英文标签 echo $field->getLabel(); // 输出: Product Name $field->setLanguage('ja'); // 显示日文标签 echo $field->getLabel(); // 输出: 商品名 // 获取特定语言的标签 echo $field->getLabelByLanguage('ko'); // 输出: 제품명 // 添加新的语言标签 $field->addLabel('fr', 'Nom du produit'); echo $field->getLabelByLanguage('fr'); // 输出: Nom du produit ``` ### 在模板中使用多语言字段 在创建模板时,可以通过在字段配置中添加 `labels` 参数来创建多语言字段: ```php use Kode\ExpressApi\Label\AdvancedLayoutManager; $layoutManager = new AdvancedLayoutManager('/path/to/templates'); $config = [ 'id' => 'multilingual_template', 'name' => '多语言模板', 'courier' => 'ems', 'size' => ['width' => 100, 'height' => 150], 'fields' => [ 'sender' => [ 'label' => '发件人', 'x' => 5, 'y' => 5, 'width' => 40, 'height' => 10 ], 'product_name' => [ 'labels' => [ 'zh' => '产品名称', 'en' => 'Product Name', 'ja' => '商品名' ], 'x' => 5, 'y' => 20, 'width' => 50, 'height' => 5, 'font_size' => 8 ] ] ]; ``` ## 错误处理 所有API调用返回的响应都遵循统一的格式: ```php // 成功响应 [ 'success' => true, 'data' => [...], // API返回的数据 'message' => 'success', 'code' => 0, ] // 失败响应 [ 'success' => false, 'data' => null, 'message' => '错误信息', 'code' => 错误代码, ] ``` ## 开发指南 ### 集成新的快递公司 1. 创建新的配置类(继承AbstractConfig) 2. 创建认证类(实现AuthInterface) 3. 创建客户端类(实现ClientInterface) 4. 更新ExpressApiClient.php,添加新的快递公司支持 5. 在ResponseHandler.php中添加新的响应处理逻辑 ### 代码规范 本项目遵循PSR-12代码规范。 #### 代码检查 使用PHP_CodeSniffer检查代码规范: ```bash composer cs-check ``` #### 自动修复 自动修复代码规范问题: ```bash composer cs-fix ``` ### 运行测试 运行所有测试用例: ```bash composer test ``` ### 测试覆盖率 生成测试覆盖率报告: ```bash composer test -- --coverage-html coverage-report ``` ### 测试要求 1. **测试覆盖率**: 所有代码都必须有相应的测试用例,测试覆盖率应达到90%以上 2. **测试类型**: 包含单元测试和集成测试 3. **测试环境**: 测试应在沙箱环境中进行,避免影响生产数据 ### Git工作流 #### 分支策略 - `main`: 主分支,包含稳定版本 - `develop`: 开发分支,包含最新功能 - `feature/*`: 功能分支,用于开发新功能 - `hotfix/*`: 热修复分支,用于紧急修复 #### 提交信息规范 提交信息应遵循以下格式: ``` ():