config = $payConfigService->getConfig('WDPay'); $this->key = $this->config['key'] ?? ''; $this->customerNo = $this->config['customerNo'] ?? ''; // WDPay官方接口:/charging/create-pay-in $this->apiUrl = $this->config['apiUrl'] ?? ''; } /** * 签名 - WDPay专用签名算法 * * 规则: * 1. 按参数名ASCII码从小到大排序(字典序) * 2. 空值不参与签名 * 3. sign参数不参与签名 * 4. 拼接格式:key1=value1&key2=value2&key=商户密钥 * 5. MD5后转大写 */ public function sign(array $params): array { // 移除sign和空值 $signParams = []; foreach ($params as $key => $value) { if ($key !== 'sign' && $value !== null && $value !== '') { // 如果是对象或数组,需要递归处理 if (is_array($value) || is_object($value)) { $signParams[$key] = $this->buildObjectString($value); } else { $signParams[$key] = $value; } } } // 按ASCII码排序 ksort($signParams); // 拼接字符串:key1=value1&key2=value2 $stringA = urldecode(http_build_query($signParams)); // 拼接商户密钥:stringA&key=商户密钥 $stringSignTemp = $stringA . '&key=' . $this->key; // MD5并转大写 $sign = strtoupper(md5($stringSignTemp)); // 将签名添加到参数中 $params['sign'] = $sign; Util::WriteLog('WDPay_sign', "待签名字符串: " . $stringSignTemp); Util::WriteLog('WDPay_sign', "签名结果: " . $sign); return $params; } /** * 验签 - WDPay专用验签算法 */ public function verifySign(array $params): bool { if (!isset($params['sign'])) { return false; } $receivedSign = $params['sign']; // 移除sign和空值 $signParams = []; foreach ($params as $key => $value) { if ($key !== 'sign' && $value !== null && $value !== '') { if (is_array($value) || is_object($value)) { $signParams[$key] = $this->buildObjectString($value); } else { $signParams[$key] = $value; } } } // 按ASCII码排序 ksort($signParams); // 拼接字符串 $stringA = urldecode(http_build_query($signParams)); // 拼接商户密钥 $stringSignTemp = $stringA . '&key=' . $this->key; // MD5并转大写 $calculatedSign = strtoupper(md5($stringSignTemp)); Util::WriteLog('WDPay_verify', "待验签字符串: " . $stringSignTemp); Util::WriteLog('WDPay_verify', "计算签名: " . $calculatedSign); Util::WriteLog('WDPay_verify', "接收签名: " . $receivedSign); return $calculatedSign === $receivedSign; } /** * 递归处理对象/数组为字符串 */ private function buildObjectString($data): string { if (is_array($data)) { $parts = []; ksort($data); foreach ($data as $k => $v) { if ($v !== null && $v !== '') { if (is_array($v) || is_object($v)) { $parts[] = $k . '=' . $this->buildObjectString($v); } else { $parts[] = $k . '=' . $v; } } } return implode('&', $parts); } return (string)$data; } /** * 生成随机字符串 */ public function getNonceStr($length = 16): string { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } /** * POST请求 - application/x-www-form-urlencoded */ public function curlPost($url, $payload) { $timeout = 20; // WDPay使用 application/x-www-form-urlencoded 格式 $data = http_build_query($payload); $headers = [ 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8', ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $result = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_errno($ch)) { $error = curl_error($ch); Util::WriteLog('WDPay_error', 'CURL Error: ' . $error); curl_close($ch); return false; } if ($httpCode != 200) { Util::WriteLog('WDPay_error', 'HTTP Code: ' . $httpCode . " | Response: " . $result); } curl_close($ch); return $result; } }