diff --git a/app/admin/controller/Config.php b/app/admin/controller/Config.php index d1e7f23..b89d23f 100755 --- a/app/admin/controller/Config.php +++ b/app/admin/controller/Config.php @@ -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') { diff --git a/app/admin/route/app.php b/app/admin/route/app.php index c9af307..eb4f8b6 100755 --- a/app/admin/route/app.php +++ b/app/admin/route/app.php @@ -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'); diff --git a/app/admin/view/Config/miniprogram.html b/app/admin/view/Config/miniprogram.html new file mode 100644 index 0000000..624f51d --- /dev/null +++ b/app/admin/view/Config/miniprogram.html @@ -0,0 +1,370 @@ +{include file="Public:header2"/} + + +
+
+
+
+
微信小程序配置
+
+
+
+
+
+ 配置多个微信小程序,系统将根据域名自动匹配使用哪个小程序。若请求域名未匹配到对应小程序,则使用默认小程序配置。 +
+
+ + +
+ {if isset($data.miniprograms) && is_array($data.miniprograms)} + {foreach $data.miniprograms as $index => $miniprogram} +
+
+ +
+ +
+
+ +
+ +
+ +
设为默认后将用于未匹配域名的请求
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
多个域名使用英文逗号分隔,如:example.com,www.example.com
+
+ +
+ +
+ +
+
长度为2位字符,用于标识订单来源小程序
+
+ +
+ +
+
+ {if isset($merchants) && is_array($merchants)} + {foreach $merchants as $merchant_id => $merchant} +
+ +
+ {/foreach} + {else} +
暂无可用商户,请先在微信支付设置中添加商户
+ {/if} +
+
+
+ + +
+ {/foreach} + {else} + +
+
+ +
+ +
+
+ +
+ +
+ +
设为默认后将用于未匹配域名的请求
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
多个域名使用英文逗号分隔,如:example.com,www.example.com
+
+ +
+ +
+ +
+
长度为2位字符,用于标识订单来源小程序
+
+ +
+ +
+
+ {if isset($merchants) && is_array($merchants)} + {foreach $merchants as $merchant_id => $merchant} +
+ +
+ {/foreach} + {else} +
暂无可用商户,请先在微信支付设置中添加商户
+ {/if} +
+
+
+ + +
+ {/if} +
+ +
+
+ + +
+
+
+
+
+
+
+
+
+{include file="Public:footer"/} + + + \ No newline at end of file diff --git a/app/admin/view/Config/systemconfig.html b/app/admin/view/Config/systemconfig.html index 294b60a..64bb0e5 100755 --- a/app/admin/view/Config/systemconfig.html +++ b/app/admin/view/Config/systemconfig.html @@ -442,35 +442,6 @@ -
-
-
微信设置
-
-
- -
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
-
-
-
微信公众号设置
@@ -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', diff --git a/app/api/controller/Pay.php b/app/api/controller/Pay.php index 767ae47..5b90f6b 100755 --- a/app/api/controller/Pay.php +++ b/app/api/controller/Pay.php @@ -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) { diff --git a/app/api/middleware.php b/app/api/middleware.php index ec9473e..d55c413 100755 --- a/app/api/middleware.php +++ b/app/api/middleware.php @@ -1,5 +1,7 @@ '默认小程序', + 'appid' => '', + 'app_secret' => '', + 'is_default' => 1, + 'domain' => '', + 'merchants' => [] + ]; + } + + /** + * 清除小程序配置缓存 + */ + public static function clearCache() + { + self::$cache = null; + } +} \ No newline at end of file diff --git a/app/common/helper/WxPayHelper.php b/app/common/helper/WxPayHelper.php index 82d9c3e..9764a46 100644 --- a/app/common/helper/WxPayHelper.php +++ b/app/common/helper/WxPayHelper.php @@ -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,如果有小程序前缀则总长度为5(3+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; diff --git a/app/common/server/WechatRefund.php b/app/common/server/WechatRefund.php index 24c6545..f91bdb7 100755 --- a/app/common/server/WechatRefund.php +++ b/app/common/server/WechatRefund.php @@ -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']]; } - } diff --git a/app/common/server/Wx.php b/app/common/server/Wx.php index b76253a..db49cb1 100755 --- a/app/common/server/Wx.php +++ b/app/common/server/Wx.php @@ -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']; diff --git a/app/middleware.php b/app/middleware.php index 599389d..0846077 100755 --- a/app/middleware.php +++ b/app/middleware.php @@ -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 ]; diff --git a/config/menu.php b/config/menu.php index 86811a9..d842010 100755 --- a/config/menu.php +++ b/config/menu.php @@ -232,7 +232,11 @@ return [ ], [ 'url' => '/admin/wechatofficialaccount', - 'name' => '公众号支付', + 'name' => '公众号设置', + ], + [ + 'url' => '/admin/miniprogram', + 'name' => '小程序设置', ], [ 'url' => '/admin/systemconfig',