'https://open-sea.worldfirst.com', // 万里汇支付域名 'is_sandbox' => 1, // 是否沙箱环境,true为沙箱环境,false为正式环境 'ClientID' => '5YEU5A2ZUGHC03903', //商户id 'CustomerId' => '2120120341978772', //商户id 'CertificationType' => 'RSA2', //商户密钥 'publicKey' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQrc4mu33M3lo+EehBo+/GHmBTC4OdyqdkbB9YAUWH1xfHa3o6frFTS0JhgZa3RX7W5g4AGMhNynaytsErFaUVuij95rQhU5rVIPAE6mxlmjZMQszzOjJZdLuEe8uQIlzxbdgJZ6s1qElrJzfCoBl3UP/3h4e29Wyfp0c9rKg8/PT5TIM5MkL0X5Bs/gU3sclziAO/HVkgSqeOVah4HRYOjTxJeLv4UlkJr7iSXshv0THDIdFbwi9b83yclOTdqZpReo41Bec8SAEQz8ITb81iFHVRuCxKL7ZtRIjlMIZHbZdZdQijZfVsMrD0Y/7eK8tuCpfvBRCsxLOaUH5Tkx6wIDAQAB', //公钥 'privateKey' => 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxCtzia7fczeWj4R6EGj78YeYFMLg53Kp2RsH1gBRYfXF8drejp+sVNLQmGBlrdFftbmDgAYyE3KdrK2wSsVpRW6KP3mtCFTmtUg8ATqbGWaNkxCzPM6Mll0u4R7y5AiXPFt2AlnqzWoSWsnN8KgGXdQ//eHh7b1bJ+nRz2sqDz89PlMgzkyQvRfkGz+BTexyXOIA78dWSBKp45VqHgdFg6NPEl4u/hSWQmvuJJeyG/RMcMh0VvCL1vzfJyU5N2pmlF6jjUF5zxIARDPwhNvzWIUdVG4LEovtm1EiOUwhkdtl1l1CKNl9WwysPRj/t4ry24Kl+8FEKzEs5pQflOTHrAgMBAAECggEBAI4mp7ZRQT7UP3d3EPaG0F37CSLPvIwRBvmS1LeXED8A8fc6pzaLZDERsTgJD5f8wYDNqJEUDUy8ktx08ACOUHOUvREBSGO2ASqOAaeAf14xqQUGkugHkQ5kzZJ4Xyq5c7w0osNVj82kd2M8g4eFfuOqRZ8djrelbS0doRVI7mQoQyxb2ms4priD4Zgv+YqnGIQz9++f9aguCpmTRLp8VC/N4CqszLhJ3N4f+X4sAfttPbkT1lKXXB4Gx1oBnpDwXpxfvocC2E4xwBprr7pVqlvMkpdmZnDBHgAk4nLISk3hv3kLC9FVVfhEgpJJlMUW0Qb3IbPx64UgV9k9pCCsSXECgYEA/ap6WAstyS3zZDAacazhqCbVDc6/IPQW6Y78ahObpV9ViCVoDPiYVimStNIZKQvCOIwo9N5PHeCNHNOp30DoY1MOV26vu9hFLGqukoIAsMhM9c9E1ZtN0IkSzTK5eP0vKHLTeB4vuq2f6eR7g4bHNRLfHplE6smFNZ4/ZW9w1hkCgYEAsqvlCA5/0j13IuVORY2iVmQ2xC86fjTRRmpZ0KRPauUjp25WIYZUc/Dh2sAwT62digL7lPTXJ9dXyGEkiMxVrck9wN6fsdgmFkcAbOQeI5v0hc/NBmsfuhNH5Lmho/ZFbrAE2ha8viK5zBZr6ZIha7d8EFQZ+6TFoeBHISDD4KMCgYBXdqGKnAgkduCWKfPkQCzqcsFBaokCTsdbrr6fwLUJF08Bi4xN9KVqCBmamVqtiW1lXAZ/L+vtrFBboSvQW6wFG001nmefiFmJkBszTvn3+oh2tQnR8SOqhKjj8dp7uii5dKYvo5DneBhMaEiiOCWVyjT+cvCNWADDVRYc3oj0eQKBgDxzFekU5sAd1Znz1PiJQ9xQyYq0o+ihPBDD2KFThh8XaWmzVea/yQD1BaT6Ex5SEhPokG/EKqsrG2MLIs45u98xT/haGdOU2sX8vbMZtuy7Tg6b0LUUN0bAUTmcaIjNwI4DdZDH0pHNs+jNyTLcIvyLtqjbm3LdO5RaCha2PC9bAoGAHDRox1+xXWJ1p1qMu73kmTjwTNIgtfZkV5un5KybpLLkpF28K61yGNTGHXN8mnNxWYiITb6LzO7OB2A97YmlVB2DYBkDfSLIL2kWCsDnHA5iiWewmrm2t3079YgiRWtN224/z+CVspaiOKgypLZzyxrPfqajXjwwOEEVkb7HO4o=', //商户私钥privateKey ]; public function index() { // 从表单获取的参数(模拟$_POST) //获取请求时间() 为 2022-04-28T12:31:30+08:00,时间遵循ISO 8601标准 $requestTime = date('Y-m-d\TH:i:sP'); $clientId = $this->config['is_sandbox'] == 1 ? 'SANDBOX_' . $this->config['ClientID'] : $this->config['ClientID']; $post_data = ["paymentId" => "202205311234234"]; // 包裹私钥 $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($this->config['privateKey'], 64, "\n", true) . "\n-----END RSA PRIVATE KEY-----"; $publicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($this->config['publicKey'], 64, "\n", true) . "\n-----END PUBLIC KEY-----"; $httpMethod = 'POST'; // 请求方法(GET/POST等) $api_url = '/amsin/api/v1/business/inquiryPayOrder'; // 请求API路径 $bodyData = json_encode($post_data); // 请求体JSON字符串 $params = [ 'clientId' => $clientId, //'customerId' => $this->config['CustomerId'], 'requestTime' => $requestTime, 'httpMethod' => $httpMethod, 'api' => $api_url, 'bodyData' => $bodyData, 'privkey' => $privateKey, ]; $signatureBody = $this->getSignatureBody($post_data, $httpMethod, $api_url, $requestTime); // 生成签名 $signature = $this->generateSignature($signatureBody, $privateKey); // 验证签名示例(需要公钥) $isValid = $this->verifySignature( $signatureBody, $signature, $publicKey ); $params['signature'] = $signature; $params['isValid'] = $isValid; return dhkSuccess($params); echo $signature; } public function getFirst() { $post_data = [ "inquiryRateConditionList" => [ [ "sellCurrency" => "USD", "buyCurrency" => "EUR" ], [ "sellCurrency" => "USD", "buyCurrency" => "GBP" ] ] ]; $requestTime = date('Y-m-d\TH:i:sP'); $httpMethod = 'POST'; // 请求方法(GET/POST等) $api_url = '/amsin/api/v1/business/inquiryPayOrder'; //支付完成后,集成商可以使用此接口来查询支付结果。 $signatureBody = $this->getSignatureBody($post_data, $httpMethod, $api_url, $requestTime); // 包裹私钥 $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($this->config['privateKey'], 64, "\n", true) . "\n-----END RSA PRIVATE KEY-----"; // 生成签名 $signature = $this->generateSignature($signatureBody, $privateKey); $post_url = $this->config['domain'] . $api_url; $header = []; $clientId = $this->config['is_sandbox'] == 1 ? 'SANDBOX_' . $this->config['ClientID'] : $this->config['ClientID']; $header['client-id'] = $clientId; $header['request-time'] = $requestTime; $header['Content-Type'] = 'application/json'; $header['signature'] = 'algorithm=RSA256,keyVersion=1,signature=' . $signature; $dhkhttp = new \app\common\toole\Dhkhttp(); echo_dhklog($api_url, '请求地址:', 'pay_notify'); $res = $dhkhttp->dhkpost($post_url, $post_data, $header); return dhkSuccess($res); } //查询支付状态http://zhc.test.com/home/testpay/query?pay_code=202504171234234 public function query() { $params = Request::param(); $payToRequestId = $params['pay_code'] ?? ''; //商户生成的支付单号 $payToId = $params['payToId'] ?? ''; //万里汇生成的支付单号 if (!$payToRequestId and !$payToId) { return dhkMsg('支付单号不能为空'); } $post_data = []; if ($payToRequestId) { $payToRequestIds = [$payToRequestId]; $post_data['payToRequestIds'] = $payToRequestIds; } if ($payToId) { $payToIds = [$payToId]; $post_data['payToIds'] = $payToIds; } $requestTime = date('Y-m-d\TH:i:sP'); $httpMethod = 'POST'; // 请求方法(GET/POST等) $api_url = '/amsin/api/v1/business/inquiryPayOrder'; //支付完成后,集成商可以使用此接口来查询支付结果。 $signatureBody = $this->getSignatureBody($post_data, $httpMethod, $api_url, $requestTime); // 包裹私钥 $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($this->config['privateKey'], 64, "\n", true) . "\n-----END RSA PRIVATE KEY-----"; // 生成签名 $signature = $this->generateSignature($signatureBody, $privateKey); $post_url = $this->config['domain'] . $api_url; $header = []; $clientId = $this->config['is_sandbox'] == 1 ? 'SANDBOX_' . $this->config['ClientID'] : $this->config['ClientID']; $header['client-id'] = $clientId; $header['request-time'] = $requestTime; $header['Content-Type'] = 'application/json'; $header['signature'] = 'algorithm=RSA256,keyVersion=1,signature=' . $signature; $dhkhttp = new \app\common\toole\Dhkhttp(); $res = $dhkhttp->dhkpost($post_url, $post_data, $header); return dhkSuccess($res); } public function notify() { $params = Request::param(); echo_dhklog($params, '支付通知data:', 'pay_notify'); $headers = Request::header(); echo_dhklog($headers, '支付通知header:', 'pay_notify'); $requestTime = $headers['request-time'] ?? ''; //请求时间 $signature = $headers['signature'] ?? ''; //签名 //algorithm=RSA256,keyVersion=1,signature=IHZfFMPNQITqmz%2FDZTlm6DEfm6yfR3NtK0LkONscZxZLqOuCa5b%2FDyLGqwZaSrGIQeFFNnGS5EgqRtFFdOmBSuSSiwK%2BjvhnIE6qHP7xr%2BjBpjFFztTOyyniGJdN5HlT8UE2wONjPIsIMh8koXa6Kb8uh1ScnCGbTzY8ogfihupFXJt%2B1%2BHPYno9483cVyA2l9UOYb6ai6SWgAxnJJUWi%2BkBQLXp%2FzvRG6SmDETbZ8MpMM2gUTbRGB4p4eSqVcX6KgwVyLSgrzAGekPv6dkZOH5zulHCv%2FmMXdO3%2FWmQn%2FX2rS1unS9yilDJbzDW86XwUf46UD2qgxkFGi6UPWaTfw%3D%3D //从$signature中提取signature=后面的内容 $signature = str_replace('algorithm=RSA256,keyVersion=1,signature=', '', $signature); echo_dhklog($signature, 'HEADER签名', 'pay_notify'); $clientId = $headers['client-id'] ?? ''; $httpMethod = 'POST'; $api_url = '/home/testpay/notify'; // 包裹私钥 $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($this->config['privateKey'], 64, "\n", true) . "\n-----END RSA PRIVATE KEY-----"; $res = ["result" => ["resultStatus" => "S", "resultCode" => "SUCCESS", "resultMessage" => "success"]]; $signatureBody = $this->getSignatureBody($params, $httpMethod, $api_url, $requestTime); // 生成签名 $signature_new = $this->generateSignature($signatureBody, $privateKey); if ($signature_new != $signature) { echo_dhklog($signature_new, '签名不匹配', 'pay_notify'); } else { echo_dhklog('验证通过', '签名匹配', 'pay_notify'); } //返回响应头 header('Content-Type: application/json'); header('client-id: ' . $clientId); header('request-time: ' . $requestTime); header('signature: algorithm=RSA256,keyVersion=1,signature=' . $signature_new); return json($res); } //1. 构造待签名字符串(与JavaScript逻辑完全一致) public function getSignatureBody($post_data = [], $httpMethod = 'POST', $api_url = '', $requestTime = '') { $bodyData = json_encode($post_data); $clientId = $this->config['is_sandbox'] == 1 ? 'SANDBOX_' . $this->config['ClientID'] : $this->config['ClientID']; $signatureBody = sprintf( "%s %s\n%s.%s.%s", strtoupper($httpMethod), $api_url, $clientId, $requestTime, $bodyData ); return $signatureBody; } /** * 生成签名 * @param string $clientId 商户ID * @param string $requestTime 请求时间戳 * @param string $httpMethod HTTP方法(GET/POST等) * @param string $api 请求API路径 * @param string $bodyData 请求体JSON字符串 * @param string $privateKey 私钥(PEM格式) * @return string 返回URL编码后的签名 */ public static function generateSignature($signatureBody, $privateKey): string { // 2. 使用私钥进行SHA256签名 $signature = ''; if (!openssl_sign($signatureBody, $signature, $privateKey, OPENSSL_ALGO_SHA256)) { // 输出错误信息 echo_dhklog(openssl_error_string(), 'OpenSSL错误', 'pay_notify'); return ''; } // 3. Base64编码签名 $ss = base64_encode($signature); echo_dhklog($ss, '签名', 'pay_notify'); // 4. URL编码签名,非必要 $ss = urlencode($ss); return $ss; } /** * 验证签名(可选) */ public static function verifySignature( string $plainText, string $signature, string $publicKey ): bool { $signature = base64_decode(urldecode($signature)); return (bool)openssl_verify( $plainText, $signature, $publicKey, OPENSSL_ALGO_SHA256 ); } }