|
|
@@ -11,6 +11,22 @@ use phpseclib\Crypt\RSA;
|
|
|
|
|
|
class IntraCityExpress
|
|
|
{
|
|
|
+ /**
|
|
|
+ * 订单状态常量
|
|
|
+ */
|
|
|
+ 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_FLOWER = 14; // 鲜花
|
|
|
+
|
|
|
/**
|
|
|
* 获取 access_token
|
|
|
* @param $merchant
|
|
|
@@ -46,11 +62,11 @@ class IntraCityExpress
|
|
|
// 获取私钥
|
|
|
$env = getenv('YII_ENV') == 'production' ? 'production' : 'dev';
|
|
|
$privateKeyPath = __DIR__ . '/wxapi/' . $env . '/rsa-private-key.txt';
|
|
|
-
|
|
|
+
|
|
|
if (!file_exists($privateKeyPath)) {
|
|
|
throw new \Exception("私钥文件不存在: {$privateKeyPath}");
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$privateKey = file_get_contents($privateKeyPath);
|
|
|
if (!$privateKey) {
|
|
|
throw new \Exception('无法获取私钥文件');
|
|
|
@@ -69,9 +85,8 @@ class IntraCityExpress
|
|
|
// $base64Signature = base64_encode($signature);
|
|
|
// \Yii::info("生成的签名: " . $base64Signature, 'intracity_signature');
|
|
|
// return $base64Signature;
|
|
|
-
|
|
|
|
|
|
-
|
|
|
+
|
|
|
// 方案二:使用 RSA SHA256签名
|
|
|
try {
|
|
|
// 使用RSA SHA256签名
|
|
|
@@ -80,17 +95,17 @@ class IntraCityExpress
|
|
|
$rsa->setHash("sha256");
|
|
|
$rsa->setMGFHash("sha256");
|
|
|
$signature = $rsa->sign($payload);
|
|
|
-
|
|
|
+
|
|
|
// $rsa = new RSA();
|
|
|
// $rsa->loadKey($privateKey);
|
|
|
// $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); // 明确使用PKCS#1 v1.5
|
|
|
// $rsa->setHash("sha256");
|
|
|
// 不要设置MGF,PKCS#1 v1.5不使用MGF
|
|
|
// $signature = $rsa->sign($payload);
|
|
|
-
|
|
|
+
|
|
|
$base64Signature = base64_encode($signature);
|
|
|
\Yii::info("phpseclib生成的签名: " . $base64Signature, 'intracity_signature');
|
|
|
-
|
|
|
+
|
|
|
return $base64Signature;
|
|
|
} catch (\Exception $e) {
|
|
|
\Yii::error("所有签名方法都失败 - OpenSSL: " . $e->getMessage() . " phpseclib: " . $e->getMessage(), 'intracity_signature');
|
|
|
@@ -164,7 +179,6 @@ class IntraCityExpress
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
// ==================== API URL常量 ====================
|
|
|
const API_BASE_URL = 'https://api.weixin.qq.com/cgi-bin/express/intracity';
|
|
|
|
|
|
@@ -195,6 +209,7 @@ class IntraCityExpress
|
|
|
|
|
|
|
|
|
// ==================== 门店管理接口 ====================
|
|
|
+
|
|
|
/**
|
|
|
* 创建门店
|
|
|
* @param array $storeData 门店信息
|
|
|
@@ -215,7 +230,7 @@ class IntraCityExpress
|
|
|
'order_pattern' => 1,
|
|
|
"address_info" => $storeData['address_info']
|
|
|
];
|
|
|
-
|
|
|
+
|
|
|
return self::sendRequest(self::API_CREATE_STORE, $data, $accessToken);
|
|
|
}
|
|
|
|
|
|
@@ -231,10 +246,10 @@ class IntraCityExpress
|
|
|
if ($merchant === null) {
|
|
|
$merchant = self::getMerchant();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
$data = $storeData;
|
|
|
-
|
|
|
+
|
|
|
return self::sendRequest(self::API_UPDATE_STORE, $data, $accessToken);
|
|
|
}
|
|
|
|
|
|
@@ -250,12 +265,12 @@ class IntraCityExpress
|
|
|
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);
|
|
|
}
|
|
|
|
|
|
@@ -277,6 +292,7 @@ class IntraCityExpress
|
|
|
|
|
|
|
|
|
// ==================== 订单管理接口 ====================
|
|
|
+
|
|
|
/**
|
|
|
* 创建订单
|
|
|
* @param array $orderData 订单信息
|
|
|
@@ -289,7 +305,7 @@ class IntraCityExpress
|
|
|
if ($merchant === null) {
|
|
|
$merchant = self::getMerchant();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
//开发环境只能使用沙箱 ssh
|
|
|
if (getenv('YII_ENV') != 'production') {
|
|
|
@@ -301,7 +317,7 @@ class IntraCityExpress
|
|
|
$orderData['user_lat'] = (double)40.014747;
|
|
|
}
|
|
|
$data = $orderData;
|
|
|
-
|
|
|
+
|
|
|
return self::sendRequest(self::API_ADD_ORDER, $data, $accessToken);
|
|
|
}
|
|
|
|
|
|
@@ -324,9 +340,9 @@ class IntraCityExpress
|
|
|
if ($merchant === null) {
|
|
|
$merchant = self::getMerchant();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
-
|
|
|
+
|
|
|
// 请求体
|
|
|
$data = $orderData;
|
|
|
|
|
|
@@ -348,7 +364,7 @@ class IntraCityExpress
|
|
|
$merchant = self::getMerchant();
|
|
|
}
|
|
|
$accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
-
|
|
|
+
|
|
|
$data = [];
|
|
|
if (!empty($wxOrderId)) {
|
|
|
$data['wx_order_id'] = $wxOrderId;
|
|
|
@@ -374,7 +390,7 @@ class IntraCityExpress
|
|
|
* @param int $ptStyle
|
|
|
* @return array
|
|
|
*/
|
|
|
- public static function queryOrder($wxStoreId, $storeOrderId='', $wxOrderId='', $merchant = null, $ptStyle = 0)
|
|
|
+ public static function queryOrder($wxStoreId, $storeOrderId = '', $wxOrderId = '', $merchant = null, $ptStyle = 0)
|
|
|
{
|
|
|
if ($merchant === null) {
|
|
|
$merchant = self::getMerchant();
|
|
|
@@ -384,19 +400,20 @@ class IntraCityExpress
|
|
|
if (empty($wxOrderId) && (empty($storeOrderId) || empty($wxStoreId))) {
|
|
|
util::fail("参数不足:wx_order_id 或 (store_order_id + wx_store_id) 必须提供一组");
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$data = [];
|
|
|
if ($wxStoreId != '') {
|
|
|
$data['wx_order_id'] = $wxOrderId;
|
|
|
}
|
|
|
$data['store_order_id'] = $storeOrderId;
|
|
|
$data['wx_store_id'] = $wxStoreId;
|
|
|
-
|
|
|
+
|
|
|
return self::sendRequest(self::API_GET_ORDER, $data, $accessToken);
|
|
|
}
|
|
|
|
|
|
|
|
|
// ==================== 资金管理接口 ====================
|
|
|
+
|
|
|
/**
|
|
|
* 查询门店余额
|
|
|
* @param string $wxStoreId
|
|
|
@@ -486,6 +503,7 @@ class IntraCityExpress
|
|
|
}
|
|
|
|
|
|
// ==================== 测试接口 ====================
|
|
|
+
|
|
|
/**
|
|
|
* 模拟回调接口
|
|
|
* @param string $wxOrderId 微信订单号
|
|
|
@@ -501,13 +519,13 @@ class IntraCityExpress
|
|
|
if ($merchant === null) {
|
|
|
$merchant = self::getMerchant();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$accessToken = self::getAccessToken($merchant, $ptStyle);
|
|
|
-
|
|
|
+
|
|
|
$data = [
|
|
|
'order_status' => $orderStatus,
|
|
|
];
|
|
|
-
|
|
|
+
|
|
|
if (!empty($wxOrderId)) {
|
|
|
$data['wx_order_id'] = $wxOrderId;
|
|
|
}
|
|
|
@@ -516,7 +534,7 @@ class IntraCityExpress
|
|
|
$data['wx_store_id'] = $outStoreId;
|
|
|
$data['store_order_id'] = $outOrderId;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return self::sendRequest(self::API_MOCK_NOTIFY, $data, $accessToken);
|
|
|
}
|
|
|
|
|
|
@@ -536,22 +554,23 @@ class IntraCityExpress
|
|
|
$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 回调参数
|
|
|
@@ -563,12 +582,12 @@ class IntraCityExpress
|
|
|
if (!isset($params['sign'])) {
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
$receivedSign = $params['sign'];
|
|
|
unset($params['sign']);
|
|
|
-
|
|
|
+
|
|
|
$calculatedSign = self::generateCallbackSignature($params, $token);
|
|
|
-
|
|
|
+
|
|
|
return $receivedSign === $calculatedSign;
|
|
|
}
|
|
|
|
|
|
@@ -587,16 +606,16 @@ class IntraCityExpress
|
|
|
'return_msg' => '签名验证失败'
|
|
|
];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 处理订单状态变化
|
|
|
$orderStatus = $callbackData['order_status'];
|
|
|
$wxOrderId = $callbackData['wx_order_id'];
|
|
|
$outOrderId = $callbackData['store_order_id'] ?? '';
|
|
|
$outStoreId = $callbackData['wx_store_id'] ?? '';
|
|
|
-
|
|
|
+
|
|
|
// 这里可以添加具体的业务逻辑处理
|
|
|
// 例如:更新数据库中的订单状态、发送通知等
|
|
|
-
|
|
|
+
|
|
|
// 伪代码示例:
|
|
|
/*
|
|
|
switch ($orderStatus) {
|
|
|
@@ -625,28 +644,10 @@ class IntraCityExpress
|
|
|
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_FLOWER = 14; // 鲜花
|
|
|
-
|
|
|
}
|