| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- <?php
- namespace App\Services;
- use App\Util;
- class SafePay
- {
- public $config;
- public $merNo;
- public $apiUrl;
- public $privateKey;
- public $platformPublicKey;
- public function __construct()
- {
- $payConfigService = new PayConfig();
- $this->config = $payConfigService->getConfig('SafePay');
- $this->merNo = $this->config['mer_no'] ?? '';
- $this->apiUrl = $this->config['apiUrl'] ?? 'https://api.safepay.wang';
- $this->privateKey = $this->config['private_key'] ?? '';
- $this->platformPublicKey = $this->config['platform_public_key'] ?? '';
- }
- /**
- * 构建待签名数据(ASCII升序、QueryString格式、跳过sign和空值)
- */
- public function buildSignData(array $params): string
- {
- // 移除sign字段和空值
- $signData = [];
- foreach ($params as $key => $value) {
- if ($key === 'sign') {
- continue;
- }
- if ($value === null || $value === '' || (is_array($value) && empty($value))) {
- continue;
- }
- if (is_array($value) || is_object($value)) {
- $signData[$key] = json_encode($value, JSON_UNESCAPED_UNICODE);
- } else {
- $signData[$key] = (string)$value;
- }
- }
- // ASCII升序(字典序)
- ksort($signData);
- // 构建 QueryString: key1=value1&key2=value2
- $result = '';
- foreach ($signData as $key => $value) {
- $result .= '&' . $key . '=' . $value;
- }
- return substr($result, 1);
- }
- /**
- * RSA SHA256 签名(使用商户私钥)
- *
- * @param array $params 请求参数
- * @return array 添加sign后的参数
- */
- public function sign(array $params): array
- {
- $signData = $this->buildSignData($params);
- $prvKeyPem = $this->convertPrivateKeyToPem($this->privateKey);
- $signature = '';
- $signed = openssl_sign($signData, $signature, $prvKeyPem, OPENSSL_ALGO_SHA256);
- if (!$signed) {
- throw new \Exception('SafePay RSA签名失败');
- }
- $params['sign'] = base64_encode($signature);
- Util::WriteLog('SafePay_sign', "待签名字符串: " . $signData);
- Util::WriteLog('SafePay_sign', "签名结果: " . $params['sign']);
- return $params;
- }
- /**
- * RSA SHA256 验签(使用平台公钥)
- *
- * @param array $params 回调参数(含sign)
- * @return bool
- */
- public function verifySign(array $params): bool
- {
- $receivedSign = $params['sign'] ?? '';
- if (empty($receivedSign)) {
- Util::WriteLog('SafePay', '验签失败:sign字段为空');
- return false;
- }
- $signData = $this->buildSignData($params);
- $pubKeyPem = $this->convertPublicKeyToPem($this->platformPublicKey);
- $isVerified = openssl_verify(
- $signData,
- base64_decode($receivedSign),
- $pubKeyPem,
- OPENSSL_ALGO_SHA256
- );
- Util::WriteLog('SafePay_verify', "验签数据: " . $signData);
- Util::WriteLog('SafePay_verify', "验签结果: " . ($isVerified === 1 ? '通过' : '失败'));
- return $isVerified === 1;
- }
- /**
- * 转换私钥为PEM格式
- */
- private function convertPrivateKeyToPem($privateKeyString)
- {
- if (strpos($privateKeyString, '-----BEGIN PRIVATE KEY-----') !== false) {
- return $privateKeyString;
- }
- $key = trim($privateKeyString);
- if (!preg_match('/^-----BEGIN/', $key)) {
- if (base64_decode($key, true)) {
- $key = base64_decode($key);
- }
- $key = "-----BEGIN PRIVATE KEY-----\n" . chunk_split(base64_encode($key), 64, "\n") . "-----END PRIVATE KEY-----";
- }
- return $key;
- }
- /**
- * 转换公钥为PEM格式
- */
- private function convertPublicKeyToPem($publicKeyString)
- {
- if (strpos($publicKeyString, '-----BEGIN PUBLIC KEY-----') !== false) {
- return $publicKeyString;
- }
- $key = trim($publicKeyString);
- if (!preg_match('/^-----BEGIN/', $key)) {
- if (base64_decode($key, true)) {
- $key = base64_decode($key);
- }
- $key = "-----BEGIN PUBLIC KEY-----\n" . chunk_split(base64_encode($key), 64, "\n") . "-----END PUBLIC KEY-----";
- }
- return $key;
- }
- /**
- * POST JSON请求
- */
- public function curlPost($url, $payload)
- {
- $timeout = 20;
- $data = json_encode($payload, JSON_UNESCAPED_UNICODE);
- $headers = [
- 'Content-Type: application/json',
- ];
- $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);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- $result = curl_exec($ch);
- if (curl_errno($ch)) {
- $error = curl_error($ch);
- Util::WriteLog('SafePay_error', 'CURL Error: ' . $error);
- curl_close($ch);
- return false;
- }
- curl_close($ch);
- return $result;
- }
- }
|