Files
cunkebao_v3/Server/application/common/util/PaymentUtil.php
2025-09-23 16:43:18 +08:00

256 lines
7.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\common\util;
/**
* 支付工具类
* 用于处理第三方支付相关功能
* 仅限内部调用
*/
class PaymentUtil
{
/**
* 签名算法类型
*/
const SIGN_TYPE_MD5 = 'MD5';
const SIGN_TYPE_RSA_1_256 = 'RSA_1_256';
const SIGN_TYPE_RSA_1_1 = 'RSA_1_1';
/**
* 生成支付签名
*
* @param array $params 待签名参数
* @param string $secretKey 签名密钥
* @param string $signType 签名类型 MD5/RSA_1_256/RSA_1_1
* @return string 签名结果
*/
public static function generateSign(array $params, string $secretKey, string $signType = self::SIGN_TYPE_MD5): string
{
// 1. 移除sign字段
unset($params['sign']);
// 2. 过滤空值
$params = array_filter($params, function($value) {
return $value !== '' && $value !== null;
});
// 3. 按字段名ASCII码从小到大排序
ksort($params);
// 4. 拼接成QueryString格式
$queryString = self::buildQueryString($params);
// 5. 根据签名类型生成签名
switch (strtoupper($signType)) {
case self::SIGN_TYPE_MD5:
return self::generateMd5Sign($queryString, $secretKey);
case self::SIGN_TYPE_RSA_1_256:
return self::generateRsa256Sign($queryString, $secretKey);
case self::SIGN_TYPE_RSA_1_1:
return self::generateRsa1Sign($queryString, $secretKey);
default:
throw new \InvalidArgumentException('不支持的签名类型: ' . $signType);
}
}
/**
* 验证支付签名
*
* @param array $params 待验证参数包含sign字段
* @param string $secretKey 签名密钥
* @param string $signType 签名类型
* @return bool 验证结果
*/
public static function verifySign(array $params, string $secretKey, string $signType = self::SIGN_TYPE_MD5): bool
{
if (!isset($params['sign'])) {
return false;
}
$receivedSign = $params['sign'];
$generatedSign = self::generateSign($params, $secretKey, $signType);
return $receivedSign === $generatedSign;
}
/**
* 构建QueryString
*
* @param array $params 参数数组
* @return string QueryString
*/
private static function buildQueryString(array $params): string
{
$pairs = [];
foreach ($params as $key => $value) {
$pairs[] = $key . '=' . $value;
}
return implode('&', $pairs);
}
/**
* 生成MD5签名
*
* @param string $queryString 待签名字符串
* @param string $secretKey 密钥
* @return string MD5签名
*/
private static function generateMd5Sign(string $queryString, string $secretKey): string
{
$signString = $queryString . '&key=' . $secretKey;
return strtoupper(md5($signString));
}
/**
* 生成RSA256签名
*
* @param string $queryString 待签名字符串
* @param string $privateKey 私钥
* @return string RSA256签名
*/
private static function generateRsa256Sign(string $queryString, string $privateKey): string
{
$privateKey = self::formatPrivateKey($privateKey);
$key = openssl_pkey_get_private($privateKey);
if (!$key) {
throw new \Exception('RSA私钥格式错误');
}
$signature = '';
$result = openssl_sign($queryString, $signature, $key, OPENSSL_ALGO_SHA256);
openssl_pkey_free($key);
if (!$result) {
throw new \Exception('RSA256签名失败');
}
return base64_encode($signature);
}
/**
* 生成RSA1签名
*
* @param string $queryString 待签名字符串
* @param string $privateKey 私钥
* @return string RSA1签名
*/
private static function generateRsa1Sign(string $queryString, string $privateKey): string
{
$privateKey = self::formatPrivateKey($privateKey);
$key = openssl_pkey_get_private($privateKey);
if (!$key) {
throw new \Exception('RSA私钥格式错误');
}
$signature = '';
$result = openssl_sign($queryString, $signature, $key, OPENSSL_ALGO_SHA1);
openssl_pkey_free($key);
if (!$result) {
throw new \Exception('RSA1签名失败');
}
return base64_encode($signature);
}
/**
* 格式化私钥
*
* @param string $privateKey 原始私钥
* @return string 格式化后的私钥
*/
private static function formatPrivateKey(string $privateKey): string
{
$privateKey = str_replace(['-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----', "\n", "\r"], '', $privateKey);
$privateKey = chunk_split($privateKey, 64, "\n");
return "-----BEGIN PRIVATE KEY-----\n" . $privateKey . "-----END PRIVATE KEY-----";
}
/**
* 格式化公钥
*
* @param string $publicKey 原始公钥
* @return string 格式化后的公钥
*/
private static function formatPublicKey(string $publicKey): string
{
$publicKey = str_replace(['-----BEGIN PUBLIC KEY-----', '-----END PUBLIC KEY-----', "\n", "\r"], '', $publicKey);
$publicKey = chunk_split($publicKey, 64, "\n");
return "-----BEGIN PUBLIC KEY-----\n" . $publicKey . "-----END PUBLIC KEY-----";
}
/**
* 验证RSA签名
*
* @param string $queryString 原始字符串
* @param string $signature 签名
* @param string $publicKey 公钥
* @param string $signType 签名类型
* @return bool 验证结果
*/
public static function verifyRsaSign(string $queryString, string $signature, string $publicKey, string $signType = self::SIGN_TYPE_RSA_1_256): bool
{
$publicKey = self::formatPublicKey($publicKey);
$key = openssl_pkey_get_public($publicKey);
if (!$key) {
return false;
}
$algorithm = $signType === self::SIGN_TYPE_RSA_1_1 ? OPENSSL_ALGO_SHA1 : OPENSSL_ALGO_SHA256;
$result = openssl_verify($queryString, base64_decode($signature), $key, $algorithm);
openssl_pkey_free($key);
return $result === 1;
}
/**
* 生成随机字符串
*
* @param int $length 长度
* @return string 随机字符串
*/
public static function generateNonceStr(int $length = 32): string
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $str;
}
/**
* 生成时间戳
*
* @return int 时间戳
*/
public static function generateTimestamp(): int
{
return time();
}
/**
* 格式化金额(分转元)
*
* @param int $amount 金额(分)
* @return string 格式化后的金额(元)
*/
public static function formatAmount(int $amount): string
{
return number_format($amount / 100, 2, '.', '');
}
/**
* 解析金额(元转分)
*
* @param string $amount 金额(元)
* @return int 金额(分)
*/
public static function parseAmount(string $amount): int
{
return (int) round(floatval($amount) * 100);
}
}