<?php declare(strict_types=1); ini_set('display_errors', 'On'); error_reporting(-1); const API_KEY = '192006250b4c09247ec02edce69f6a2d'; // API密钥 /** * 过滤空值参数并排序 * 说明:空值参数不参与签名(调用API时也无需传递),需要将其删除,空值只有空字符串''和null两种情况。 * * @param array $params 签名参数(数组键值对) * @return array 过滤空值参数并排序后的数组 */ function params_filter_and_sort(array $params): array { // 这里不能直接使用array_filter()函数,因为这样会把值为0的元素(不管是int还是string)也删除,而0不是空值。 $arr = array_filter($params, static function (mixed $value): bool { return $value !== '' && $value !== null; // 返回true则会保留该元素,即删除数组中的空字符串''和null }); // 按照参数名ASCII字典序排序(即按照a~z升序) ksort($arr); return $arr; } /** * 生成签名 * * @param array $params 签名参数(数组键值对) * @param string $type 签名类型,可选值:MD5|HMAC-SHA256 * @return string 签名字符串 */ function generate_signature(array $params, string $type): string { $paramsSign = params_filter_and_sort($params); // 将数组格式的参数转成签名原串(即“key1=value1&key2=value2&key3=value3”格式的字符串) // 重要说明:http_build_query()会自动将中文或某些特殊字符URLencode编码,故需要urldecode()解码。 $stringA = urldecode(http_build_query($paramsSign)); // 拼接API密钥 $stringSignTemp = "$stringA&key=" . API_KEY; if (strtoupper($type) === 'MD5') { $sign = md5($stringSignTemp); // MD5签名类型 } else { $sign = hash_hmac('sha256', $stringSignTemp, API_KEY); // HMAC-SHA256签名类型 } return strtoupper($sign); } // API参数 $params = [ 'site' => '码农人生', 'algorithm' => null, 'appid' => 'wxd930ea5d5a258f4f', 'mch_id' => 10000100, 'device_info' => 1000, 'body' => 'test', 'desc' => '', 'price' => 0, 'site_encode' => '%E7%A0%81%E5%86%9C%E4%BA%BA%E7%94%9F', 'nonce_str' => 'ibuaiVcKdpRxkhJA', ]; $signMd5 = generate_signature($params, 'MD5'); echo "MD5签名:$signMd5" . PHP_EOL; // MD5签名:F93653F407056555AEAD160BF39C72D3 $signSha256 = generate_signature($params, 'HMAC-SHA256'); echo "HMAC-SHA256签名:$signSha256" . PHP_EOL; // HMAC-SHA256签名:D560DAB7A4687B5A3CE9CC9BA43716EDD41BC572C2EE65091F0BB88ED55D212F // 最终的提交XML $paramsPost = params_filter_and_sort($params); $xml = '<xml>' . PHP_EOL; foreach ($paramsPost as $key => $value) { $xml .= " <$key><![CDATA[$value]]></$key>" . PHP_EOL; } $xml .= " <sign>$signMd5</sign>" . PHP_EOL; $xml .= " <sign>$signSha256</sign>" . PHP_EOL; $xml .= '</xml>'; echo $xml; // <xml> // <appid><![CDATA[wxd930ea5d5a258f4f]]></appid> // <body><![CDATA[test]]></body> // <device_info><![CDATA[1000]]></device_info> // <mch_id><![CDATA[10000100]]></mch_id> // <nonce_str><![CDATA[ibuaiVcKdpRxkhJA]]></nonce_str> // <price><![CDATA[0]]></price> // <site><![CDATA[码农人生]]></site> // <site_encode><![CDATA[%E7%A0%81%E5%86%9C%E4%BA%BA%E7%94%9F]]></site_encode> // <sign>F93653F407056555AEAD160BF39C72D3</sign> // <sign>D560DAB7A4687B5A3CE9CC9BA43716EDD41BC572C2EE65091F0BB88ED55D212F</sign> // </xml> // 签名校验工具:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1 //========== 总结 ==========// // 1、只要参数不变,那么每次生成的签名都是一样的。 // 2、空值参数不参与签名(调用API时也无需传递),空值只有空字符串''和null两种情况。 // 3、构造“key1=value1&key2=value2&key3=value3”格式的签名原串时,value需要使用原值,如果value被URLencode编码了务必解码。
Copyright © 2024 码农人生. All Rights Reserved