baseUrl = self::PROD_BASE_URL; $this->appKey = 'dadaf2279d901a5ba40'; $this->appSecret = '828a03677a1c00a3a5b3c59209c4433d'; $this->sourceId = '499021274'; } else { // 测试环境配置(需要替换为实际的测试环境凭证) $this->baseUrl = self::TEST_BASE_URL; $this->appKey = 'dadaf2279d901a5ba40'; $this->appSecret = '828a03677a1c00a3a5b3c59209c4433d'; $this->sourceId = '499021274'; } $this->isSandbox = !$isProduction; } /** * 获取已开通城市列表 * * 通过此接口可获取到达达配送业务所有已开通的城市列表信息 * * @return array 包含城市列表的响应数据 * 示例结构: * [ * 'status' => 'success', * 'code' => 0, * 'msg' => '成功', * 'result' => [ * 'cityList' => [ * [ * 'cityCode' => '110101', * 'cityName' => '东城区', * 'provinceName' => '北京市', * ], * // ... 更多城市 * ] * ] * ] */ public function cityList() { // 业务参数为空时使用空字符串 $body = ''; // 构建完整的 API 请求参数 $payload = $this->buildRequestPayload('cityList', $body); // 发送 POST 请求 $resp = HttpClient::post("{$this->baseUrl}/cityList", $payload); return $resp; } /** * 创建订单(指派订单给配送员) * * 根据达达官方文档实现下单功能 * * @param array $data 订单数据,结构参考: * [ * 'order_id' => '订单编号' (必需), // 商户系统内部订单编号 * 'shop_no' => '门店编号' (必需), // 门店编号 * 'transporter_id' => 配送员ID (必需), // 指定配送员ID(可选,为空则由系统自动分配) * 'receiver_name' => '收货人名称' (可选), * 'receiver_address' => '收货地址' (可选), * 'receiver_phone' => '收货人电话' (可选), * 'receiver_lng' => 经度 (可选), * 'receiver_lat' => 纬度 (可选), * 'cargo_price' => 货物价格(分) (可选), * 'tips' => 小费(分) (可选), * 'distance' => 距离(米) (可选), * 'prepare_time' => 商品准备时间 (可选), // 单位:秒 * 'remark' => '备注' (可选), * 'callback' => '回调地址' (可选), * ] * @return array API 响应结果 */ public function createOrder($data) { // 验证必需字段 if (empty($data['order_id']) || empty($data['shop_no'])) { return [ 'code' => -1, 'msg' => '缺少必需参数: order_id 和 shop_no' ]; } // 构建业务参数(按达达 API 规范) $apiData = [ 'order_id' => $data['order_id'], 'shop_no' => $data['shop_no'], ]; // 如果指定了配送员ID,添加到请求 if (isset($data['transporter_id'])) { $apiData['transporter_id'] = (int)$data['transporter_id']; } // 可选参数 if (!empty($data['receiver_name'])) { $apiData['receiver_name'] = $data['receiver_name']; } if (!empty($data['receiver_address'])) { $apiData['receiver_address'] = $data['receiver_address']; } if (!empty($data['receiver_phone'])) { $apiData['receiver_phone'] = $data['receiver_phone']; } if (isset($data['receiver_lng']) && $data['receiver_lng'] !== '') { $apiData['receiver_lng'] = (double)$data['receiver_lng']; } if (isset($data['receiver_lat']) && $data['receiver_lat'] !== '') { $apiData['receiver_lat'] = (double)$data['receiver_lat']; } if (isset($data['cargo_price'])) { $apiData['cargo_price'] = (int)$data['cargo_price']; } if (isset($data['tips'])) { $apiData['tips'] = (int)$data['tips']; } if (isset($data['distance'])) { $apiData['distance'] = (int)$data['distance']; } if (isset($data['prepare_time'])) { $apiData['prepare_time'] = (int)$data['prepare_time']; } if (!empty($data['remark'])) { $apiData['remark'] = $data['remark']; } if (!empty($data['callback'])) { $apiData['callback'] = $data['callback']; } // 将业务参数转换为 JSON 字符串 $body = json_encode($apiData, JSON_UNESCAPED_UNICODE); // 构建完整的 API 请求参数 $payload = $this->buildRequestPayload('orderAddRequest', $body); // 发送 POST 请求 $resp = HttpClient::post("{$this->baseUrl}/orderAddRequest", $payload); return $resp; } /** * 获取订单运费报价 * * 根据达达官方文档实现订单运费估价功能 * * @param array $data 运费估价参数,结构参考: * [ * 'shop_no' => '门店编号' (必需), * 'origin_id' => '原始订单ID' (必需), // 商户侧订单编号 * 'receiver_address' => '收货地址' (必需), * 'receiver_phone' => '收货人电话' (必需), * 'receiver_lng' => 经度 (必需), * 'receiver_lat' => 纬度 (必需), * 'cargo_price' => 货物价格(分) (可选), // 商品价格,单位为分 * 'distance' => 距离(米) (可选), * 'prepare_time' => 商品准备时间 (可选), // 单位:秒 * ] * @return array|null 运费报价结果 */ public function getPrice($data) { // 验证必需字段 if (empty($data['shop_no']) || empty($data['origin_id']) || empty($data['receiver_address']) || empty($data['receiver_phone']) || !isset($data['receiver_lng']) || !isset($data['receiver_lat'])) { return [ 'code' => -1, 'msg' => '缺少必需参数' ]; } // 构建业务参数(按达达 API 规范) $apiData = [ 'shop_no' => $data['shop_no'], 'origin_id' => $data['origin_id'], 'receiver_address' => $data['receiver_address'], 'receiver_phone' => $data['receiver_phone'], 'receiver_lng' => (double)$data['receiver_lng'], 'receiver_lat' => (double)$data['receiver_lat'], ]; // 可选参数 if (isset($data['cargo_price'])) { $apiData['cargo_price'] = (int)$data['cargo_price']; } if (isset($data['distance'])) { $apiData['distance'] = (int)$data['distance']; } if (isset($data['prepare_time'])) { $apiData['prepare_time'] = (int)$data['prepare_time']; } // 将业务参数转换为 JSON 字符串 $body = json_encode($apiData, JSON_UNESCAPED_UNICODE); // 构建完整的 API 请求参数 $payload = $this->buildRequestPayload('queryDeliverFee', $body); // 发送 POST 请求 $resp = HttpClient::post("{$this->baseUrl}/queryDeliverFee", $payload); // 处理响应 if (isset($resp['status']) && $resp['status'] === 'success' && $resp['code'] == 0) { $result = $resp['result'] ?? []; return [ 'code' => 0, 'platform' => 'dada', 'deliver_fee' => $result['deliver_fee'] ?? 0, // 配送费(分) 'distance' => $result['distance'] ?? 0, // 距离(米) 'duration' => $result['duration'] ?? 0, // 配送时间(分钟) 'tips' => $result['tips'] ?? 0, // 小费(分) ]; } return [ 'code' => $resp['code'] ?? -1, 'msg' => $resp['msg'] ?? 'Unknown error', 'data' => null ]; } /** * 构建达达 API 请求参数 * * 根据达达 API 规范构建完整的请求参数,包括签名 * 参数说明详见接口协议文档 * * @param string $apiMethod 接口方法名(不包括基础 URL) * @param string $body 业务参数 JSON 字符串 * @return array 完整的 POST 请求参数 */ protected function buildRequestPayload($apiMethod, $body = '') { // 获取当前时间戳(单位:秒) $timestamp = (string)time(); // 构建待签名的参数(不包括 signature) $params = [ 'app_key' => $this->appKey, 'v' => self::API_VERSION, 'format' => self::FORMAT, 'source_id' => $this->sourceId, 'timestamp' => $timestamp, 'body' => $body, ]; // 计算签名(按达达 API 规范) $signature = $this->generateSignature($params); // 加入签名到请求参数 $params['signature'] = $signature; return $params; } /** * 生成达达 API 签名 * * 达达签名算法: * 1. 将所有参数(除 signature 外)按 key 升序排列 * 2. 拼接为 key1value1key2value2...keyNvalueN 的形式 * 3. 在字符串后面加上 app_secret * 4. 对结果进行 MD5 加密,得到 32 位大写字符串 * * @param array $params 待签名参数(不包括 signature) * @return string 签名值(32 位大写) */ protected function generateSignature($params) { // 按 key 升序排列参数 ksort($params); // 拼接参数字符串 $str = ''; foreach ($params as $key => $value) { // 跳过空值 if ($value === '' || $value === null) { continue; } $str .= $key . $value; } // 加上 app_secret $str .= $this->appSecret; // MD5 加密,返回 32 位大写字符串 return strtoupper(md5($str)); } /** * 生成随机数字符串(备用方法,如需要使用) * * @param int $length 长度 * @return string 随机字符串 */ protected function generateNonceStr($length = 16) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $str = ''; for ($i = 0; $i < $length; $i++) { $str .= $chars[random_int(0, strlen($chars) - 1)]; } return $str; } }