chore(addon): 添加线下支付及华为支付基本配置

This commit is contained in:
2025-12-01 11:36:51 +08:00
parent 3a7f510e19
commit e51f6c6544
40 changed files with 3238 additions and 12 deletions

View File

@@ -0,0 +1,291 @@
<?php
/**
* 华为支付客户端类
* 封装华为支付API调用
*/
namespace addon\huaweipay\data\sdk;
use think\facade\Log;
class HuaweiPayClient
{
// 华为支付网关地址
private $gatewayUrl = 'https://pay.cloud.huawei.com/gateway/api/pay';
// 华为支付配置
private $config = [];
// 签名算法
private $signType = 'RSA2';
/**
* 构造函数
* @param array $config 华为支付配置
*/
public function __construct($config)
{
$this->config = $config;
// 根据配置设置网关地址
if (isset($config['sandbox']) && $config['sandbox']) {
$this->gatewayUrl = 'https://pay-drcn.cloud.huawei.com/gateway/api/pay';
}
}
/**
* 生成签名
* @param array $params 请求参数
* @return string 签名结果
*/
private function generateSign($params)
{
// 移除空值和签名参数
$params = array_filter($params, function($value) {
return $value !== null && $value !== '';
});
unset($params['sign']);
unset($params['sign_type']);
// 按键名排序
ksort($params);
// 拼接参数
$stringToSign = '';
foreach ($params as $key => $value) {
$stringToSign .= $key . '=' . $value . '&';
}
$stringToSign = rtrim($stringToSign, '&');
// 根据签名类型生成签名
switch ($this->signType) {
case 'RSA2':
$privateKey = $this->config['private_key'];
$privateKey = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($privateKey, 64, "\n", true) . "\n-----END PRIVATE KEY-----";
openssl_sign($stringToSign, $sign, $privateKey, OPENSSL_ALGO_SHA256);
break;
case 'RSA':
$privateKey = $this->config['private_key'];
$privateKey = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($privateKey, 64, "\n", true) . "\n-----END PRIVATE KEY-----";
openssl_sign($stringToSign, $sign, $privateKey, OPENSSL_ALGO_SHA1);
break;
default:
throw new \Exception('不支持的签名类型');
}
return base64_encode($sign);
}
/**
* 验证签名
* @param array $params 响应参数
* @return bool 验证结果
*/
public function verifySign($params)
{
// 保存签名
$sign = $params['sign'] ?? '';
unset($params['sign']);
unset($params['sign_type']);
// 移除空值
$params = array_filter($params, function($value) {
return $value !== null && $value !== '';
});
// 按键名排序
ksort($params);
// 拼接参数
$stringToSign = '';
foreach ($params as $key => $value) {
$stringToSign .= $key . '=' . $value . '&';
}
$stringToSign = rtrim($stringToSign, '&');
// 验证签名
$huaweiPublicKey = $this->config['huawei_public_key'];
$huaweiPublicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($huaweiPublicKey, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
switch ($this->signType) {
case 'RSA2':
$result = openssl_verify($stringToSign, base64_decode($sign), $huaweiPublicKey, OPENSSL_ALGO_SHA256);
break;
case 'RSA':
$result = openssl_verify($stringToSign, base64_decode($sign), $huaweiPublicKey, OPENSSL_ALGO_SHA1);
break;
default:
throw new \Exception('不支持的签名类型');
}
return $result === 1;
}
/**
* 发送HTTP请求
* @param string $url 请求地址
* @param array $params 请求参数
* @param string $method 请求方法
* @return array 响应结果
*/
private function httpRequest($url, $params, $method = 'POST')
{
$ch = curl_init();
// 设置请求头
$headers = [
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
];
// 生成签名
$params['app_id'] = $this->config['app_id'];
$params['sign_type'] = $this->signType;
$params['sign'] = $this->generateSign($params);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
} else {
$url .= '?' . http_build_query($params);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new \Exception('HTTP请求错误: ' . curl_error($ch));
}
curl_close($ch);
// 解析响应
parse_str($response, $result);
return $result;
}
/**
* H5支付
* @param array $params 支付参数
* @param string $returnUrl 同步回调地址
* @param string $notifyUrl 异步回调地址
* @return array 支付结果
*/
public function h5Pay($params, $returnUrl, $notifyUrl)
{
$requestParams = [
'method' => 'h5pay.createPayment',
'version' => '1.0',
'timestamp' => date('Y-m-d H:i:s'),
'out_trade_no' => $params['out_trade_no'],
'subject' => $params['subject'],
'total_amount' => $params['total_amount'],
'body' => $params['body'],
'return_url' => $returnUrl,
'notify_url' => $notifyUrl,
'scene' => 'h5',
];
return $this->httpRequest($this->gatewayUrl, $requestParams);
}
/**
* APP支付
* @param array $params 支付参数
* @param string $notifyUrl 异步回调地址
* @return array 支付结果
*/
public function appPay($params, $notifyUrl)
{
$requestParams = [
'method' => 'apppay.createPayment',
'version' => '1.0',
'timestamp' => date('Y-m-d H:i:s'),
'out_trade_no' => $params['out_trade_no'],
'subject' => $params['subject'],
'total_amount' => $params['total_amount'],
'body' => $params['body'],
'notify_url' => $notifyUrl,
];
return $this->httpRequest($this->gatewayUrl, $requestParams);
}
/**
* 查询订单
* @param array $params 查询参数
* @return array 查询结果
*/
public function queryOrder($params)
{
$requestParams = [
'method' => 'unifiedorder.query',
'version' => '1.0',
'timestamp' => date('Y-m-d H:i:s'),
];
// 支持通过商户订单号或华为交易号查询
if (isset($params['out_trade_no'])) {
$requestParams['out_trade_no'] = $params['out_trade_no'];
} elseif (isset($params['trade_no'])) {
$requestParams['trade_no'] = $params['trade_no'];
} else {
throw new \Exception('查询参数错误必须提供out_trade_no或trade_no');
}
return $this->httpRequest($this->gatewayUrl, $requestParams);
}
/**
* 关闭订单
* @param array $params 关闭参数
* @return array 关闭结果
*/
public function closeOrder($params)
{
$requestParams = [
'method' => 'unifiedorder.close',
'version' => '1.0',
'timestamp' => date('Y-m-d H:i:s'),
'out_trade_no' => $params['out_trade_no'],
];
return $this->httpRequest($this->gatewayUrl, $requestParams);
}
/**
* 退款
* @param array $params 退款参数
* @return array 退款结果
*/
public function refund($params)
{
$requestParams = [
'method' => 'refund.apply',
'version' => '1.0',
'timestamp' => date('Y-m-d H:i:s'),
'out_trade_no' => $params['out_trade_no'],
'trade_no' => $params['trade_no'],
'refund_amount' => $params['refund_amount'],
'out_request_no' => $params['out_request_no'],
'refund_reason' => $params['refund_reason'] ?? '',
];
return $this->httpRequest($this->gatewayUrl, $requestParams);
}
/**
* 验证回调签名
* @param array $params 回调参数
* @return bool 验证结果
*/
public function verifyNotify($params)
{
return $this->verifySign($params);
}
}