loadKey($privateKey); $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'); throw new \Exception('签名生成失败: OpenSSL和phpseclib都失败'); } } /** * 发送HTTP请求 * @param string $url 请求URL * @param array $data 请求数据 * @param string $accessToken access_token * @return array */ public static function sendRequest($url, $data, $accessToken) { // ================ 选择哪个 appId ================= // 微信开放平台 appId $open = \biz\wx\classes\WxOpenClass::getByCondition(['style' => dict::getDict('ptStyle', 'hd')]); if (empty($open)) { util::fail('没有找到平台信息'); } $component_app_id = $open['appId']; $appId = $component_app_id; // 当前小程序的 appId //$appId = $merchant['miniAppId'] ?? util::fail('app-id 出错'); // 方式一: 使用WxApi方式处理请求 ============================ try { $wxApi = new \common\components\wxapi\WxApi($appId, $accessToken); $result = $wxApi->request($url, $data); return $result; } catch (\Exception $e) { Yii::error($e->getMessage()); // 如果WxApi方式失败,回退到简单方式 util::fail('error'); } // 添加access_token到URL $urlWithToken = $url . (strpos($url, '?') !== false ? '&' : '?') . 'access_token=' . $accessToken; $timestamp = time(); $body = Json::encode($data); if ($body === '[]') { $body = '{}'; } // 方式二: 自己处理请求 =========== 失败,不使用(可删除) // $signature = self::generateWxApiSignature($url, $appId, $timestamp, $body); // 构建请求头,包含微信API签名所需的头部 // $headers = [ // 'Content-Type: application/json', // 'Accept: application/json', // 'Wechatmp-Appid: ' . $appId, // 'Wechatmp-TimeStamp: ' . $timestamp, // 'Wechatmp-Signature: ' . $signature // ]; // $curl = new curl\Curl(); // $response = $curl->setOption(CURLOPT_POSTFIELDS, $body) // ->setOption(CURLOPT_HTTPHEADER, $headers) // ->post($urlWithToken); // if (is_array($response)) { // return $response; // } // 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_APPLY_STORE = self::API_BASE_URL . '/apply'; // 订单管理 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_BALANCE_QUERY = self::API_BASE_URL . '/balancequery'; const API_STORE_CHARGE = self::API_BASE_URL . '/storecharge'; const API_STORE_REFUND = self::API_BASE_URL . '/storerefund'; const API_CHARGE_RECORD = self::API_BASE_URL . '/queryflow'; // 测试接口 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'], 'order_pattern' => 1, "address_info" => $storeData['address_info'] ]; 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 = $storeData; 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 null $merchant * @param int $ptStyle * @return array */ public static function applyStore($merchant = null, $ptStyle = 0) { if ($merchant === null) { $merchant = self::getMerchant(); } $accessToken = self::getAccessToken($merchant, $ptStyle); return self::sendRequest(self::API_APPLY_STORE, [], $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 = $orderData; 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) { // 创建表单验证模型 $form = new StoreFeeForm(); $form->setScenario('query_fee'); $form->load($orderData, ''); // 直接从 post 数据加载,不使用模型名作为前缀 // 执行表单验证 $form->validateForm(); if ($merchant === null) { $merchant = self::getMerchant(); } $accessToken = self::getAccessToken($merchant, $ptStyle); // 请求体 $data = $orderData; return self::sendRequest(self::API_PRE_ADD_ORDER, $data, $accessToken); } /** * 取消订单 * @param string $wxOrderId 微信订单号 * @param string $storeOrderId 商户订单号 * @param string $wxStoreId 门店ID * @param string $merchant 商户信息 * @param int $ptStyle * @return array */ public static function cancelOrder($wxOrderId = '', $storeOrderId = '', $wxStoreId = '', $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($storeOrderId)) { $data['store_order_id'] = $storeOrderId; } if (!empty($wxStoreId)) { $data['wx_store_id'] = $wxStoreId; } $cancelReasons = []; // 1:不需要了 2:信息填错 3:无人接单 99:其他 $data['cancel_reason_id'] = 1; 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 queryOrder($wxStoreId, $storeOrderId='', $wxOrderId='', $merchant = null, $ptStyle = 0) { if ($merchant === null) { $merchant = self::getMerchant(); } $accessToken = self::getAccessToken($merchant, $ptStyle); 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 * @param null $merchant * @param int $ptStyle * @return array */ public static function getBalance($wxStoreId, $merchant = null, $ptStyle = 0) { if ($merchant === null) { $merchant = self::getMerchant(); } $accessToken = self::getAccessToken($merchant, $ptStyle); $data = ['wx_store_id' => $wxStoreId]; //'out_store_id' => $outStoreId, return self::sendRequest(self::API_BALANCE_QUERY, $data, $accessToken); } /** * 门店充值 * @param string $wxStoreId * @param string $outChargeId * @param int $amount * @param null $merchant * @param int $ptStyle * @return array */ public static function storeCharge($wxStoreId, $chargeId, $amount, $merchant = null, $ptStyle = 0) { if ($merchant === null) { $merchant = self::getMerchant(); } $accessToken = self::getAccessToken($merchant, $ptStyle); $data = [ 'wx_store_id' => $wxStoreId, 'service_trans_id' => $chargeId, 'amount' => $amount, ]; return self::sendRequest(self::API_STORE_CHARGE, $data, $accessToken); } /** * 门店退款 * @param string $wxStoreId * @param string $payMode * @param string $transId * @param null $merchant * @param int $ptStyle * @return array */ public static function storeFund($wxStoreId, $payMode, $transId, $merchant = null, $ptStyle = 0) { if ($merchant === null) { $merchant = self::getMerchant(); } $accessToken = self::getAccessToken($merchant, $ptStyle); $data = [ 'wx_store_id' => $wxStoreId, 'pay_mode' => $payMode, 'service_trans_id' => $transId, ]; return self::sendRequest(self::API_STORE_REFUND, $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_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_FLOWER = 14; // 鲜花 }