微信支付-支持多个商户号

This commit is contained in:
manghe 2025-03-21 19:14:08 +00:00
parent 0ab7bbe02c
commit f9fb3e8916
11 changed files with 362 additions and 51 deletions

View File

@ -20,8 +20,25 @@ class Config extends Base
//微信支付
public function weixinpay(Request $request)
{
$config = getConfig('weixinpay');
View::assign("key", "weixinpay");
$config = getConfig('weixinpay_setting');
if(empty($config)) {
// 兼容旧数据,如果新配置为空,则尝试获取旧配置并转换格式
$old_config = getConfig('weixinpay');
if(!empty($old_config)) {
$config = [
'merchants' => [
[
'name' => '默认商户',
'mch_id' => $old_config['mch_id'] ?? '',
'keys' => $old_config['keys'] ?? '',
'weight' => 1
]
]
];
}
}
View::assign("key", "weixinpay_setting");
View::assign("data", $config);
return View::fetch('Config/weixinpay');
}
@ -60,10 +77,12 @@ class Config extends Base
$config = getConfig('systemconfig');
$user_config = getConfig('user_config');
$wechat_setting = getConfig('wechat_setting');
$wechatofficialaccount_setting = getConfig('wechatofficialaccount_setting');
View::assign("key", "systemconfig");
View::assign("data", $config);
View::assign("user_config", $user_config);
View::assign("wechat_setting", $wechat_setting);
View::assign("wechatofficialaccount_setting", $wechatofficialaccount_setting);
return View::fetch('Config/systemconfig');
}

View File

@ -125,6 +125,34 @@
</div>
</div>
</div>
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">微信公众号设置</div>
<div class="layui-card-body">
<form class="layui-form" action="" lay-filter="wechatofficialaccount-setting-form">
<input type="hidden" name="key" value="wechatofficialaccount_setting">
<div class="layui-form-item">
<label class="layui-form-label">Appid</label>
<div class="layui-input-block">
<input type="text" name="appid" value="{$wechatofficialaccount_setting.appid|default=''}" autocomplete="off" class="layui-input" placeholder="请输入微信公众号AppID">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">AppSecret</label>
<div class="layui-input-block">
<input type="text" name="appSecret" value="{$wechatofficialaccount_setting.appSecret|default=''}" autocomplete="off" class="layui-input" placeholder="请输入微信公众号AppSecret">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="wechatofficialaccount-setting-form">保存</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@ -219,6 +247,26 @@
return false;
});
// 微信公众号设置表单提交
form.on('submit(wechatofficialaccount-setting-form)', function (data) {
var field = data.field;
// 提交到后台
$.ajax({
url: '{:url("/admin/update")}',
type: 'post',
data: field,
success: function (res) {
if (res.status) {
layer.msg(res.msg, { icon: 1 });
} else {
layer.msg(res.msg, { icon: 2 });
}
}
});
return false;
});
// UID类型切换事件
form.on('radio(uid_type)', function (data) {
var value = this.value;

View File

@ -13,15 +13,8 @@
<form onsubmit="return false;">
<div class="layui-form" wid100 lay-filter="">
<div class="layui-form-item">
<label class="layui-form-label">Appid</label>
<div class="layui-input-inline ggg" >
<input type="text" name="appid" value="{$data.appid}" lay-verify="required" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">AppSecret</label>
<div class="layui-input-inline ggg">
<input type="text" name="appSecret" value="{$data.appSecret}" lay-verify="required" class="layui-input">
<div class="layui-alert layui-bg-gray">
<i class="layui-icon layui-icon-tips"></i> 微信公众号Appid和AppSecret已移至系统设置页面请在那里设置
</div>
</div>
<div class="layui-form-item">
@ -37,6 +30,8 @@
</div>
</div>
<input type="hidden" name="key" value="{$key}" lay-verify="required" class="layui-input">
<input type="hidden" name="appid" value="{$data.appid}" class="layui-input">
<input type="hidden" name="appSecret" value="{$data.appSecret}" class="layui-input">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" onclick="return check()">确认保存</button>

View File

@ -17,23 +17,74 @@
<i class="layui-icon layui-icon-tips"></i> 微信Appid和AppSecret已移至系统设置页面请在那里设置
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">MCHID</label>
<div class="layui-input-inline ggg">
<input type="text" name="mch_id" value="{$data.mch_id}" lay-verify="required" class="layui-input">
<input type="hidden" name="key" value="weixinpay_setting" lay-verify="required" class="layui-input">
<div id="merchants-container">
{if isset($data.merchants) && is_array($data.merchants)}
{foreach $data.merchants as $index => $merchant}
<div class="merchant-item" style="border: 1px solid #e6e6e6; padding: 20px; margin-bottom: 20px; position: relative;">
<div class="layui-form-item">
<label class="layui-form-label">商户名称</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[{$index}][name]" value="{$merchant.name}" lay-verify="required" class="layui-input" placeholder="请输入商户名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">MCHID</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[{$index}][mch_id]" value="{$merchant.mch_id}" lay-verify="required" class="layui-input" placeholder="请输入商户号">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Key</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[{$index}][keys]" value="{$merchant.keys}" lay-verify="required" class="layui-input" placeholder="请输入商户密钥">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">权重</label>
<div class="layui-input-inline ggg">
<input type="number" name="merchants[{$index}][weight]" value="{$merchant.weight|default='1'}" min="1" lay-verify="required" class="layui-input" placeholder="请输入权重,数值越大被选中的概率越高">
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-merchant" style="position: absolute; top: 10px; right: 10px;">删除商户</button>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Key</label>
<div class="layui-input-inline ggg">
<input type="text" name="keys" value="{$data.keys}" lay-verify="required" class="layui-input">
{/foreach}
{else}
<!-- 默认商户项 -->
<div class="merchant-item" style="border: 1px solid #e6e6e6; padding: 20px; margin-bottom: 20px; position: relative;">
<div class="layui-form-item">
<label class="layui-form-label">商户名称</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[0][name]" value="{$data.mch_id|default='默认商户'}" lay-verify="required" class="layui-input" placeholder="请输入商户名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">MCHID</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[0][mch_id]" value="{$data.mch_id}" lay-verify="required" class="layui-input" placeholder="请输入商户号">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Key</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[0][keys]" value="{$data.keys}" lay-verify="required" class="layui-input" placeholder="请输入商户密钥">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">权重</label>
<div class="layui-input-inline ggg">
<input type="number" name="merchants[0][weight]" value="1" min="1" lay-verify="required" class="layui-input" placeholder="请输入权重,数值越大被选中的概率越高">
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-merchant" style="position: absolute; top: 10px; right: 10px;">删除商户</button>
</div>
{/if}
</div>
<input type="hidden" name="key" value="{$key}" lay-verify="required" class="layui-input">
<input type="hidden" name="appid" value="{$data.appid}" class="layui-input">
<input type="hidden" name="appSecret" value="{$data.appSecret}" class="layui-input">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" id="add-merchant">添加商户</button>
<button class="layui-btn" onclick="return check()">确认保存</button>
</div>
</div>
@ -51,6 +102,58 @@
layui.use(['layer','form','upload','element'], function(){
var $ = layui.$;
// 添加商户按钮点击事件
$('#add-merchant').on('click', function() {
var index = $('.merchant-item').length;
var newItemHtml = `
<div class="merchant-item" style="border: 1px solid #e6e6e6; padding: 20px; margin-bottom: 20px; position: relative;">
<div class="layui-form-item">
<label class="layui-form-label">商户名称</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[${index}][name]" value="" lay-verify="required" class="layui-input" placeholder="请输入商户名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">MCHID</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[${index}][mch_id]" value="" lay-verify="required" class="layui-input" placeholder="请输入商户号">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Key</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[${index}][keys]" value="" lay-verify="required" class="layui-input" placeholder="请输入商户密钥">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">权重</label>
<div class="layui-input-inline ggg">
<input type="number" name="merchants[${index}][weight]" value="1" min="1" lay-verify="required" class="layui-input" placeholder="请输入权重,数值越大被选中的概率越高">
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-merchant" style="position: absolute; top: 10px; right: 10px;">删除商户</button>
</div>
`;
$('#merchants-container').append(newItemHtml);
});
// 删除商户按钮点击事件(使用事件委托)
$(document).on('click', '.remove-merchant', function() {
if ($('.merchant-item').length <= 1) {
layer.msg('至少保留一个商户信息', {icon: 2});
} else {
$(this).closest('.merchant-item').remove();
// 重新排序索引
$('.merchant-item').each(function(idx) {
$(this).find('input').each(function() {
var name = $(this).attr('name');
if (name) {
$(this).attr('name', name.replace(/merchants\[\d+\]/, 'merchants[' + idx + ']'));
}
});
});
}
});
});
function check(){

View File

@ -40,10 +40,15 @@ class AliNotify extends Base
public function initialize()
{
// 获取微信支付配置
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
$merchant = $wxpayConfig['merchant'];
$secret_key = getConfig('weixinpay')['keys'];
// 使用随机商户的密钥
static::$secretKey = $merchant['keys'];
// 获取公众号密钥
static::$secretKeyAccount = getConfig('wechatofficialaccount')['keys'];
static::$secretKey = $secret_key;//秘钥
}
public function ceshi()

View File

@ -40,9 +40,15 @@ class Notify extends Base
public function initialize()
{
$secret_key = getConfig('weixinpay')['keys'];
// 获取微信支付配置
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
$merchant = $wxpayConfig['merchant'];
// 使用随机商户的密钥
static::$secretKey = $merchant['keys'];
// 获取公众号密钥
static::$secretKeyAccount = getConfig('wechatofficialaccount')['keys'];
static::$secretKey = $secret_key;//秘钥
}
public function ceshi()

View File

@ -6,6 +6,7 @@ namespace app\api\controller;
use app\common\model\Order as OrderModel;
use app\common\model\ProductOrder;
use app\common\model\UserRecharge;
use app\common\helper\WxPayHelper;
use think\App;
use think\facade\Db;
@ -22,23 +23,39 @@ class Pay extends Base
{
// 获取系统微信设置
$wechat_setting = getConfig('wechat_setting');
$wechatofficialaccount_setting = getConfig('wechatofficialaccount_setting');
if ($this->ish5()) {
// 公众号配置不变
$config = getConfig('wechatofficialaccount');
// 公众号使用自己的appid
$this->appid = $config['appid'];
$this->merchant = $config['mch_id'];
$this->secretKey = $config['keys'];
} else {
$config = getConfig('weixinpay');
// 如果系统设置中存在微信配置,则优先使用
if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) {
$this->appid = $wechat_setting['appid'];
// 如果系统设置中存在微信公众号配置,则优先使用
if (!empty($wechatofficialaccount_setting) && !empty($wechatofficialaccount_setting['appid']) && !empty($wechatofficialaccount_setting['appSecret'])) {
$this->appid = $wechatofficialaccount_setting['appid'];
} else {
// 公众号使用自己的appid
$this->appid = $config['appid'];
}
$this->merchant = $config['mch_id'];
$this->secretKey = $config['keys'];
} else {
// 小程序使用随机商户
$wxpayConfig = WxPayHelper::getWxPayConfig();
if (!empty($wxpayConfig['merchant'])) {
$this->appid = $wxpayConfig['appid'];
$this->merchant = $wxpayConfig['merchant']['mch_id'];
$this->secretKey = $wxpayConfig['merchant']['keys'];
} else {
// 如果没有获取到商户信息,则使用旧方式
$config = getConfig('weixinpay');
// 如果系统设置中存在微信配置,则优先使用
if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) {
$this->appid = $wechat_setting['appid'];
} else {
$this->appid = $config['appid'];
}
$this->merchant = $config['mch_id'];
$this->secretKey = $config['keys'];
}
}
$this->noticeurl = request()->domain() . '/api/notify/order_notify';#订单回调URL

View File

@ -0,0 +1,88 @@
<?php
namespace app\common\helper;
class WxPayHelper
{
/**
* 根据权重随机获取一个商户
*
* @param array $merchants 商户数组
* @return array|null 随机选择的商户信息
*/
public static function getRandomMerchant(array $merchants)
{
if (empty($merchants)) {
return null;
}
// 只有一个商户,直接返回
if (count($merchants) === 1) {
return $merchants[0];
}
// 计算总权重
$totalWeight = 0;
foreach ($merchants as $merchant) {
$totalWeight += isset($merchant['weight']) ? (int)$merchant['weight'] : 1;
}
// 生成随机数
$randomWeight = mt_rand(1, $totalWeight);
// 根据权重选择商户
$currentWeight = 0;
foreach ($merchants as $merchant) {
$weight = isset($merchant['weight']) ? (int)$merchant['weight'] : 1;
$currentWeight += $weight;
if ($randomWeight <= $currentWeight) {
return $merchant;
}
}
// 默认返回第一个商户
return $merchants[0];
}
/**
* 获取微信支付配置
*
* @return array 包含随机选择的商户信息和appid
*/
public static function getWxPayConfig()
{
$wechat_setting = getConfig('wechat_setting');
$weixinpay_setting = getConfig('weixinpay_setting');
// 选择一个随机商户
$merchant = null;
if (!empty($weixinpay_setting) && !empty($weixinpay_setting['merchants'])) {
$merchant = self::getRandomMerchant($weixinpay_setting['merchants']);
} else {
// 兼容旧配置
$old_config = getConfig('weixinpay');
if (!empty($old_config)) {
$merchant = [
'name' => '默认商户',
'mch_id' => $old_config['mch_id'],
'keys' => $old_config['keys'],
'weight' => 1
];
}
}
// 获取微信AppID
$appid = '';
if (!empty($wechat_setting) && !empty($wechat_setting['appid'])) {
$appid = $wechat_setting['appid'];
} else if (!empty($old_config) && !empty($old_config['appid'])) {
$appid = $old_config['appid'];
}
return [
'merchant' => $merchant,
'appid' => $appid
];
}
}

View File

@ -15,8 +15,17 @@ class WechatOfficialAccount extends MyController
public function initialize()
{
$weixinpay = getConfig("wechatofficialaccount");
self::$wx_appid = $weixinpay['appid'];
self::$wx_secret = $weixinpay['appSecret'];
$wechatofficialaccount_setting = getConfig("wechatofficialaccount_setting");
// 如果系统设置中存在微信公众号配置,则优先使用
if (!empty($wechatofficialaccount_setting) && !empty($wechatofficialaccount_setting['appid']) && !empty($wechatofficialaccount_setting['appSecret'])) {
self::$wx_appid = $wechatofficialaccount_setting['appid'];
self::$wx_secret = $wechatofficialaccount_setting['appSecret'];
} else {
self::$wx_appid = $weixinpay['appid'];
self::$wx_secret = $weixinpay['appSecret'];
}
self::$mch = $weixinpay['mch_id'];
self::$key = $weixinpay['keys'];
}

View File

@ -3,6 +3,7 @@
namespace app\common\server;
use app\MyController;
use app\common\helper\WxPayHelper;
class WechatRefund extends MyController
@ -14,10 +15,11 @@ class WechatRefund extends MyController
*/
public function OrderRefund($info)
{
$weixinpay = getConfig("weixinpay");
$appid = $weixinpay['appid'];
$merchant = $weixinpay['mch_id'];
// 使用WxPayHelper获取随机商户配置
$wxpayConfig = WxPayHelper::getWxPayConfig();
$appid = $wxpayConfig['appid'];
$merchant = $wxpayConfig['merchant']['mch_id'];
$params['appid'] = $appid;
$params['mch_id'] = $merchant;
$params['nonce_str'] = $this->genRandomString();
@ -45,8 +47,10 @@ class WechatRefund extends MyController
*/
public function MakeSign($params)
{
$weixinpay = getConfig("weixinpay");
$secretKey = $weixinpay['keys'];
// 使用WxPayHelper获取随机商户配置
$wxpayConfig = WxPayHelper::getWxPayConfig();
$secretKey = $wxpayConfig['merchant']['keys'];
//签名步骤一:按字典序排序数组参数
ksort($params);
$string = $this->ToUrlParams($params);
@ -153,8 +157,19 @@ class WechatRefund extends MyController
private function postXmlCurl($xml, $url, $second = 30)
{
$path = app()->getRootPath();
$ssl_cert = $path . 'app/common/ssl/apiclient_cert.pem';
$ssl_key = $path . 'app/common/ssl/apiclient_key.pem';
// 获取商户配置
$wxpayConfig = WxPayHelper::getWxPayConfig();
$merchant = $wxpayConfig['merchant'];
// 优先使用商户配置中的证书路径,如果未设置则使用默认路径
$ssl_cert = isset($merchant['ssl_cert']) && !empty($merchant['ssl_cert'])
? $merchant['ssl_cert']
: $path . 'app/common/ssl/apiclient_cert.pem';
$ssl_key = isset($merchant['ssl_key']) && !empty($merchant['ssl_key'])
? $merchant['ssl_key']
: $path . 'app/common/ssl/apiclient_key.pem';
$ch = curl_init();
//设置超时

View File

@ -14,20 +14,26 @@ class Wx extends MyController
public function initialize()
{
$weixinpay = getConfig("weixinpay");
$wechat_setting = getConfig("wechat_setting");
// 获取微信支付配置
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
// 如果系统设置中存在微信配置,则优先使用
if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) {
self::$wx_appid = $wechat_setting['appid'];
self::$wx_secret = $wechat_setting['appSecret'];
} else {
self::$wx_appid = $weixinpay['appid'];
self::$wx_secret = $weixinpay['appSecret'];
self::$wx_appid = $wxpayConfig['appid'];
// 从旧配置或商户配置中获取appSecret
$weixinpay = getConfig("weixinpay");
self::$wx_secret = !empty($weixinpay['appSecret']) ? $weixinpay['appSecret'] : '';
}
self::$mch = $weixinpay['mch_id'];
self::$key = $weixinpay['keys'];
// 使用商户信息
$merchant = $wxpayConfig['merchant'];
self::$mch = $merchant['mch_id'];
self::$key = $merchant['keys'];
}
/**