支持多个小程序账号

This commit is contained in:
manghe 2025-04-08 04:31:06 +00:00
parent 7c68e99ad6
commit b679b9396b
13 changed files with 915 additions and 146 deletions

View File

@ -73,6 +73,34 @@ class Config extends Base
return View::fetch('Config/wechatofficialaccount');
}
//微信小程序
public function miniprogram(Request $request)
{
// 获取小程序配置
$config = getConfig('miniprogram_setting');
// 获取旧的微信小程序配置,用于兼容
$wechat_setting = getConfig('wechat_setting');
// 获取所有微信支付商户信息
$merchants = [];
$weixinpay_setting = getConfig('weixinpay_setting');
if (!empty($weixinpay_setting) && !empty($weixinpay_setting['merchants'])) {
foreach ($weixinpay_setting['merchants'] as $index => $merchant) {
$merchants[$index] = [
'id' => $index,
'name' => $merchant['name'],
'mch_id' => $merchant['mch_id']
];
}
}
View::assign("data", $config);
View::assign("wechat_setting", $wechat_setting);
View::assign("merchants", $merchants);
return View::fetch('Config/miniprogram');
}
//系统设置
public function systemconfig(Request $request)
{
@ -117,6 +145,39 @@ class Config extends Base
$prefixes[] = $merchant['order_prefix'];
}
}
// 处理微信小程序配置
if ($data['key'] == 'miniprogram_setting' && isset($data['miniprograms']) && is_array($data['miniprograms'])) {
// 检查是否有默认小程序
$hasDefault = false;
$prefixes = [];
foreach ($data['miniprograms'] as $index => $miniprogram) {
if (isset($miniprogram['is_default']) && $miniprogram['is_default'] == 1) {
$hasDefault = true;
}
// 验证订单前缀
if (!empty($miniprogram['order_prefix'])) {
if (strlen($miniprogram['order_prefix']) != 2) {
return $this->renderError('小程序"' . $miniprogram['name'] . '"的订单前缀必须是2位字符');
}
if (in_array($miniprogram['order_prefix'], $prefixes)) {
return $this->renderError('订单前缀"' . $miniprogram['order_prefix'] . '"重复,每个小程序的前缀必须唯一');
}
$prefixes[] = $miniprogram['order_prefix'];
}
}
if (!$hasDefault) {
return $this->renderError('请至少设置一个默认小程序');
}
// 清除旧的微信小程序配置缓存
$redis = new RedisHelper();
($redis->getRedis())->del('config:miniprogram_setting');
}
// 处理同步地址数据格式
if ($data['key'] == 'systemconfig') {

View File

@ -271,6 +271,7 @@ Route::get('sign', 'Config/sign');//签到设置
Route::get('weixinpay', 'Config/weixinpay');
Route::get('uploadsFile', 'Config/uploads'); //上传设置
Route::get('systemconfig', 'Config/systemconfig'); //系统设置
Route::get('miniprogram', 'Config/miniprogram'); //微信小程序配置
Route::post('update', 'Config/update');
Route::get('wechatofficialaccount', 'Config/wechatofficialaccount');

View File

@ -0,0 +1,370 @@
{include file="Public:header2"/}
<body style="min-height: 100%;background-color: #fff">
<style type="text/css">
.ggg{
width:600px !important
}
.merchant-selector {
width: 100%;
max-height: 200px;
overflow-y: auto;
padding: 10px;
border: 1px solid #e6e6e6;
margin-top: 10px;
}
.merchant-item-mini {
margin-bottom: 5px;
}
</style>
<div class="layui-fluid">
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">微信小程序配置</div>
<div class="layui-card-body">
<form onsubmit="return false;">
<div class="layui-form" wid100 lay-filter="">
<div class="layui-form-item">
<div class="layui-alert layui-bg-gray">
<i class="layui-icon layui-icon-tips"></i> 配置多个微信小程序,系统将根据域名自动匹配使用哪个小程序。若请求域名未匹配到对应小程序,则使用默认小程序配置。
</div>
</div>
<input type="hidden" name="key" value="miniprogram_setting" lay-verify="required" class="layui-input">
<div id="miniprogram-container">
{if isset($data.miniprograms) && is_array($data.miniprograms)}
{foreach $data.miniprograms as $index => $miniprogram}
<div class="miniprogram-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="miniprograms[{$index}][name]" value="{$miniprogram.name}" 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">
<input type="checkbox" name="miniprograms[{$index}][is_default]" value="1" title="设为默认" lay-filter="is_default" {if isset($miniprogram.is_default) && $miniprogram.is_default==1}checked{/if}>
<div class="layui-form-mid layui-word-aux">设为默认后将用于未匹配域名的请求</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Appid</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[{$index}][appid]" value="{$miniprogram.appid}" lay-verify="required" class="layui-input" placeholder="请输入微信小程序AppID">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">AppSecret</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[{$index}][app_secret]" value="{$miniprogram.app_secret}" lay-verify="required" class="layui-input" placeholder="请输入微信小程序AppSecret">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">域名</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[{$index}][domain]" value="{$miniprogram.domain}" class="layui-input" placeholder="请输入域名,多个域名用英文逗号分隔">
</div>
<div class="layui-form-mid layui-word-aux">多个域名使用英文逗号分隔example.com,www.example.com</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">订单前缀</label>
<div class="layui-input-inline">
<input type="text" name="miniprograms[{$index}][order_prefix]" value="{$miniprogram.order_prefix|default=''}" maxlength="2" class="layui-input" placeholder="请输入订单前缀">
</div>
<div class="layui-form-mid layui-word-aux">长度为2位字符用于标识订单来源小程序</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关联商户</label>
<div class="layui-input-block">
<div class="merchant-selector">
{if isset($merchants) && is_array($merchants)}
{foreach $merchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="miniprograms[{$index}][merchants][]" value="{$merchant.id}" title="{$merchant.name}" lay-skin="primary" {if isset($miniprogram.merchants) && in_array($merchant.id, $miniprogram.merchants)}checked{/if}>
</div>
{/foreach}
{else}
<div class="layui-form-mid">暂无可用商户,请先在<a href="{:url('/admin/weixinpay')}" class="layui-btn layui-btn-xs">微信支付设置</a>中添加商户</div>
{/if}
</div>
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-miniprogram" style="position: absolute; top: 10px; right: 10px;">删除小程序</button>
</div>
{/foreach}
{else}
<!-- 默认小程序项 -->
<div class="miniprogram-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="miniprograms[0][name]" 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">
<input type="checkbox" name="miniprograms[0][is_default]" value="1" title="设为默认" lay-filter="is_default" checked>
<div class="layui-form-mid layui-word-aux">设为默认后将用于未匹配域名的请求</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Appid</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[0][appid]" value="{$wechat_setting.appid|default=''}" lay-verify="required" class="layui-input" placeholder="请输入微信小程序AppID">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">AppSecret</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[0][app_secret]" value="{$wechat_setting.appSecret|default=''}" lay-verify="required" class="layui-input" placeholder="请输入微信小程序AppSecret">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">域名</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[0][domain]" value="" class="layui-input" placeholder="请输入域名,多个域名用英文逗号分隔">
</div>
<div class="layui-form-mid layui-word-aux">多个域名使用英文逗号分隔example.com,www.example.com</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">订单前缀</label>
<div class="layui-input-inline">
<input type="text" name="miniprograms[0][order_prefix]" value="" maxlength="2" class="layui-input" placeholder="请输入订单前缀">
</div>
<div class="layui-form-mid layui-word-aux">长度为2位字符用于标识订单来源小程序</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关联商户</label>
<div class="layui-input-block">
<div class="merchant-selector">
{if isset($merchants) && is_array($merchants)}
{foreach $merchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="miniprograms[0][merchants][]" value="{$merchant.id}" title="{$merchant.name}" lay-skin="primary">
</div>
{/foreach}
{else}
<div class="layui-form-mid">暂无可用商户,请先在<a href="{:url('/admin/weixinpay')}" class="layui-btn layui-btn-xs">微信支付设置</a>中添加商户</div>
{/if}
</div>
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-miniprogram" style="position: absolute; top: 10px; right: 10px;">删除小程序</button>
</div>
{/if}
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" id="add-miniprogram">添加小程序</button>
<button class="layui-btn" onclick="return check()">确认保存</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{include file="Public:footer"/}
</body>
</html>
<script type="text/javascript">
layui.use(['layer','form','upload','element'], function(){
var $ = layui.$;
var form = layui.form;
// 监听默认小程序复选框
form.on('checkbox(is_default)', function(data){
if(data.elem.checked){
// 取消其他默认小程序
$('input[name$="[is_default]"]').not(this).prop('checked', false);
form.render('checkbox');
}
});
// 添加小程序按钮点击事件
$('#add-miniprogram').on('click', function() {
var index = $('.miniprogram-item').length;
// 构建商户选择器HTML
var merchantSelectorHtml = '';
{if isset($merchants) && is_array($merchants)}
merchantSelectorHtml = `
<div class="merchant-selector">
{foreach $merchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="miniprograms[${index}][merchants][]" value="{$merchant.id}" title="{$merchant.name}" lay-skin="primary">
</div>
{/foreach}
</div>
`;
{else}
merchantSelectorHtml = `
<div class="merchant-selector">
<div class="layui-form-mid">暂无可用商户,请先在<a href="{:url('/admin/weixinpay')}" class="layui-btn layui-btn-xs">微信支付设置</a>中添加商户</div>
</div>
`;
{/if}
var newItemHtml = `
<div class="miniprogram-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="miniprograms[${index}][name]" 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">
<input type="checkbox" name="miniprograms[${index}][is_default]" value="1" title="设为默认" lay-filter="is_default">
<div class="layui-form-mid layui-word-aux">设为默认后将用于未匹配域名的请求</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Appid</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[${index}][appid]" value="" lay-verify="required" class="layui-input" placeholder="请输入微信小程序AppID">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">AppSecret</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[${index}][app_secret]" value="" lay-verify="required" class="layui-input" placeholder="请输入微信小程序AppSecret">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">域名</label>
<div class="layui-input-inline ggg">
<input type="text" name="miniprograms[${index}][domain]" value="" class="layui-input" placeholder="请输入域名,多个域名用英文逗号分隔">
</div>
<div class="layui-form-mid layui-word-aux">多个域名使用英文逗号分隔example.com,www.example.com</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">订单前缀</label>
<div class="layui-input-inline">
<input type="text" name="miniprograms[${index}][order_prefix]" value="" maxlength="2" class="layui-input" placeholder="请输入订单前缀">
</div>
<div class="layui-form-mid layui-word-aux">长度为2位字符用于标识订单来源小程序</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关联商户</label>
<div class="layui-input-block">
${merchantSelectorHtml}
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-miniprogram" style="position: absolute; top: 10px; right: 10px;">删除小程序</button>
</div>
`;
$('#miniprogram-container').append(newItemHtml);
form.render();
});
// 删除小程序按钮点击事件(使用事件委托)
$(document).on('click', '.remove-miniprogram', function() {
if ($('.miniprogram-item').length <= 1) {
layer.msg('至少保留一个小程序配置', {icon: 2});
} else {
$(this).closest('.miniprogram-item').remove();
// 重新排序索引
$('.miniprogram-item').each(function(idx) {
$(this).find('input').each(function() {
var name = $(this).attr('name');
if (name) {
// 保持数组结构的替换
$(this).attr('name', name.replace(/miniprograms\[\d+\]/, 'miniprograms[' + idx + ']'));
}
});
});
}
});
});
function check(){
var $ = layui.$;
// 确保至少有一个默认小程序
var hasDefault = false;
$('input[name$="[is_default]"]').each(function() {
if(this.checked) {
hasDefault = true;
return false; // 跳出循环
}
});
if (!hasDefault) {
layer.msg('请至少设置一个默认小程序', {icon: 2, anim: 6, time: 2000});
return false;
}
// 验证订单前缀
var prefixes = [];
var prefixValid = true;
$('.miniprogram-item').each(function(index) {
var prefixInput = $(this).find('input[name$="[order_prefix]"]');
var prefix = prefixInput.val().trim();
var name = $(this).find('input[name$="[name]"]').val();
if (prefix) {
if (prefix.length != 2) {
layer.msg('小程序"' + name + '"的订单前缀必须是2位字符', {icon: 2, anim: 6, time: 2000});
prefixValid = false;
return false; // 跳出循环
}
if (prefixes.indexOf(prefix) !== -1) {
layer.msg('订单前缀"' + prefix + '"重复,每个小程序的前缀必须唯一', {icon: 2, anim: 6, time: 2000});
prefixValid = false;
return false; // 跳出循环
}
prefixes.push(prefix);
}
});
if (!prefixValid) {
return false;
}
// 提交表单
var url="{:url('/admin/update')}";
var load=layer.load(2);
$.post(url,$("form").serialize(),function(data){
if(data.status==1){
layer.msg(data.msg,{icon:1,time:1000},function(){
location.reload();
});
}else{
layer.msg(data.msg,{icon:2,anim:6,time:1500},function(){
layer.close(load);
});
}
})
}
</script>

View File

@ -442,35 +442,6 @@
</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="wechat-setting-form">
<input type="hidden" name="key" value="wechat_setting">
<div class="layui-form-item">
<label class="layui-form-label">Appid</label>
<div class="layui-input-block">
<input type="text" name="appid" value="{$wechat_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="{$wechat_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="wechat-setting-form">保存</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header">微信公众号设置</div>
@ -663,56 +634,6 @@
return false;
});
// 微信设置表单提交
form.on('submit(wechat-setting-form)', function (data) {
var field = data.field;
// 检查并移除空的file字段
if ('file' in field && !field.file) {
delete field.file;
}
// 提交到后台
$.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;
});
// 微信公众号设置表单提交
form.on('submit(wechatofficialaccount-setting-form)', function (data) {
var field = data.field;
// 检查并移除空的file字段
if ('file' in field && !field.file) {
delete field.file;
}
// 提交到后台
$.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;
});
// 无限赏抽奖倍数表单提交
form.on('submit(infinite-multiple-form)', function (data) {
var field = data.field;
@ -783,6 +704,31 @@
return false;
});
// 微信公众号设置表单提交
form.on('submit(wechatofficialaccount-setting-form)', function (data) {
var field = data.field;
// 检查并移除空的file字段
if ('file' in field && !field.file) {
delete field.file;
}
// 提交到后台
$.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;
});
// 初始化排行榜日期选择器
laydate.render({
elem: '#dadajuan_start_time',

View File

@ -71,21 +71,57 @@ class Pay extends Base
protected function setMerchantByOrderNum($order_num)
{
if (!$this->ish5() && strpos($order_num, 'MH_') === 0) {
// 提取订单中的商户前缀
$merchant_prefix = WxPayHelper::extractOrderPrefix($order_num);
// 提取订单中的商户前缀和小程序前缀
$prefixInfo = WxPayHelper::extractOrderPrefix($order_num);
$merchant_prefix = null;
$miniprogram_prefix = null;
if (!empty($merchant_prefix)) {
// 根据前缀获取固定的商户配置
$wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix);
// 新格式返回为数组,包含商户前缀和可能的小程序前缀
if (is_array($prefixInfo)) {
$merchant_prefix = $prefixInfo['merchant_prefix'] ?? null;
$miniprogram_prefix = $prefixInfo['miniprogram_prefix'] ?? null;
} else {
// 兼容旧格式,直接作为商户前缀
$merchant_prefix = $prefixInfo;
}
// 优先根据小程序前缀获取配置
$wxpayConfig = null;
if (!empty($miniprogram_prefix)) {
// 通过小程序前缀获取小程序配置
$miniprogramConfig = \app\common\helper\MiniprogramHelper::getMiniprogramConfigByOrderPrefix($miniprogram_prefix);
if (!empty($wxpayConfig['merchant'])) {
// 更新当前实例的商户信息
$this->appid = $wxpayConfig['appid'];
$this->merchant = $wxpayConfig['merchant']['mch_id'];
$this->secretKey = $wxpayConfig['merchant']['keys'];
return true;
if (!empty($miniprogramConfig)) {
// 使用小程序关联的商户配置
if (!empty($merchant_prefix)) {
$wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix);
// 确保使用小程序的appid
$wxpayConfig['appid'] = $miniprogramConfig['appid'];
} else {
// 没有商户前缀但有小程序配置,使用小程序默认关联的商户
$wxpayConfig = WxPayHelper::getWxPayConfig();
$wxpayConfig['appid'] = $miniprogramConfig['appid'];
}
}
}
// 如果没有通过小程序前缀获取到配置,则回退到商户前缀
if (empty($wxpayConfig) && !empty($merchant_prefix)) {
$wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix);
}
// 如果前两种方式都没有获取到配置,则使用默认配置
if (empty($wxpayConfig)) {
return false;
}
if (!empty($wxpayConfig['merchant'])) {
// 更新当前实例的商户信息
$this->appid = $wxpayConfig['appid'];
$this->merchant = $wxpayConfig['merchant']['mch_id'];
$this->secretKey = $wxpayConfig['merchant']['keys'];
return true;
}
}
return false;
}
@ -100,6 +136,9 @@ class Pay extends Base
*/
public function pay($order_num, $title, $openid, $type)
{
// 确保设置正确的商户配置
$this->setMerchantByOrderNum($order_num);
if ($type == 1) {
$order = OrderModel::getInfo(['order_num' => $order_num]);
if ($order['status'] != 1) {

View File

@ -1,5 +1,7 @@
<?php
// 这是系统自动生成的middleware定义文件
return [
//h5跨域
\app\api\middleware\Allow::class,
];

View File

@ -281,6 +281,15 @@ if (!function_exists('create_order_no')) {
// 在MH_后面添加商户前缀而不是替换MH_
$pre = 'MH_' . $merchant_prefix;
}
// 获取当前域名的微信小程序配置
$miniprogramConfig = \app\common\helper\MiniprogramHelper::getMiniprogramConfig();
// 如果小程序配置存在且有订单前缀,则添加到订单号中
if (!empty($miniprogramConfig) && !empty($miniprogramConfig['order_prefix'])) {
// 在现有前缀后面添加小程序订单前缀
$pre .= $miniprogramConfig['order_prefix'];
}
}
$order_no = $pre . date('Ymd') . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8) . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);

View File

@ -0,0 +1,218 @@
<?php
namespace app\common\helper;
use think\facade\Request;
/**
* 微信小程序配置助手类
*/
class MiniprogramHelper
{
/**
* 缓存的小程序配置
*/
private static $cache = null;
/**
* 获取当前请求域名对应的小程序配置
*
* @return array 小程序配置
*/
public static function getMiniprogramConfig()
{
// 获取当前请求的域名
$domain = Request::domain();
$host = parse_url($domain, PHP_URL_HOST);
// 根据域名获取小程序配置
return self::getMiniprogramConfigByDomain($host);
}
/**
* 根据域名获取小程序配置
*
* @param string $domain 域名
* @return array 小程序配置
*/
public static function getMiniprogramConfigByDomain($domain)
{
// 加载所有小程序配置
self::loadMiniprogramConfigs();
// 如果没有配置,返回空数组
if (empty(self::$cache) || empty(self::$cache['miniprograms'])) {
return self::getCompatibleConfig();
}
// 遍历所有小程序配置,匹配域名
foreach (self::$cache['miniprograms'] as $miniprogram) {
if (empty($miniprogram['domain'])) {
continue;
}
// 分割多个域名
$domains = explode(',', $miniprogram['domain']);
// 检查域名是否匹配
foreach ($domains as $d) {
$d = trim($d);
if (!empty($d) && (strtolower($d) === strtolower($domain) || self::domainMatch($d, $domain))) {
return $miniprogram;
}
}
}
// 如果没有匹配的域名,返回默认小程序配置
return self::getDefaultMiniprogramConfig();
}
/**
* 检查域名是否匹配
*
* @param string $pattern 配置中的域名模式
* @param string $domain 当前请求的域名
* @return bool 是否匹配
*/
private static function domainMatch($pattern, $domain)
{
// 简单的域名匹配,支持通配符 * (例如: *.example.com)
if (strpos($pattern, '*') !== false) {
$pattern = str_replace('*', '.*', $pattern);
$pattern = '/^' . str_replace('.', '\.', $pattern) . '$/i';
return preg_match($pattern, $domain);
}
return strtolower($pattern) === strtolower($domain);
}
/**
* 获取默认小程序配置
*
* @return array 默认小程序配置
*/
public static function getDefaultMiniprogramConfig()
{
// 加载所有小程序配置
self::loadMiniprogramConfigs();
// 如果没有配置,返回空数组
if (empty(self::$cache) || empty(self::$cache['miniprograms'])) {
return self::getCompatibleConfig();
}
// 查找默认小程序配置
foreach (self::$cache['miniprograms'] as $miniprogram) {
if (isset($miniprogram['is_default']) && $miniprogram['is_default'] == 1) {
return $miniprogram;
}
}
// 如果没有设置默认配置,返回第一个配置
if (!empty(self::$cache['miniprograms'])) {
return self::$cache['miniprograms'][0];
}
// 如果还是没有配置,返回兼容配置
return self::getCompatibleConfig();
}
/**
* 获取与商户ID关联的小程序配置
*
* @param string $merchantId 商户ID
* @return array|null 小程序配置或null
*/
public static function getMiniprogramConfigByMerchantId($merchantId)
{
// 加载所有小程序配置
self::loadMiniprogramConfigs();
// 如果没有配置返回null
if (empty(self::$cache) || empty(self::$cache['miniprograms'])) {
return null;
}
// 遍历所有小程序配置查找关联该商户ID的配置
foreach (self::$cache['miniprograms'] as $miniprogram) {
if (isset($miniprogram['merchants']) && is_array($miniprogram['merchants']) && in_array($merchantId, $miniprogram['merchants'])) {
return $miniprogram;
}
}
// 如果没有找到关联的小程序配置,返回默认配置
return self::getDefaultMiniprogramConfig();
}
/**
* 通过订单前缀获取小程序配置
*
* @param string $orderPrefix 订单前缀
* @return array|null 小程序配置或null
*/
public static function getMiniprogramConfigByOrderPrefix($orderPrefix)
{
// 如果订单前缀为空,返回默认配置
if (empty($orderPrefix)) {
return self::getDefaultMiniprogramConfig();
}
// 加载所有小程序配置
self::loadMiniprogramConfigs();
// 如果没有配置返回null
if (empty(self::$cache) || empty(self::$cache['miniprograms'])) {
return self::getCompatibleConfig();
}
// 遍历所有小程序配置,查找匹配订单前缀的配置
foreach (self::$cache['miniprograms'] as $miniprogram) {
if (isset($miniprogram['order_prefix']) && $miniprogram['order_prefix'] === $orderPrefix) {
return $miniprogram;
}
}
// 如果没有找到匹配的小程序配置,返回默认配置
return self::getDefaultMiniprogramConfig();
}
/**
* 加载所有小程序配置
*/
private static function loadMiniprogramConfigs()
{
// 如果已经加载过配置,直接返回
if (self::$cache !== null) {
return;
}
// 从配置中获取小程序配置
self::$cache = getConfig('miniprogram_setting');
}
/**
* 获取兼容旧版的微信小程序配置
*
* @return array 兼容的小程序配置
*/
private static function getCompatibleConfig()
{
// 微信小程序设置已移除,直接返回空配置
return [
'name' => '默认小程序',
'appid' => '',
'app_secret' => '',
'is_default' => 1,
'domain' => '',
'merchants' => []
];
}
/**
* 清除小程序配置缓存
*/
public static function clearCache()
{
self::$cache = null;
}
}

View File

@ -52,33 +52,64 @@ class WxPayHelper
*/
public static function getWxPayConfig()
{
$wechat_setting = getConfig('wechat_setting');
$weixinpay_setting = getConfig('weixinpay_setting');
// 获取当前域名对应的小程序配置
$miniprogram = \app\common\helper\MiniprogramHelper::getMiniprogramConfig();
// 选择一个随机商户
// 选择一个商户
$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'],
'order_prefix' => $old_config['order_prefix'] ?? 'MYH',
'weight' => 1
];
// 如果小程序配置了关联商户,从关联商户中随机选择一个
if (!empty($miniprogram) && !empty($miniprogram['merchants']) && is_array($miniprogram['merchants']) && count($miniprogram['merchants']) > 0) {
$weixinpay_setting = getConfig('weixinpay_setting');
if (!empty($weixinpay_setting) && !empty($weixinpay_setting['merchants'])) {
// 过滤出关联的商户
$associatedMerchants = [];
foreach ($weixinpay_setting['merchants'] as $m) {
if (in_array($m['id'] ?? '', $miniprogram['merchants'])) {
$associatedMerchants[] = $m;
}
}
// 如果有关联商户,从中随机选择一个
if (!empty($associatedMerchants)) {
$merchant = self::getRandomMerchant($associatedMerchants);
}
}
}
// 获取微信AppID
// 如果没有关联商户或无法获取关联商户,则使用默认商户选择逻辑
if (empty($merchant)) {
$weixinpay_setting = getConfig('weixinpay_setting');
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'],
'order_prefix' => $old_config['order_prefix'] ?? 'MYH',
'weight' => 1
];
}
}
}
// 获取微信AppID - 优先使用小程序配置中的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'];
if (!empty($miniprogram) && !empty($miniprogram['appid'])) {
$appid = $miniprogram['appid'];
} else {
// 兼容旧配置
$wechat_setting = getConfig('wechat_setting');
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 [
@ -95,7 +126,8 @@ class WxPayHelper
*/
public static function getFixedWxPayConfig($order_prefix = '')
{
$wechat_setting = getConfig('wechat_setting');
// 获取当前域名对应的小程序配置
$miniprogram = \app\common\helper\MiniprogramHelper::getMiniprogramConfig();
$weixinpay_setting = getConfig('weixinpay_setting');
// 尝试查找与订单前缀匹配的商户
@ -114,14 +146,20 @@ class WxPayHelper
return self::getWxPayConfig();
}
// 获取微信AppID
// 获取微信AppID - 优先使用小程序配置中的AppID
$appid = '';
if (!empty($wechat_setting) && !empty($wechat_setting['appid'])) {
$appid = $wechat_setting['appid'];
if (!empty($miniprogram) && !empty($miniprogram['appid'])) {
$appid = $miniprogram['appid'];
} else {
$old_config = getConfig('weixinpay');
if (!empty($old_config) && !empty($old_config['appid'])) {
$appid = $old_config['appid'];
// 兼容旧配置
$wechat_setting = getConfig('wechat_setting');
if (!empty($wechat_setting) && !empty($wechat_setting['appid'])) {
$appid = $wechat_setting['appid'];
} else {
$old_config = getConfig('weixinpay');
if (!empty($old_config) && !empty($old_config['appid'])) {
$appid = $old_config['appid'];
}
}
}
@ -140,10 +178,28 @@ class WxPayHelper
public static function extractOrderPrefix($order_no)
{
if (strpos($order_no, 'MH_') === 0) {
// 尝试提取MH_后的3个字符作为商户前缀
$prefix = substr($order_no, 3, 3);
if (!empty($prefix) && strlen($prefix) === 3) {
return $prefix;
// 尝试提取MH_后的字符作为商户前缀
// 商户前缀长度为3如果有小程序前缀则总长度为53+2
$totalPrefix = substr($order_no, 3, 5);
// 提取商户前缀前3位
$merchantPrefix = substr($totalPrefix, 0, 3);
// 如果有足够长度,可能包含小程序前缀
if (strlen($totalPrefix) >= 5) {
// 提取小程序前缀后2位
$miniprogramPrefix = substr($totalPrefix, 3, 2);
// 使用数组返回,包含商户前缀和小程序前缀
return [
'merchant_prefix' => $merchantPrefix,
'miniprogram_prefix' => $miniprogramPrefix
];
}
// 只有商户前缀,没有小程序前缀
if (!empty($merchantPrefix) && strlen($merchantPrefix) === 3) {
return ['merchant_prefix' => $merchantPrefix];
}
}
return null;

View File

@ -15,16 +15,53 @@ class WechatRefund extends MyController
*/
public function OrderRefund($info)
{
// 提取订单号中的商户前缀
// 提取订单号中的商户前缀和小程序前缀
$prefixInfo = null;
$merchant_prefix = null;
$miniprogram_prefix = null;
if (!empty($info['send_num']) && strpos($info['send_num'], 'MH_') === 0) {
$merchant_prefix = WxPayHelper::extractOrderPrefix($info['send_num']);
$prefixInfo = WxPayHelper::extractOrderPrefix($info['send_num']);
// 新格式返回为数组,包含商户前缀和可能的小程序前缀
if (is_array($prefixInfo)) {
$merchant_prefix = $prefixInfo['merchant_prefix'] ?? null;
$miniprogram_prefix = $prefixInfo['miniprogram_prefix'] ?? null;
} else {
// 兼容旧格式,直接作为商户前缀
$merchant_prefix = $prefixInfo;
}
}
// 根据订单中的商户前缀获取配置
$wxpayConfig = !empty($merchant_prefix)
? WxPayHelper::getFixedWxPayConfig($merchant_prefix)
: WxPayHelper::getWxPayConfig();
// 优先根据小程序前缀获取配置
$wxpayConfig = null;
if (!empty($miniprogram_prefix)) {
// 通过小程序前缀获取小程序配置
$miniprogramConfig = \app\common\helper\MiniprogramHelper::getMiniprogramConfigByOrderPrefix($miniprogram_prefix);
if (!empty($miniprogramConfig)) {
// 使用小程序关联的商户配置
if (!empty($merchant_prefix)) {
$wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix);
// 确保使用小程序的appid
$wxpayConfig['appid'] = $miniprogramConfig['appid'];
} else {
// 没有商户前缀但有小程序配置,使用小程序默认关联的商户
$wxpayConfig = WxPayHelper::getWxPayConfig();
$wxpayConfig['appid'] = $miniprogramConfig['appid'];
}
}
}
// 如果没有通过小程序前缀获取到配置,则回退到商户前缀
if (empty($wxpayConfig) && !empty($merchant_prefix)) {
$wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix);
}
// 如果前两种方式都没有获取到配置,则使用默认配置
if (empty($wxpayConfig)) {
$wxpayConfig = WxPayHelper::getWxPayConfig();
}
$appid = $wxpayConfig['appid'];
$merchant = $wxpayConfig['merchant']['mch_id'];
@ -46,7 +83,6 @@ class WechatRefund extends MyController
} else {
return ['status' => 0, 'msg' => $result['err_code_des']];
}
}

View File

@ -14,23 +14,50 @@ class Wx extends MyController
public function initialize()
{
$wechat_setting = getConfig("wechat_setting");
// 使用新的MiniprogramHelper获取当前域名对应的小程序配置
$miniprogram = \app\common\helper\MiniprogramHelper::getMiniprogramConfig();
// 获取微信支付配置
$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'];
// 设置appid和secret
if (!empty($miniprogram) && !empty($miniprogram['appid'])) {
self::$wx_appid = $miniprogram['appid'];
self::$wx_secret = $miniprogram['app_secret'];
} else {
self::$wx_appid = $wxpayConfig['appid'];
// 从旧配置或商户配置中获取appSecret
$weixinpay = getConfig("weixinpay");
self::$wx_secret = !empty($weixinpay['appSecret']) ? $weixinpay['appSecret'] : '';
// 旧的兼容代码,逐步过渡,最终应该删除
$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 = $wxpayConfig['appid'];
// 从旧配置或商户配置中获取appSecret
$weixinpay = getConfig("weixinpay");
self::$wx_secret = !empty($weixinpay['appSecret']) ? $weixinpay['appSecret'] : '';
}
}
// 使用商户信息
// 获取对应的商户信息
// 如果小程序配置了关联商户,则使用关联商户中的第一个
if (!empty($miniprogram) && !empty($miniprogram['merchants']) && is_array($miniprogram['merchants']) && count($miniprogram['merchants']) > 0) {
// 获取商户配置
$weixinpay_setting = getConfig('weixinpay_setting');
if (!empty($weixinpay_setting) && !empty($weixinpay_setting['merchants'])) {
foreach ($weixinpay_setting['merchants'] as $merchant) {
if (in_array($merchant['id'] ?? '', $miniprogram['merchants'])) {
self::$mch = $merchant['mch_id'];
self::$key = $merchant['keys'];
return; // 找到商户后直接返回
}
}
}
}
// 如果没有找到关联商户或没有配置,则使用默认商户配置
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
$merchant = $wxpayConfig['merchant'];
self::$mch = $merchant['mch_id'];
self::$key = $merchant['keys'];

View File

@ -7,6 +7,6 @@ return [
// \think\middleware\LoadLangPack::class,
// Session初始化
// \think\middleware\SessionInit::class
//h5跨域
\app\api\middleware\Allow::class
// //h5跨域
// \app\api\middleware\Allow::class
];

View File

@ -232,7 +232,11 @@ return [
],
[
'url' => '/admin/wechatofficialaccount',
'name' => '公众号支付',
'name' => '公众号设置',
],
[
'url' => '/admin/miniprogram',
'name' => '小程序设置',
],
[
'url' => '/admin/systemconfig',