|
|
@@ -0,0 +1,669 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace common\components;
|
|
|
+
|
|
|
+use yii\helpers\Json;
|
|
|
+use linslin\yii2\curl;
|
|
|
+use bizHd\wx\classes\WxOpenClass;
|
|
|
+
|
|
|
+class IntraCityExpress
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * 获取 access_token
|
|
|
+ * @param $merchant
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public static function getAccessToken($merchant, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($ptStyle == 0) {
|
|
|
+ $ptStyle = dict::getDict('ptStyle', 'hd');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 直接复用 miniUtil 的获取小程序 access_token 方法
|
|
|
+ return miniUtil::getMiniProgramAccessToken($merchant, $ptStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function getMerchant()
|
|
|
+ {
|
|
|
+ $merchant = WxOpenClass::getMallWxInfo();
|
|
|
+ return $merchant;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送HTTP请求
|
|
|
+ * @param string $url 请求URL
|
|
|
+ * @param array $data 请求数据
|
|
|
+ * @param string $accessToken access_token
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function sendRequest($url, $data, $accessToken)
|
|
|
+ {
|
|
|
+ $curl = new curl\Curl();
|
|
|
+
|
|
|
+ // 添加access_token到URL
|
|
|
+ $urlWithToken = $url . (strpos($url, '?') !== false ? '&' : '?') . 'access_token=' . $accessToken;
|
|
|
+
|
|
|
+ $response = $curl->setOption(CURLOPT_POSTFIELDS, Json::encode($data))
|
|
|
+ ->post($urlWithToken);//->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json'])
|
|
|
+
|
|
|
+ return Json::decode($response, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== API URL常量 ====================
|
|
|
+ const API_BASE_URL = 'https://api.weixin.qq.com/cgi-bin/express/intracity';
|
|
|
+
|
|
|
+ // 门店管理
|
|
|
+ const API_CREATE_STORE = self::API_BASE_URL . '/createstore';
|
|
|
+ const API_UPDATE_STORE = self::API_BASE_URL . '/updatestore';
|
|
|
+ const API_QUERY_STORE = self::API_BASE_URL . '/querystore';
|
|
|
+
|
|
|
+ // 订单管理
|
|
|
+ const API_ADD_ORDER = self::API_BASE_URL . '/addorder';
|
|
|
+ const API_CANCEL_ORDER = self::API_BASE_URL . '/cancelorder';
|
|
|
+ const API_GET_ORDER = self::API_BASE_URL . '/queryorder';
|
|
|
+
|
|
|
+ // 预下单
|
|
|
+ const API_PRE_ADD_ORDER = self::API_BASE_URL . '/preaddorder';
|
|
|
+
|
|
|
+ // 资金管理
|
|
|
+ const API_GET_BALANCE = self::API_BASE_URL . '/getbalance';
|
|
|
+ const API_CHARGE_STORE = self::API_BASE_URL . '/chargestore';
|
|
|
+ const API_REFUND_STORE = self::API_BASE_URL . '/refundstore';
|
|
|
+ const API_GET_CHARGE_RECORD = self::API_BASE_URL . '/getchargerecord';
|
|
|
+
|
|
|
+ // 测试接口
|
|
|
+ const API_MOCK_NOTIFY = self::API_BASE_URL . '/mocknotify';
|
|
|
+
|
|
|
+
|
|
|
+ // ==================== 门店管理接口 ====================
|
|
|
+ /**
|
|
|
+ * 创建门店
|
|
|
+ * @param array $storeData 门店信息
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function createStore($storeData, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $storeData['out_store_id'],
|
|
|
+ 'store_name' => $storeData['store_name'],
|
|
|
+ 'store_address' => $storeData['store_address'],
|
|
|
+ 'store_longitude' => $storeData['store_longitude'],
|
|
|
+ 'store_latitude' => $storeData['store_latitude'],
|
|
|
+ 'store_phone' => $storeData['store_phone'],
|
|
|
+ 'service_type' => $storeData['service_type'] ?? 1, // 默认同城配送
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_CREATE_STORE, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新门店
|
|
|
+ * @param array $storeData 门店信息
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function updateStore($storeData, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $storeData['out_store_id'],
|
|
|
+ 'store_name' => $storeData['store_name'] ?? null,
|
|
|
+ 'store_address' => $storeData['store_address'] ?? null,
|
|
|
+ 'store_longitude' => $storeData['store_longitude'] ?? null,
|
|
|
+ 'store_latitude' => $storeData['store_latitude'] ?? null,
|
|
|
+ 'store_phone' => $storeData['store_phone'] ?? null,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_UPDATE_STORE, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询门店
|
|
|
+ * @param string $outStoreId 门店ID
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getStore($outStoreId, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $outStoreId,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_QUERY_STORE, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // ==================== 订单管理接口 ====================
|
|
|
+ /**
|
|
|
+ * 创建订单
|
|
|
+ * @param array $orderData 订单信息
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function createOrder($orderData, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $orderData['out_store_id'],
|
|
|
+ 'store_order_id' => $orderData['store_order_id'], // 注意:文档是 store_order_id,不是 out_order_id
|
|
|
+ 'delivery_service_code' => $orderData['delivery_service_code'],
|
|
|
+ 'receiver' => [
|
|
|
+ 'name' => $orderData['to_user_name'],
|
|
|
+ 'phone' => $orderData['to_user_phone'],
|
|
|
+ 'address' => $orderData['to_user_address'],
|
|
|
+ 'lng' => $orderData['to_user_longitude'],
|
|
|
+ 'lat' => $orderData['to_user_latitude'],
|
|
|
+ ],
|
|
|
+ 'cargo' => [
|
|
|
+ 'goods_value' => $orderData['goods_value'],
|
|
|
+ 'goods_weight' => $orderData['goods_weight'],
|
|
|
+ 'goods_pickup_info' => $orderData['goods_pickup_info'],
|
|
|
+ 'goods_delivery_info' => $orderData['goods_delivery_info'],
|
|
|
+ 'cargo_first_class' => $orderData['cargo_first_class'] ?? '',
|
|
|
+ 'cargo_second_class' => $orderData['cargo_second_class'] ?? '',
|
|
|
+ 'goods_type' => $orderData['goods_type'] ?? 99,
|
|
|
+ ],
|
|
|
+ 'order' => [
|
|
|
+ 'order_type' => $orderData['order_type'] ?? 0,
|
|
|
+ 'expected_pickup_time' => $orderData['expected_pickup_time'] ?? 0,
|
|
|
+ 'expected_delivery_time' => $orderData['expected_delivery_time'] ?? 0,
|
|
|
+ 'tips' => $orderData['tips'] ?? 0,
|
|
|
+ 'is_insured' => $orderData['is_insured'] ?? 0,
|
|
|
+ 'declared_value' => $orderData['declared_value'] ?? 0,
|
|
|
+ 'cash_on_delivery' => $orderData['cash_on_delivery'] ?? 0,
|
|
|
+ 'cash_on_delivery_value' => $orderData['cash_on_delivery_value'] ?? 0,
|
|
|
+ ],
|
|
|
+ 'sub_biz_id' => $orderData['sub_biz_id'] ?? '',
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_ADD_ORDER, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 预下单(查询运费)
|
|
|
+ * @param array $orderData
|
|
|
+ * @param null $merchant
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function preAddOrder($orderData, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ // 构建与 createOrder 相同的请求体
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $orderData['out_store_id'],
|
|
|
+ 'store_order_id' => $orderData['store_order_id'],
|
|
|
+ 'delivery_service_code' => $orderData['delivery_service_code'],
|
|
|
+ 'receiver' => [
|
|
|
+ 'name' => $orderData['to_user_name'],
|
|
|
+ 'phone' => $orderData['to_user_phone'],
|
|
|
+ 'address' => $orderData['to_user_address'],
|
|
|
+ 'lng' => $orderData['to_user_longitude'],
|
|
|
+ 'lat' => $orderData['to_user_latitude'],
|
|
|
+ ],
|
|
|
+ 'cargo' => [
|
|
|
+ 'goods_value' => $orderData['goods_value'],
|
|
|
+ 'goods_weight' => $orderData['goods_weight'],
|
|
|
+ 'goods_pickup_info' => $orderData['goods_pickup_info'],
|
|
|
+ 'goods_delivery_info' => $orderData['goods_delivery_info'],
|
|
|
+ 'cargo_first_class' => $orderData['cargo_first_class'] ?? '',
|
|
|
+ 'cargo_second_class' => $orderData['cargo_second_class'] ?? '',
|
|
|
+ 'goods_type' => $orderData['goods_type'] ?? 99,
|
|
|
+ ],
|
|
|
+ 'order' => [
|
|
|
+ 'order_type' => $orderData['order_type'] ?? 0,
|
|
|
+ 'expected_pickup_time' => $orderData['expected_pickup_time'] ?? 0,
|
|
|
+ 'expected_delivery_time' => $orderData['expected_delivery_time'] ?? 0,
|
|
|
+ 'tips' => $orderData['tips'] ?? 0,
|
|
|
+ 'is_insured' => $orderData['is_insured'] ?? 0,
|
|
|
+ 'declared_value' => $orderData['declared_value'] ?? 0,
|
|
|
+ 'cash_on_delivery' => $orderData['cash_on_delivery'] ?? 0,
|
|
|
+ 'cash_on_delivery_value' => $orderData['cash_on_delivery_value'] ?? 0,
|
|
|
+ ],
|
|
|
+ 'sub_biz_id' => $orderData['sub_biz_id'] ?? '',
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_PRE_ADD_ORDER, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消订单
|
|
|
+ * @param string $wxOrderId 微信订单号
|
|
|
+ * @param string $outOrderId 商户订单号
|
|
|
+ * @param string $outStoreId 门店ID
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function cancelOrder($wxOrderId = '', $outOrderId = '', $outStoreId = '', $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ $data = [];
|
|
|
+ if (!empty($wxOrderId)) {
|
|
|
+ $data['wx_order_id'] = $wxOrderId;
|
|
|
+ }
|
|
|
+ if (!empty($outOrderId)) {
|
|
|
+ $data['store_order_id'] = $outOrderId;
|
|
|
+ }
|
|
|
+ if (!empty($outStoreId)) {
|
|
|
+ $data['out_store_id'] = $outStoreId;
|
|
|
+ }
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_CANCEL_ORDER, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询订单
|
|
|
+ * @param string $wxOrderId 微信订单号
|
|
|
+ * @param string $outOrderId 商户订单号
|
|
|
+ * @param string $outStoreId 门店ID
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getOrder($wxOrderId = '', $outOrderId = '', $outStoreId = '', $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ $data = [];
|
|
|
+ if (!empty($wxOrderId)) {
|
|
|
+ $data['wx_order_id'] = $wxOrderId;
|
|
|
+ }
|
|
|
+ if (!empty($outOrderId)) {
|
|
|
+ $data['store_order_id'] = $outOrderId;
|
|
|
+ }
|
|
|
+ if (!empty($outStoreId)) {
|
|
|
+ $data['out_store_id'] = $outStoreId;
|
|
|
+ }
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_GET_ORDER, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取订单列表
|
|
|
+ * @param int $offset 偏移量
|
|
|
+ * @param int $limit 限制数量
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getOrderList($offset = 0, $limit = 20, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $url = 'https://api.weixin.qq.com/cgi-bin/express/intracity/order/list';
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'offset' => $offset,
|
|
|
+ 'limit' => $limit,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest($url, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 运力管理接口 ====================
|
|
|
+ /**
|
|
|
+ * 获取运力列表
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getDeliveryList($merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $url = 'https://api.weixin.qq.com/cgi-bin/express/intracity/delivery/list';
|
|
|
+
|
|
|
+ return self::sendRequest($url, [], $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取运力服务范围
|
|
|
+ * @param string $deliveryId 运力ID
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getDeliveryServiceArea($deliveryId, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $url = 'https://api.weixin.qq.com/cgi-bin/express/intracity/delivery/service_area';
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'delivery_id' => $deliveryId,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest($url, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 资金管理接口 ====================
|
|
|
+ /**
|
|
|
+ * 查询门店余额
|
|
|
+ * @param string $outStoreId
|
|
|
+ * @param null $merchant
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getBalance($outStoreId, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $data = ['out_store_id' => $outStoreId];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_GET_BALANCE, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 门店充值
|
|
|
+ * @param string $outStoreId
|
|
|
+ * @param string $outChargeId
|
|
|
+ * @param int $amount
|
|
|
+ * @param null $merchant
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function chargeStore($outStoreId, $outChargeId, $amount, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $outStoreId,
|
|
|
+ 'out_charge_id' => $outChargeId,
|
|
|
+ 'amount' => $amount,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_CHARGE_STORE, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 门店退款
|
|
|
+ * @param string $outStoreId
|
|
|
+ * @param string $outRefundId
|
|
|
+ * @param string $outChargeId
|
|
|
+ * @param null $merchant
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function refundStore($outStoreId, $outRefundId, $outChargeId, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $data = [
|
|
|
+ 'out_store_id' => $outStoreId,
|
|
|
+ 'out_refund_id' => $outRefundId,
|
|
|
+ 'out_charge_id' => $outChargeId,
|
|
|
+ ];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_REFUND_STORE, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询充值记录
|
|
|
+ * @param string $outChargeId
|
|
|
+ * @param null $merchant
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function getChargeRecord($outChargeId, $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+ $data = ['out_charge_id' => $outChargeId];
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_GET_CHARGE_RECORD, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 测试接口 ====================
|
|
|
+ /**
|
|
|
+ * 模拟回调接口
|
|
|
+ * @param string $wxOrderId 微信订单号
|
|
|
+ * @param string $outStoreId 门店ID
|
|
|
+ * @param string $outOrderId 商户订单号
|
|
|
+ * @param int $orderStatus 订单状态
|
|
|
+ * @param string $merchant 商户信息
|
|
|
+ * @param int $ptStyle
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function mockNotify($orderStatus, $wxOrderId = '', $outStoreId = '', $outOrderId = '', $merchant = null, $ptStyle = 0)
|
|
|
+ {
|
|
|
+ if ($merchant === null) {
|
|
|
+ $merchant = self::getMerchant();
|
|
|
+ }
|
|
|
+
|
|
|
+ $accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'order_status' => $orderStatus,
|
|
|
+ ];
|
|
|
+
|
|
|
+ if (!empty($wxOrderId)) {
|
|
|
+ $data['wx_order_id'] = $wxOrderId;
|
|
|
+ }
|
|
|
+ // 注意:文档中模拟回调使用的是 wx_store_id 和 store_order_id
|
|
|
+ if (!empty($outStoreId) && !empty($outOrderId)) {
|
|
|
+ $data['wx_store_id'] = $outStoreId;
|
|
|
+ $data['store_order_id'] = $outOrderId;
|
|
|
+ }
|
|
|
+
|
|
|
+ return self::sendRequest(self::API_MOCK_NOTIFY, $data, $accessToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成回调签名
|
|
|
+ * @param array $params 回调参数
|
|
|
+ * @param string $token 安全token
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public static function generateCallbackSignature($params, $token)
|
|
|
+ {
|
|
|
+ // 1. 筛选出参与签名的字段
|
|
|
+ $signParams = [];
|
|
|
+ $fieldsToSign = ['appid', 'order_status', 'service_trans_id', 'status_change_time', 'store_order_id', 'timestamp', 'wx_order_id', 'wx_store_id'];
|
|
|
+ foreach ($fieldsToSign as $field) {
|
|
|
+ if (isset($params[$field])) {
|
|
|
+ $signParams[$field] = $params[$field];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 按字典序排序参数
|
|
|
+ ksort($signParams);
|
|
|
+
|
|
|
+ // 3. 拼接参数字符串
|
|
|
+ $signStr = '';
|
|
|
+ foreach ($signParams as $key => $value) {
|
|
|
+ $signStr .= $key . '=' . $value . '&';
|
|
|
+ }
|
|
|
+ $signStr .= 'token=' . $token;
|
|
|
+
|
|
|
+ // 4. 计算MD5并转为小写
|
|
|
+ return strtolower(md5($signStr));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 回调验证接口 ====================
|
|
|
+ /**
|
|
|
+ * 验证回调签名
|
|
|
+ * @param array $params 回调参数
|
|
|
+ * @param string $token 安全token
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public static function verifyCallback($params, $token)
|
|
|
+ {
|
|
|
+ if (!isset($params['sign'])) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $receivedSign = $params['sign'];
|
|
|
+ unset($params['sign']);
|
|
|
+
|
|
|
+ $calculatedSign = self::generateCallbackSignature($params, $token);
|
|
|
+
|
|
|
+ return $receivedSign === $calculatedSign;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理订单状态回调
|
|
|
+ * @param array $callbackData 回调数据
|
|
|
+ * @param string $token 安全token
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public static function handleOrderCallback($callbackData, $token)
|
|
|
+ {
|
|
|
+ // 验证签名
|
|
|
+ if (!self::verifyCallback($callbackData, $token)) {
|
|
|
+ return [
|
|
|
+ 'return_code' => 1,
|
|
|
+ 'return_msg' => '签名验证失败'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理订单状态变化
|
|
|
+ $orderStatus = $callbackData['order_status'];
|
|
|
+ $wxOrderId = $callbackData['wx_order_id'];
|
|
|
+ $outOrderId = $callbackData['store_order_id'] ?? '';
|
|
|
+ $outStoreId = $callbackData['wx_store_id'] ?? '';
|
|
|
+
|
|
|
+ // 这里可以添加具体的业务逻辑处理
|
|
|
+ // 例如:更新数据库中的订单状态、发送通知等
|
|
|
+
|
|
|
+ // 伪代码示例:
|
|
|
+ /*
|
|
|
+ switch ($orderStatus) {
|
|
|
+ case 10000: // 订单创建成功
|
|
|
+ // 处理订单创建成功逻辑
|
|
|
+ break;
|
|
|
+ case 30000: // 配送员接单
|
|
|
+ // 处理配送员接单逻辑
|
|
|
+ break;
|
|
|
+ case 40000: // 配送员到店
|
|
|
+ // 处理配送员到店逻辑
|
|
|
+ break;
|
|
|
+ case 50000: // 配送中
|
|
|
+ // 处理配送中逻辑
|
|
|
+ break;
|
|
|
+ case 70000: // 配送完成
|
|
|
+ // 处理配送完成逻辑
|
|
|
+ break;
|
|
|
+ case 20000: // 商家取消订单
|
|
|
+ case 20001: // 配送方取消订单
|
|
|
+ case 60000: // 配送员撤单
|
|
|
+ // 处理订单取消逻辑
|
|
|
+ break;
|
|
|
+ case 90000: // 配送异常
|
|
|
+ // 处理配送异常逻辑
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'return_code' => 0,
|
|
|
+ 'return_msg' => 'OK'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 常量定义 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单状态常量
|
|
|
+ */
|
|
|
+ const ORDER_STATUS_CREATED = 10000; // 订单创建成功
|
|
|
+ const ORDER_STATUS_CANCELED_BY_MERCHANT = 20000; // 商家取消订单
|
|
|
+ const ORDER_STATUS_CANCELED_BY_DELIVERY = 20001; // 配送方取消订单
|
|
|
+ const ORDER_STATUS_ACCEPTED = 30000; // 配送员接单
|
|
|
+ const ORDER_STATUS_ARRIVED = 40000; // 配送员到店
|
|
|
+ const ORDER_STATUS_DELIVERING = 50000; // 配送中
|
|
|
+ const ORDER_STATUS_WITHDRAWN = 60000; // 配送员撤单
|
|
|
+ const ORDER_STATUS_COMPLETED = 70000; // 配送完成
|
|
|
+ const ORDER_STATUS_EXCEPTION = 90000; // 配送异常
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 物品类型常量
|
|
|
+ */
|
|
|
+ const GOODS_TYPE_FAST_FOOD = 1; // 快餐
|
|
|
+ const GOODS_TYPE_MEDICINE = 2; // 药品
|
|
|
+ const GOODS_TYPE_GENERAL = 3; // 百货
|
|
|
+ const GOODS_TYPE_FRESH = 6; // 生鲜
|
|
|
+ const GOODS_TYPE_WINE = 8; // 酒品
|
|
|
+ const GOODS_TYPE_DOCUMENT = 12; // 文件
|
|
|
+ const GOODS_TYPE_CAKE = 13; // 蛋糕
|
|
|
+ const GOODS_TYPE_FLOWER = 14; // 鲜花
|
|
|
+ const GOODS_TYPE_DIGITAL = 15; // 数码
|
|
|
+ const GOODS_TYPE_CLOTHING = 16; // 服装
|
|
|
+ const GOODS_TYPE_AUTO_PARTS = 17; // 汽配
|
|
|
+ const GOODS_TYPE_JEWELRY = 18; // 珠宝
|
|
|
+ const GOODS_TYPE_DRINK = 32; // 饮料
|
|
|
+ const GOODS_TYPE_LICENSE = 36; // 证照
|
|
|
+ const GOODS_TYPE_PET = 55; // 宠物用品
|
|
|
+ const GOODS_TYPE_MATERNITY = 56; // 母婴用品
|
|
|
+ const GOODS_TYPE_COSMETICS = 57; // 美妆用品
|
|
|
+ const GOODS_TYPE_HOME = 58; // 家居建材
|
|
|
+ const GOODS_TYPE_OTHER = 99; // 其他
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 运力常量
|
|
|
+ */
|
|
|
+ const DELIVERY_DADA = 'DADA'; // 达达
|
|
|
+ const DELIVERY_SFTC = 'SFTC'; // 顺丰同城
|
|
|
+}
|