chore(docker): 为dev准备部署条件
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace addon\huaweipay\data\sdk;
|
||||
|
||||
use app\exception\ApiException;
|
||||
use think\facade\Log;
|
||||
|
||||
class HuaweiPayClient
|
||||
@@ -18,6 +19,18 @@ class HuaweiPayClient
|
||||
|
||||
// 签名算法
|
||||
private $signType = 'RSA2';
|
||||
|
||||
// 商户应用私有证书实例
|
||||
private $private_key_certificate_instance = '';
|
||||
|
||||
// 华为平台支付证书实例
|
||||
private $huawei_public_key_certificate_instance = '';
|
||||
|
||||
// 华为平台支付服务加密证书实例
|
||||
private $huawei_public_key_certificate_instance_encrypt = '';
|
||||
|
||||
// 是否加载了华为平台支付服务加密证书
|
||||
private $has_huawei_public_key_certificate_instance_encrypt = false;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@@ -26,6 +39,46 @@ class HuaweiPayClient
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
// 证书基础路径
|
||||
$cert_base_path = app()->getRootPath();
|
||||
|
||||
// 加载商户应用私有证书
|
||||
$private_key_path = realpath($cert_base_path . $this->config['private_key']);
|
||||
if (!$private_key_path) {
|
||||
throw new \Exception('商户应用私有证书文件不存在: ' . $this->config['private_key']);
|
||||
}
|
||||
$private_key_content = file_get_contents($private_key_path);
|
||||
$this->private_key_certificate_instance = openssl_pkey_get_private($private_key_content);
|
||||
if (!$this->private_key_certificate_instance) {
|
||||
throw new \Exception('加载商户应用私有证书失败');
|
||||
}
|
||||
|
||||
// 加载华为平台支付证书
|
||||
$huawei_public_key_path = realpath($cert_base_path . $this->config['huawei_public_key']);
|
||||
if (!$huawei_public_key_path) {
|
||||
throw new \Exception('华为平台支付证书文件不存在: ' . $this->config['huawei_public_key']);
|
||||
}
|
||||
$huawei_public_key_content = file_get_contents($huawei_public_key_path);
|
||||
$this->huawei_public_key_certificate_instance = openssl_pkey_get_public($huawei_public_key_content);
|
||||
if (!$this->huawei_public_key_certificate_instance) {
|
||||
throw new \Exception('加载华为平台支付证书失败');
|
||||
}
|
||||
|
||||
// 加载华为平台支付服务加密证书
|
||||
if (!empty($this->config['huawei_public_key_for_sessionkey'])) {
|
||||
$huawei_public_key_encrypt_path = realpath($cert_base_path . $this->config['huawei_public_key_for_sessionkey']);
|
||||
if (!$huawei_public_key_encrypt_path) {
|
||||
throw new \Exception('华为平台支付服务加密证书文件不存在: ' . $this->config['huawei_public_key_for_sessionkey']);
|
||||
}
|
||||
$huawei_public_key_encrypt_content = file_get_contents($huawei_public_key_encrypt_path);
|
||||
$this->huawei_public_key_certificate_instance_encrypt = openssl_pkey_get_public($huawei_public_key_encrypt_content);
|
||||
if (!$this->huawei_public_key_certificate_instance_encrypt) {
|
||||
throw new \Exception('加载华为平台支付服务加密证书失败');
|
||||
}
|
||||
$this->has_huawei_public_key_certificate_instance_encrypt = true;
|
||||
}
|
||||
|
||||
|
||||
// 根据配置设置网关地址
|
||||
if (isset($config['sandbox']) && $config['sandbox']) {
|
||||
@@ -57,17 +110,20 @@ class HuaweiPayClient
|
||||
}
|
||||
$stringToSign = rtrim($stringToSign, '&');
|
||||
|
||||
// 签名结果
|
||||
$sign = '';
|
||||
|
||||
// 根据签名类型生成签名
|
||||
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);
|
||||
if (!openssl_sign($stringToSign, $sign, $this->private_key_certificate_instance, OPENSSL_ALGO_SHA256)) {
|
||||
throw new \Exception('RSA2签名生成失败');
|
||||
}
|
||||
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);
|
||||
if (!openssl_sign($stringToSign, $sign, $this->private_key_certificate_instance, OPENSSL_ALGO_SHA1)) {
|
||||
throw new \Exception('RSA签名生成失败');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('不支持的签名类型');
|
||||
@@ -85,6 +141,10 @@ class HuaweiPayClient
|
||||
{
|
||||
// 保存签名
|
||||
$sign = $params['sign'] ?? '';
|
||||
if (empty($sign)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($params['sign']);
|
||||
unset($params['sign_type']);
|
||||
|
||||
@@ -104,15 +164,14 @@ class HuaweiPayClient
|
||||
$stringToSign = rtrim($stringToSign, '&');
|
||||
|
||||
// 验证签名
|
||||
$huaweiPublicKey = $this->config['huawei_public_key'];
|
||||
$huaweiPublicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($huaweiPublicKey, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
|
||||
$result = 0;
|
||||
|
||||
switch ($this->signType) {
|
||||
case 'RSA2':
|
||||
$result = openssl_verify($stringToSign, base64_decode($sign), $huaweiPublicKey, OPENSSL_ALGO_SHA256);
|
||||
$result = openssl_verify($stringToSign, base64_decode($sign), $this->huawei_public_key_certificate_instance, OPENSSL_ALGO_SHA256);
|
||||
break;
|
||||
case 'RSA':
|
||||
$result = openssl_verify($stringToSign, base64_decode($sign), $huaweiPublicKey, OPENSSL_ALGO_SHA1);
|
||||
$result = openssl_verify($stringToSign, base64_decode($sign), $this->huawei_public_key_certificate_instance, OPENSSL_ALGO_SHA1);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('不支持的签名类型');
|
||||
@@ -131,14 +190,20 @@ class HuaweiPayClient
|
||||
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['appId'] = $this->config['app_id'];
|
||||
$params['mercNo'] = $this->config['merc_no'];
|
||||
|
||||
// --- 验证以下参数必须存在 ---
|
||||
$requiredParams = ['appId', 'mercNo', 'mercOrderNo', 'tradeSummary', 'totalAmount', 'callbackUrl'];
|
||||
foreach ($requiredParams as $param) {
|
||||
if (!isset($params[$param]) || empty($params[$param])) {
|
||||
throw new \Exception('缺少必要参数: ' . $param);
|
||||
}
|
||||
}
|
||||
|
||||
$params['sign_type'] = $this->signType;
|
||||
$params['sign'] = $this->generateSign($params);
|
||||
|
||||
@@ -148,6 +213,27 @@ class HuaweiPayClient
|
||||
} else {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
// 参考:https://developer.huawei.com/consumer/cn/doc/HMSCore-References/api-data-model-0000001538219104#section11744172016145
|
||||
$pay_merc_auth_part = [
|
||||
'callerId' => $this->config['merc_no'], // 商户号
|
||||
'traceId' => $params['traceId'] ?? uniqid(), // 交易ID或跟踪ID
|
||||
'time' => date('YmdHis'), // 时间戳
|
||||
'authId' => $this->config['auth_id'], // 商户证书编号
|
||||
'bodySign' => $this->generateSign($params), // 对请求Body参数签名
|
||||
];
|
||||
|
||||
$pay_merc_auth = [
|
||||
'headerSign' => $this->generateSign($pay_merc_auth_part), // 对PayMercAuthPart参数签名
|
||||
// 其他参数直接使用PayMercAuthPart中的值,不进行修改,能用解构赋值
|
||||
...$pay_merc_auth_part,
|
||||
];
|
||||
|
||||
// 设置请求头
|
||||
$headers = [
|
||||
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
|
||||
'PayMercAuth: ' . json_encode($pay_merc_auth),
|
||||
];
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
@@ -164,7 +250,14 @@ class HuaweiPayClient
|
||||
curl_close($ch);
|
||||
|
||||
// 解析响应
|
||||
parse_str($response, $result);
|
||||
$result = json_decode($response, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
// 尝试解析为URL编码格式
|
||||
parse_str($response, $result);
|
||||
if (empty($result)) {
|
||||
throw new \Exception('响应解析失败: ' . json_last_error_msg());
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
@@ -184,16 +277,31 @@ class HuaweiPayClient
|
||||
'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'],
|
||||
'total_amount' => $params['total_amount'],
|
||||
'scene' => 'h5',
|
||||
'return_url' => $returnUrl,
|
||||
'notify_url' => $notifyUrl,
|
||||
'scene' => 'h5',
|
||||
];
|
||||
|
||||
// 可选参数,根据文档添加
|
||||
if (isset($params['currency'])) {
|
||||
$requestParams['currency'] = $params['currency'];
|
||||
}
|
||||
if (isset($params['client_ip'])) {
|
||||
$requestParams['client_ip'] = $params['client_ip'];
|
||||
}
|
||||
if (isset($params['timeout_express'])) {
|
||||
$requestParams['timeout_express'] = $params['timeout_express'];
|
||||
}
|
||||
if (isset($params['attach'])) {
|
||||
$requestParams['attach'] = $params['attach'];
|
||||
}
|
||||
|
||||
return $this->httpRequest($this->gatewayUrl, $requestParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* APP支付
|
||||
* @param array $params 支付参数
|
||||
@@ -208,11 +316,22 @@ class HuaweiPayClient
|
||||
'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'],
|
||||
'total_amount' => $params['total_amount'],
|
||||
'notify_url' => $notifyUrl,
|
||||
];
|
||||
|
||||
// 可选参数,根据文档添加
|
||||
if (isset($params['currency'])) {
|
||||
$requestParams['currency'] = $params['currency'];
|
||||
}
|
||||
if (isset($params['timeout_express'])) {
|
||||
$requestParams['timeout_express'] = $params['timeout_express'];
|
||||
}
|
||||
if (isset($params['attach'])) {
|
||||
$requestParams['attach'] = $params['attach'];
|
||||
}
|
||||
|
||||
return $this->httpRequest($this->gatewayUrl, $requestParams);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class Pay
|
||||
public function handle($param)
|
||||
{
|
||||
if ($param[ "pay_type" ] == "huaweipay") {
|
||||
if (in_array($param[ "app_type" ], [ "h5", "app", "pc", "hwapp" ])) {
|
||||
if (in_array($param[ "app_type" ], [ "h5", "app", "pc", "hwapp", 'weapp'])) {
|
||||
$pay_model = new PayModel($param[ 'site_id' ]);
|
||||
$res = $pay_model->pay($param);
|
||||
return $res;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace addon\huaweipay\model;
|
||||
|
||||
use addon\huaweipay\data\sdk\HuaweiPayClient;
|
||||
use app\exception\ApiException;
|
||||
use app\model\BaseModel;
|
||||
use app\model\system\Cron;
|
||||
use app\model\system\Pay as PayCommon;
|
||||
@@ -17,7 +18,23 @@ use think\facade\Log;
|
||||
*/
|
||||
class Pay extends BaseModel
|
||||
{
|
||||
public $hwpay_client;
|
||||
/**
|
||||
* 支付接口实例
|
||||
* @var
|
||||
*/
|
||||
private $hwpay_client;
|
||||
|
||||
/**
|
||||
* 支付配置
|
||||
* @var array|mixed
|
||||
*/
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* 站点id
|
||||
* @var
|
||||
*/
|
||||
private $site_id;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@@ -25,17 +42,16 @@ class Pay extends BaseModel
|
||||
*/
|
||||
function __construct($site_id)
|
||||
{
|
||||
try {
|
||||
// 获取华为支付参数
|
||||
$config_info = (new Config())->getPayConfig($site_id)['data']['value'];
|
||||
|
||||
if (!empty($config_info)) {
|
||||
// 初始化华为支付客户端
|
||||
$this->hwpay_client = new HuaweiPayClient($config_info);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->error('', '华为支付配置错误: ' . $e->getMessage());
|
||||
}
|
||||
$this->site_id = $site_id;
|
||||
|
||||
// 获取华为支付参数
|
||||
$config_info = (new Config())->getPayConfig($this->site_id)['data']['value'];
|
||||
$this->config = $config_info;
|
||||
if (empty($this->config)) throw new ApiException(-1, "平台未配置华为支付");
|
||||
$this->config['site_id'] = $this->site_id;
|
||||
|
||||
// 初始化华为支付客户端
|
||||
$this->hwpay_client = new HuaweiPayClient($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ class Pay extends BaseShop
|
||||
$mch_id = input("mch_id", "");//商户号, // PETALPAY.MERC_NO, 商户号
|
||||
$private_key = input("private_key", "");//商户应用私钥, // PETALPAY.MERC_PRIVATE_KEY, 商户应用私钥
|
||||
$mch_auth_id = input("mch_auth_id", "");//商户证书id, // PETALPAY.MERC_AUTH_ID, 商户证书id
|
||||
$sign_type = input("sign_type", "RSA");//商户公私钥签名类型, // PETALPAY.SIGN_TYPE, 签名类型, 默认使用RSA
|
||||
$sign_type = input("sign_type", "RSA2");//商户公私钥签名类型, // PETALPAY.SIGN_TYPE, 签名类型, 默认使用RSA
|
||||
$huawei_public_key = input("huawei_public_key", ""); //华为公钥, // PETALPAY.HW_PAY_PUBLIC_KEY_FOR_CALLBACK, 华为公钥
|
||||
$huawei_public_key_for_sessionkey = input("huawei_public_key_for_sessionkey", ""); //华为公钥, // PETALPAY.HW_PAY_PUBLIC_KEY_FOR_SESSIONKEY, 华为公钥
|
||||
$enable_app_types = input("enable_app_types", '');//支持端口 如web app,app // PETALPAY.ENABLE_APP_TYPES, 支持的支付端口
|
||||
@@ -68,7 +68,7 @@ class Pay extends BaseShop
|
||||
*/
|
||||
public function uploadHuaweiCrt()
|
||||
{
|
||||
$site_id = request()->siteid();
|
||||
$site_id = $this->site_id;
|
||||
$upload_model = new Upload($site_id);
|
||||
|
||||
$name = input("name", "");
|
||||
|
||||
Reference in New Issue
Block a user