config = $payConfigService->getConfig('SfPay'); $this->merchantKey = $this->config['merchant_key'] ?? ''; $this->aesKey = $this->config['aes_key'] ?? ''; $this->aesIv = $this->config['aes_iv'] ?? ''; $this->apiUrl = $this->config['apiUrl'] ?? 'https://sfgateway.sforest.io'; } /** * AES/CBC/PKCS5Padding 加密 * * @param string $str 待加密JSON字符串 * @return string base64编码的密文 */ public function encrypt($str) { return base64_encode(openssl_encrypt($str, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv)); } /** * AES/CBC/PKCS5Padding 解密 * * @param string $encrypted base64编码的密文 * @return string 解密后的JSON字符串 */ public function decrypt($encrypted) { return openssl_decrypt(base64_decode($encrypted), 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv); } /** * 验证回调签名 * * signature_n = MD5(merchantKey + message + amount + status + merchantOrderNo + orderNo + aeskey) * * 注意:如果amount为空,则拼接"null"字符串(代付回调可能出现amount为空的情况) * * @param array $data 回调data参数 * @param string $signatureN 签名值 * @return bool */ public function verifySign($data, $signatureN) { $message = $data['message'] ?? ''; $amount = $data['amount'] ?? ''; $status = $data['status'] ?? ''; $merchantOrderNo = $data['merchantOrderNo'] ?? ''; $orderNo = $data['orderNo'] ?? ''; // 代付回调中amount为空时,拼接"null"字符串 if ($amount === '' || $amount === null) { $amount = 'null'; } $signStr = $this->merchantKey . $message . $amount . $status . $merchantOrderNo . $orderNo . $this->aesKey; $expectedSign = md5($signStr); Util::WriteLog('SfPay', "待签名字符串: {$signStr}"); Util::WriteLog('SfPay', "期望签名: {$expectedSign}, 实际签名: {$signatureN}"); return strtolower($expectedSign) === strtolower($signatureN); } /** * POST JSON请求(SfPay格式:data字段AES加密,merchant_key放在header) * * @param string $url 请求地址 * @param array $params 待加密的请求参数 * @return string|false */ public function curlPost($url, $params) { $timeout = 30; $jsonStr = json_encode($params, JSON_UNESCAPED_UNICODE); // AES加密 $encrypted = $this->encrypt($jsonStr); $body = json_encode(['data' => $encrypted], JSON_UNESCAPED_UNICODE); $headers = [ 'Content-Type: application/json;charset=UTF-8', 'merchant_key: ' . $this->merchantKey, ]; Util::WriteLog('SfPay', "SfPay请求URL: {$url}"); Util::WriteLog('SfPay', "SfPay请求明文: {$jsonStr}"); Util::WriteLog('SfPay', "SfPay请求密文: {$body}"); $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, $body); 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); if (curl_errno($ch)) { $error = curl_error($ch); Util::WriteLog('SfPay_error', 'CURL Error: ' . $error); curl_close($ch); return false; } curl_close($ch); Util::WriteLog('SfPay', "SfPay响应: {$result}"); return $result; } }