chore(docker): 为dev准备部署条件
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -30,3 +30,8 @@ src/cache
|
|||||||
src/temp
|
src/temp
|
||||||
src/tmp
|
src/tmp
|
||||||
src/attachments
|
src/attachments
|
||||||
|
|
||||||
|
# 数据库
|
||||||
|
mysql_db_data
|
||||||
|
redis_data
|
||||||
|
xdebug_logs
|
||||||
|
|||||||
@@ -119,9 +119,9 @@ services:
|
|||||||
- "com.docker.compose.project.working_dir=${PROJECT_NAME}"
|
- "com.docker.compose.project.working_dir=${PROJECT_NAME}"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mysql_db_data:
|
mysql_db_data: ./mysql_db_data
|
||||||
redis_data:
|
redis_data: ./redis_data
|
||||||
xdebug_logs:
|
xdebug_logs: ./xdebug_logs
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
sass-platform-net:
|
sass-platform-net:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
namespace addon\huaweipay\data\sdk;
|
namespace addon\huaweipay\data\sdk;
|
||||||
|
|
||||||
|
use app\exception\ApiException;
|
||||||
use think\facade\Log;
|
use think\facade\Log;
|
||||||
|
|
||||||
class HuaweiPayClient
|
class HuaweiPayClient
|
||||||
@@ -18,6 +19,18 @@ class HuaweiPayClient
|
|||||||
|
|
||||||
// 签名算法
|
// 签名算法
|
||||||
private $signType = 'RSA2';
|
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)
|
public function __construct($config)
|
||||||
{
|
{
|
||||||
$this->config = $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']) {
|
if (isset($config['sandbox']) && $config['sandbox']) {
|
||||||
@@ -57,17 +110,20 @@ class HuaweiPayClient
|
|||||||
}
|
}
|
||||||
$stringToSign = rtrim($stringToSign, '&');
|
$stringToSign = rtrim($stringToSign, '&');
|
||||||
|
|
||||||
|
// 签名结果
|
||||||
|
$sign = '';
|
||||||
|
|
||||||
// 根据签名类型生成签名
|
// 根据签名类型生成签名
|
||||||
switch ($this->signType) {
|
switch ($this->signType) {
|
||||||
case 'RSA2':
|
case 'RSA2':
|
||||||
$privateKey = $this->config['private_key'];
|
if (!openssl_sign($stringToSign, $sign, $this->private_key_certificate_instance, OPENSSL_ALGO_SHA256)) {
|
||||||
$privateKey = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($privateKey, 64, "\n", true) . "\n-----END PRIVATE KEY-----";
|
throw new \Exception('RSA2签名生成失败');
|
||||||
openssl_sign($stringToSign, $sign, $privateKey, OPENSSL_ALGO_SHA256);
|
}
|
||||||
break;
|
break;
|
||||||
case 'RSA':
|
case 'RSA':
|
||||||
$privateKey = $this->config['private_key'];
|
if (!openssl_sign($stringToSign, $sign, $this->private_key_certificate_instance, OPENSSL_ALGO_SHA1)) {
|
||||||
$privateKey = "-----BEGIN PRIVATE KEY-----\n" . wordwrap($privateKey, 64, "\n", true) . "\n-----END PRIVATE KEY-----";
|
throw new \Exception('RSA签名生成失败');
|
||||||
openssl_sign($stringToSign, $sign, $privateKey, OPENSSL_ALGO_SHA1);
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \Exception('不支持的签名类型');
|
throw new \Exception('不支持的签名类型');
|
||||||
@@ -85,6 +141,10 @@ class HuaweiPayClient
|
|||||||
{
|
{
|
||||||
// 保存签名
|
// 保存签名
|
||||||
$sign = $params['sign'] ?? '';
|
$sign = $params['sign'] ?? '';
|
||||||
|
if (empty($sign)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unset($params['sign']);
|
unset($params['sign']);
|
||||||
unset($params['sign_type']);
|
unset($params['sign_type']);
|
||||||
|
|
||||||
@@ -104,15 +164,14 @@ class HuaweiPayClient
|
|||||||
$stringToSign = rtrim($stringToSign, '&');
|
$stringToSign = rtrim($stringToSign, '&');
|
||||||
|
|
||||||
// 验证签名
|
// 验证签名
|
||||||
$huaweiPublicKey = $this->config['huawei_public_key'];
|
$result = 0;
|
||||||
$huaweiPublicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($huaweiPublicKey, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
|
|
||||||
|
|
||||||
switch ($this->signType) {
|
switch ($this->signType) {
|
||||||
case 'RSA2':
|
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;
|
break;
|
||||||
case 'RSA':
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \Exception('不支持的签名类型');
|
throw new \Exception('不支持的签名类型');
|
||||||
@@ -131,14 +190,20 @@ class HuaweiPayClient
|
|||||||
private function httpRequest($url, $params, $method = 'POST')
|
private function httpRequest($url, $params, $method = 'POST')
|
||||||
{
|
{
|
||||||
$ch = curl_init();
|
$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_type'] = $this->signType;
|
||||||
$params['sign'] = $this->generateSign($params);
|
$params['sign'] = $this->generateSign($params);
|
||||||
|
|
||||||
@@ -148,6 +213,27 @@ class HuaweiPayClient
|
|||||||
} else {
|
} else {
|
||||||
$url .= '?' . http_build_query($params);
|
$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_URL, $url);
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
@@ -164,7 +250,14 @@ class HuaweiPayClient
|
|||||||
curl_close($ch);
|
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;
|
return $result;
|
||||||
}
|
}
|
||||||
@@ -184,16 +277,31 @@ class HuaweiPayClient
|
|||||||
'timestamp' => date('Y-m-d H:i:s'),
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
'out_trade_no' => $params['out_trade_no'],
|
'out_trade_no' => $params['out_trade_no'],
|
||||||
'subject' => $params['subject'],
|
'subject' => $params['subject'],
|
||||||
'total_amount' => $params['total_amount'],
|
|
||||||
'body' => $params['body'],
|
'body' => $params['body'],
|
||||||
|
'total_amount' => $params['total_amount'],
|
||||||
|
'scene' => 'h5',
|
||||||
'return_url' => $returnUrl,
|
'return_url' => $returnUrl,
|
||||||
'notify_url' => $notifyUrl,
|
'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);
|
return $this->httpRequest($this->gatewayUrl, $requestParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APP支付
|
* APP支付
|
||||||
* @param array $params 支付参数
|
* @param array $params 支付参数
|
||||||
@@ -208,11 +316,22 @@ class HuaweiPayClient
|
|||||||
'timestamp' => date('Y-m-d H:i:s'),
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
'out_trade_no' => $params['out_trade_no'],
|
'out_trade_no' => $params['out_trade_no'],
|
||||||
'subject' => $params['subject'],
|
'subject' => $params['subject'],
|
||||||
'total_amount' => $params['total_amount'],
|
|
||||||
'body' => $params['body'],
|
'body' => $params['body'],
|
||||||
|
'total_amount' => $params['total_amount'],
|
||||||
'notify_url' => $notifyUrl,
|
'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);
|
return $this->httpRequest($this->gatewayUrl, $requestParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class Pay
|
|||||||
public function handle($param)
|
public function handle($param)
|
||||||
{
|
{
|
||||||
if ($param[ "pay_type" ] == "huaweipay") {
|
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' ]);
|
$pay_model = new PayModel($param[ 'site_id' ]);
|
||||||
$res = $pay_model->pay($param);
|
$res = $pay_model->pay($param);
|
||||||
return $res;
|
return $res;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
namespace addon\huaweipay\model;
|
namespace addon\huaweipay\model;
|
||||||
|
|
||||||
use addon\huaweipay\data\sdk\HuaweiPayClient;
|
use addon\huaweipay\data\sdk\HuaweiPayClient;
|
||||||
|
use app\exception\ApiException;
|
||||||
use app\model\BaseModel;
|
use app\model\BaseModel;
|
||||||
use app\model\system\Cron;
|
use app\model\system\Cron;
|
||||||
use app\model\system\Pay as PayCommon;
|
use app\model\system\Pay as PayCommon;
|
||||||
@@ -17,7 +18,23 @@ use think\facade\Log;
|
|||||||
*/
|
*/
|
||||||
class Pay extends BaseModel
|
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)
|
function __construct($site_id)
|
||||||
{
|
{
|
||||||
try {
|
$this->site_id = $site_id;
|
||||||
// 获取华为支付参数
|
|
||||||
$config_info = (new Config())->getPayConfig($site_id)['data']['value'];
|
// 获取华为支付参数
|
||||||
|
$config_info = (new Config())->getPayConfig($this->site_id)['data']['value'];
|
||||||
if (!empty($config_info)) {
|
$this->config = $config_info;
|
||||||
// 初始化华为支付客户端
|
if (empty($this->config)) throw new ApiException(-1, "平台未配置华为支付");
|
||||||
$this->hwpay_client = new HuaweiPayClient($config_info);
|
$this->config['site_id'] = $this->site_id;
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
// 初始化华为支付客户端
|
||||||
return $this->error('', '华为支付配置错误: ' . $e->getMessage());
|
$this->hwpay_client = new HuaweiPayClient($this->config);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class Pay extends BaseShop
|
|||||||
$mch_id = input("mch_id", "");//商户号, // PETALPAY.MERC_NO, 商户号
|
$mch_id = input("mch_id", "");//商户号, // PETALPAY.MERC_NO, 商户号
|
||||||
$private_key = input("private_key", "");//商户应用私钥, // PETALPAY.MERC_PRIVATE_KEY, 商户应用私钥
|
$private_key = input("private_key", "");//商户应用私钥, // PETALPAY.MERC_PRIVATE_KEY, 商户应用私钥
|
||||||
$mch_auth_id = input("mch_auth_id", "");//商户证书id, // PETALPAY.MERC_AUTH_ID, 商户证书id
|
$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 = 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, 华为公钥
|
$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, 支持的支付端口
|
$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()
|
public function uploadHuaweiCrt()
|
||||||
{
|
{
|
||||||
$site_id = request()->siteid();
|
$site_id = $this->site_id;
|
||||||
$upload_model = new Upload($site_id);
|
$upload_model = new Upload($site_id);
|
||||||
|
|
||||||
$name = input("name", "");
|
$name = input("name", "");
|
||||||
|
|||||||
Reference in New Issue
Block a user