This commit is contained in:
youda 2025-05-03 01:07:10 +08:00
parent 0109490d1c
commit 61c40f2eef
18 changed files with 2199 additions and 225 deletions

View File

@ -23,29 +23,20 @@ class Config extends Base
public function weixinpay(Request $request)
{
$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'] ?? '',
'order_prefix' => $old_config['order_prefix'] ?? 'MYH',
'weight' => 1
]
]
];
}
}
View::assign("key", "weixinpay_setting");
View::assign("data", $config);
return View::fetch('Config/weixinpay');
}
//支付宝支付
public function alipay(Request $request)
{
$config = getConfig('alipay_setting');
View::assign("key", "alipay_setting");
View::assign("data", $config);
return View::fetch('Config/alipay');
}
//签到设置
public function sign(Request $request)
{
@ -79,10 +70,6 @@ class Config extends Base
{
// 获取小程序配置
$config = getConfig('miniprogram_setting');
// 获取旧的微信小程序配置,用于兼容
$wechat_setting = getConfig('wechat_setting');
// 获取所有微信支付商户信息
$merchants = [];
$weixinpay_setting = getConfig('weixinpay_setting');
@ -95,13 +82,49 @@ class Config extends Base
];
}
}
View::assign("data", $config);
View::assign("wechat_setting", $wechat_setting);
View::assign("merchants", $merchants);
return View::fetch('Config/miniprogram');
}
//H5页面
public function h5(Request $request)
{
// 获取H5页面配置
$config = getConfig('h5_setting');
// 获取所有微信支付商户信息
$wxmerchants = [];
$weixinpay_setting = getConfig('weixinpay_setting');
if (!empty($weixinpay_setting) && !empty($weixinpay_setting['merchants'])) {
foreach ($weixinpay_setting['merchants'] as $index => $merchant) {
$wxmerchants[$index] = [
'id' => $index,
'name' => $merchant['name'],
'mch_id' => $merchant['mch_id']
];
}
}
// 获取所有支付宝商户信息
$alimerchants = [];
$alipay_setting = getConfig('alipay_setting');
if (!empty($alipay_setting) && !empty($alipay_setting['merchants'])) {
foreach ($alipay_setting['merchants'] as $index => $merchant) {
$alimerchants[$index] = [
'id' => $index,
'name' => $merchant['name'],
'appId' => $merchant['appId']
];
}
}
View::assign("data", $config);
View::assign("wxmerchants", $wxmerchants);
View::assign("alimerchants", $alimerchants);
return View::fetch('Config/h5');
}
//系统设置
public function systemconfig(Request $request)
{
@ -184,6 +207,39 @@ class Config extends Base
($redis->getRedis())->del('config:miniprogram_setting');
}
// 处理H5页面配置
if ($data['key'] == 'h5_setting' && isset($data['h5apps']) && is_array($data['h5apps'])) {
// 检查是否有默认H5
$hasDefault = false;
$prefixes = [];
foreach ($data['h5apps'] as $index => $h5app) {
if (isset($h5app['is_default']) && $h5app['is_default'] == 1) {
$hasDefault = true;
}
// 验证订单前缀
if (!empty($h5app['order_prefix'])) {
if (strlen($h5app['order_prefix']) != 2) {
return $this->renderError('H5应用"' . $h5app['name'] . '"的订单前缀必须是2位字符');
}
if (in_array($h5app['order_prefix'], $prefixes)) {
return $this->renderError('订单前缀"' . $h5app['order_prefix'] . '"重复每个H5应用的前缀必须唯一');
}
$prefixes[] = $h5app['order_prefix'];
}
}
if (!$hasDefault) {
return $this->renderError('请至少设置一个默认H5应用');
}
// 清除旧的H5配置缓存
$redis = new RedisHelper();
($redis->getRedis())->del('config:h5_setting');
}
// 处理同步地址数据格式
if ($data['key'] == 'systemconfig') {
$syncAddresses = [];
@ -234,6 +290,12 @@ class Config extends Base
($redis->getRedis())->del('config:tencent_sms_config');
}
if ($data['key'] == 'alipay_setting') {
//清除redis缓存alipay_setting
$redis = new RedisHelper();
($redis->getRedis())->del('config:alipay_setting');
}
$result = setConfig($data['key'], $data);
if ($result) {
return $this->renderSuccess('修改成功');

View File

@ -272,14 +272,16 @@ Route::rule('admin_operationlog', 'Admins/admin_operationlog', 'GET|POST');
Route::rule('admin_goods_log', 'Admins/admin_goods_log', 'GET|POST');
#============================
#Config.网站配置
#Config.php配置
#============================
Route::get('base', 'Config/base');
Route::get('sign', 'Config/sign');//签到设置
Route::get('weixinpay', 'Config/weixinpay');
Route::get('alipay', 'Config/alipay');
Route::get('sign', 'Config/sign');
Route::get('uploadsFile', 'Config/uploads'); //上传设置
Route::get('systemconfig', 'Config/systemconfig'); //系统设置
Route::get('miniprogram', 'Config/miniprogram'); //微信小程序配置
Route::get('miniprogram', 'Config/miniprogram'); //微信小程序
Route::get('h5', 'Config/h5'); //H5页面配置
Route::post('update', 'Config/update');
Route::get('wechatofficialaccount', 'Config/wechatofficialaccount');

View File

@ -0,0 +1,231 @@
{include file="Public:header2"/}
<body style="min-height: 100%;background-color: #fff">
<style type="text/css">
.ggg{
width:600px !important
}
</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-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="alipay_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|default=''}" lay-verify="required" class="layui-input" placeholder="请输入商户名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付宝AppId</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[{$index}][appId]" value="{$merchant.appId|default=''}" lay-verify="required" class="layui-input" placeholder="请输入支付宝AppId">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">应用私钥</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[{$index}][merchantPrivateKey]" lay-verify="required" class="layui-textarea" placeholder="请输入应用私钥" rows="4">{$merchant.merchantPrivateKey|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付宝公钥</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[{$index}][alipayPublicKey]" lay-verify="required" class="layui-textarea" placeholder="请输入支付宝公钥" rows="4">{$merchant.alipayPublicKey|default=''}</textarea>
</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>
<div class="layui-form-item">
<label class="layui-form-label">是否启用</label>
<div class="layui-input-inline ggg">
<input type="checkbox" name="merchants[{$index}][is_enabled]" value="1" lay-skin="switch" lay-text="启用|禁用" {if isset($merchant.is_enabled) && $merchant.is_enabled == 1}checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[{$index}][remark]" class="layui-textarea" placeholder="请输入备注信息">{$merchant.remark|default=''}</textarea>
</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>
{/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.name|default='默认商户'}" lay-verify="required" class="layui-input" placeholder="请输入商户名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付宝AppId</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[0][appId]" value="{$data.appId|default=''}" lay-verify="required" class="layui-input" placeholder="请输入支付宝AppId">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">应用私钥</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[0][merchantPrivateKey]" lay-verify="required" class="layui-textarea" placeholder="请输入应用私钥" rows="4">{$data.merchantPrivateKey|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付宝公钥</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[0][alipayPublicKey]" lay-verify="required" class="layui-textarea" placeholder="请输入支付宝公钥" rows="4">{$data.alipayPublicKey|default=''}</textarea>
</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>
<div class="layui-form-item">
<label class="layui-form-label">是否启用</label>
<div class="layui-input-inline ggg">
<input type="checkbox" name="merchants[0][is_enabled]" value="1" lay-skin="switch" lay-text="启用|禁用" checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[0][remark]" class="layui-textarea" placeholder="请输入备注信息">{$data.remark|default=''}</textarea>
</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>
<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>
</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;
// 添加商户按钮点击事件
$('#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">支付宝AppId</label>
<div class="layui-input-inline ggg">
<input type="text" name="merchants[\${index}][appId]" value="" lay-verify="required" class="layui-input" placeholder="请输入支付宝AppId">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">应用私钥</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[\${index}][merchantPrivateKey]" lay-verify="required" class="layui-textarea" placeholder="请输入应用私钥" rows="4"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付宝公钥</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[\${index}][alipayPublicKey]" lay-verify="required" class="layui-textarea" placeholder="请输入支付宝公钥" rows="4"></textarea>
</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>
<div class="layui-form-item">
<label class="layui-form-label">是否启用</label>
<div class="layui-input-inline ggg">
<input type="checkbox" name="merchants[\${index}][is_enabled]" value="1" lay-skin="switch" lay-text="启用|禁用" checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[\${index}][remark]" class="layui-textarea" placeholder="请输入备注信息"></textarea>
</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);
form.render();
});
// 删除商户按钮点击事件(使用事件委托)
$(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, textarea').each(function() {
var name = $(this).attr('name');
if (name) {
$(this).attr('name', name.replace(/merchants\[\d+\]/, 'merchants[' + idx + ']'));
}
});
});
}
});
});
function check(){
var $ = layui.$;
var load=layer.load(2);
var url="{:url('/admin/update')}";
$.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

@ -0,0 +1,468 @@
{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;
}
.payment-tabs {
margin-bottom: 20px;
}
.payment-tab {
padding: 10px 15px;
display: inline-block;
cursor: pointer;
border: 1px solid #e6e6e6;
margin-right: 5px;
}
.payment-tab.active {
background-color: #1E9FFF;
color: #fff;
border-color: #1E9FFF;
}
</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">H5页面配置</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> 配置多个H5应用系统将根据域名自动匹配使用哪个H5应用。若请求域名未匹配到对应H5应用则使用默认H5应用配置。
</div>
</div>
<input type="hidden" name="key" value="h5_setting" lay-verify="required" class="layui-input">
<div id="h5app-container">
{if isset($data.h5apps) && is_array($data.h5apps)}
{foreach $data.h5apps as $index => $h5app}
<div class="h5app-item" style="border: 1px solid #e6e6e6; padding: 20px; margin-bottom: 20px; position: relative;">
<div class="layui-form-item">
<label class="layui-form-label">H5名称</label>
<div class="layui-input-inline ggg">
<input type="text" name="h5apps[{$index}][name]" value="{$h5app.name|default=''}" lay-verify="required" class="layui-input" placeholder="请输入H5应用名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否默认</label>
<div class="layui-input-inline">
<input type="checkbox" name="h5apps[{$index}][is_default]" value="1" title="设为默认" lay-filter="is_default" {if isset($h5app.is_default) && $h5app.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="h5apps[{$index}][appid]" value="{$h5app.appid|default=''}" lay-verify="required" class="layui-input" placeholder="请输入H5应用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="h5apps[{$index}][app_secret]" value="{$h5app.app_secret|default=''}" lay-verify="required" class="layui-input" placeholder="请输入H5应用AppSecret">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">域名</label>
<div class="layui-input-inline ggg">
<input type="text" name="h5apps[{$index}][domain]" value="{$h5app.domain|default=''}" 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="h5apps[{$index}][order_prefix]" value="{$h5app.order_prefix|default=''}" maxlength="2" class="layui-input" placeholder="请输入订单前缀">
</div>
<div class="layui-form-mid layui-word-aux">长度为2位字符用于标识订单来源H5应用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付方式</label>
<div class="layui-input-block">
<div class="payment-tabs">
<div class="payment-tab active" data-payment="alipay">支付宝支付</div>
<div class="payment-tab" data-payment="wechat">微信支付</div>
</div>
<!-- 支付宝商户 -->
<div class="payment-content" id="alipay-payment-{$index}">
<div class="merchant-selector">
{if isset($alimerchants) && is_array($alimerchants) && count($alimerchants) > 0}
{foreach $alimerchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="h5apps[{$index}][ali_merchants][]" value="{$merchant.appId}" title="{$merchant.name}" lay-skin="primary" {if isset($h5app.ali_merchants) && in_array($merchant.appId, $h5app.ali_merchants)}checked{/if}>
</div>
{/foreach}
{else}
<div class="layui-form-mid">暂无可用支付宝商户,请先在<a href="{:url('/admin/alipay')}" class="layui-btn layui-btn-xs">支付宝支付设置</a>中添加商户</div>
{/if}
</div>
</div>
<!-- 微信支付商户 -->
<div class="payment-content" id="wechat-payment-{$index}" style="display: none;">
<div class="merchant-selector">
{if isset($wxmerchants) && is_array($wxmerchants) && count($wxmerchants) > 0}
{foreach $wxmerchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="h5apps[{$index}][wx_merchants][]" value="{$merchant.mch_id}" title="{$merchant.name}" lay-skin="primary" {if isset($h5app.wx_merchants) && in_array($merchant.mch_id, $h5app.wx_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>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-h5app" style="position: absolute; top: 10px; right: 10px;">删除H5应用</button>
</div>
{/foreach}
{else}
<!-- 默认H5应用项 -->
<div class="h5app-item" style="border: 1px solid #e6e6e6; padding: 20px; margin-bottom: 20px; position: relative;">
<div class="layui-form-item">
<label class="layui-form-label">H5名称</label>
<div class="layui-input-inline ggg">
<input type="text" name="h5apps[0][name]" value="默认H5应用" lay-verify="required" class="layui-input" placeholder="请输入H5应用名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否默认</label>
<div class="layui-input-inline">
<input type="checkbox" name="h5apps[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="h5apps[0][appid]" value="" lay-verify="required" class="layui-input" placeholder="请输入H5应用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="h5apps[0][app_secret]" value="" lay-verify="required" class="layui-input" placeholder="请输入H5应用AppSecret">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">域名</label>
<div class="layui-input-inline ggg">
<input type="text" name="h5apps[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="h5apps[0][order_prefix]" value="H5" maxlength="2" class="layui-input" placeholder="请输入订单前缀">
</div>
<div class="layui-form-mid layui-word-aux">长度为2位字符用于标识订单来源H5应用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付方式</label>
<div class="layui-input-block">
<div class="payment-tabs">
<div class="payment-tab active" data-payment="alipay">支付宝支付</div>
<div class="payment-tab" data-payment="wechat">微信支付</div>
</div>
<!-- 支付宝商户 -->
<div class="payment-content" id="alipay-payment-0">
<div class="merchant-selector">
{if isset($alimerchants) && is_array($alimerchants) && count($alimerchants) > 0}
{foreach $alimerchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="h5apps[0][ali_merchants][]" value="{$merchant.appId}" title="{$merchant.name}" lay-skin="primary">
</div>
{/foreach}
{else}
<div class="layui-form-mid">暂无可用支付宝商户,请先在<a href="{:url('/admin/alipay')}" class="layui-btn layui-btn-xs">支付宝支付设置</a>中添加商户</div>
{/if}
</div>
</div>
<!-- 微信支付商户 -->
<div class="payment-content" id="wechat-payment-0" style="display: none;">
<div class="merchant-selector">
{if isset($wxmerchants) && is_array($wxmerchants) && count($wxmerchants) > 0}
{foreach $wxmerchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="h5apps[0][wx_merchants][]" value="{$merchant.mch_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>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-h5app" style="position: absolute; top: 10px; right: 10px;">删除H5应用</button>
</div>
{/if}
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" id="add-h5app">添加H5应用</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;
// 监听默认H5应用复选框
form.on('checkbox(is_default)', function(data){
if(data.elem.checked){
// 取消其他默认H5应用
$('input[name$="[is_default]"]').not(this).prop('checked', false);
form.render('checkbox');
}
});
// 支付方式选项卡切换
$(document).on('click', '.payment-tab', function() {
var $this = $(this);
var payment = $this.data('payment');
var $parent = $this.closest('.layui-input-block');
// 切换选项卡样式
$this.addClass('active').siblings().removeClass('active');
// 切换内容显示
$parent.find('.payment-content').hide();
$parent.find('[id^=' + payment + '-payment-]').show();
});
// 添加H5应用按钮点击事件
$('#add-h5app').on('click', function() {
var index = $('.h5app-item').length;
// 构建微信商户选择器HTML
var wxMerchantSelectorHtml = '';
{if isset($wxmerchants) && is_array($wxmerchants) && count($wxmerchants) > 0}
wxMerchantSelectorHtml = `
<div class="merchant-selector">
{foreach $wxmerchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="h5apps[\${index}][wx_merchants][]" value="{$merchant.mch_id}" title="{$merchant.name}" lay-skin="primary">
</div>
{/foreach}
</div>
`;
{else}
wxMerchantSelectorHtml = `
<div class="merchant-selector">
<div class="layui-form-mid">暂无可用微信商户,请先在<a href="{:url('/admin/weixinpay')}" class="layui-btn layui-btn-xs">微信支付设置</a>中添加商户</div>
</div>
`;
{/if}
// 构建支付宝商户选择器HTML
var aliMerchantSelectorHtml = '';
{if isset($alimerchants) && is_array($alimerchants) && count($alimerchants) > 0}
aliMerchantSelectorHtml = `
<div class="merchant-selector">
{foreach $alimerchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="h5apps[\${index}][ali_merchants][]" value="{$merchant.appId}" title="{$merchant.name}" lay-skin="primary">
</div>
{/foreach}
</div>
`;
{else}
aliMerchantSelectorHtml = `
<div class="merchant-selector">
<div class="layui-form-mid">暂无可用支付宝商户,请先在<a href="{:url('/admin/alipay')}" class="layui-btn layui-btn-xs">支付宝支付设置</a>中添加商户</div>
</div>
`;
{/if}
var newItemHtml = `
<div class="h5app-item" style="border: 1px solid #e6e6e6; padding: 20px; margin-bottom: 20px; position: relative;">
<div class="layui-form-item">
<label class="layui-form-label">H5名称</label>
<div class="layui-input-inline ggg">
<input type="text" name="h5apps[\${index}][name]" value="" lay-verify="required" class="layui-input" placeholder="请输入H5应用名称">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否默认</label>
<div class="layui-input-inline">
<input type="checkbox" name="h5apps[\${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="h5apps[\${index}][appid]" value="" lay-verify="required" class="layui-input" placeholder="请输入H5应用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="h5apps[\${index}][app_secret]" value="" lay-verify="required" class="layui-input" placeholder="请输入H5应用AppSecret">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">域名</label>
<div class="layui-input-inline ggg">
<input type="text" name="h5apps[\${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="h5apps[\${index}][order_prefix]" value="" maxlength="2" class="layui-input" placeholder="请输入订单前缀">
</div>
<div class="layui-form-mid layui-word-aux">长度为2位字符用于标识订单来源H5应用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">支付方式</label>
<div class="layui-input-block">
<div class="payment-tabs">
<div class="payment-tab active" data-payment="alipay">支付宝支付</div>
<div class="payment-tab" data-payment="wechat">微信支付</div>
</div>
<!-- 支付宝商户 -->
<div class="payment-content" id="alipay-payment-\${index}">
\${aliMerchantSelectorHtml}
</div>
<!-- 微信支付商户 -->
<div class="payment-content" id="wechat-payment-\${index}" style="display: none;">
\${wxMerchantSelectorHtml}
</div>
</div>
</div>
<button type="button" class="layui-btn layui-btn-danger layui-btn-sm remove-h5app" style="position: absolute; top: 10px; right: 10px;">删除H5应用</button>
</div>
`;
$('#h5app-container').append(newItemHtml);
form.render();
});
// 删除H5应用按钮点击事件(使用事件委托)
$(document).on('click', '.remove-h5app', function() {
if ($('.h5app-item').length <= 1) {
layer.msg('至少保留一个H5应用配置', {icon: 2});
} else {
$(this).closest('.h5app-item').remove();
// 重新排序索引
$('.h5app-item').each(function(idx) {
$(this).find('input, select').each(function() {
var name = $(this).attr('name');
if (name) {
// 修改索引,同时保持数组结构
$(this).attr('name', name.replace(/h5apps\[\d+\]/, 'h5apps[' + idx + ']'));
}
});
// 更新支付选项卡的ID
$(this).find('[id^=wechat-payment-]').attr('id', 'wechat-payment-' + idx);
$(this).find('[id^=alipay-payment-]').attr('id', 'alipay-payment-' + idx);
});
}
});
});
function check(){
var $ = layui.$;
// 确保至少有一个默认H5应用
var hasDefault = false;
$('input[name$="[is_default]"]').each(function() {
if(this.checked) {
hasDefault = true;
return false; // 跳出循环
}
});
if (!hasDefault) {
layer.msg('请至少设置一个默认H5应用', {icon: 2, anim: 6, time: 2000});
return false;
}
// 检查订单前缀长度
var hasError = false;
$('input[name$="[order_prefix]"]').each(function() {
var prefix = $(this).val();
if (prefix && prefix.length !== 2) {
hasError = true;
layer.msg('订单前缀必须是2位字符', {icon: 2, anim: 6, time: 2000});
return false; // 跳出循环
}
});
if (hasError) {
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

@ -32,7 +32,7 @@
<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">
@ -100,74 +100,7 @@
<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.mch_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">

View File

@ -53,6 +53,18 @@
<input type="number" name="merchants[{$index}][weight]" value="{$merchant.weight|default='1'}" min="1" 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="checkbox" name="merchants[{$index}][is_enabled]" value="1" lay-skin="switch" lay-text="启用|禁用" {if isset($merchant.is_enabled) && $merchant.is_enabled == 1}checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[{$index}][remark]" class="layui-textarea" placeholder="请输入备注信息">{$merchant.remark|default=''}</textarea>
</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>
{/foreach}
@ -89,6 +101,18 @@
<input type="number" name="merchants[0][weight]" value="1" min="1" 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="checkbox" name="merchants[0][is_enabled]" value="1" lay-skin="switch" lay-text="启用|禁用" checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[0][remark]" class="layui-textarea" placeholder="请输入备注信息">{$data.remark|default=''}</textarea>
</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}
@ -199,6 +223,18 @@
<input type="number" name="merchants[${index}][weight]" value="1" min="1" 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="checkbox" name="merchants[${index}][is_enabled]" value="1" lay-skin="switch" lay-text="启用|禁用" checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注</label>
<div class="layui-input-inline ggg">
<textarea name="merchants[${index}][remark]" class="layui-textarea" placeholder="请输入备注信息"></textarea>
</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>
`;

View File

@ -507,7 +507,7 @@ class Infinite extends Base
if (empty($user['mobile'])) {
return $this->renderError('请先绑定手机号', [], -9);
}
$ad_id = request()->header('adid');
$goods_id = request()->param('goods_id/d', 0); #盒子ID
$prize_num = request()->param('prize_num/d', 0); #抽几发
@ -516,9 +516,10 @@ class Infinite extends Base
$use_money2_is = request()->param('use_money2_is/d', 0); #0不抵扣 1抵扣 货币2抵扣
$coupon_id = request()->param('coupon_id'); //优惠券
$is_mibao = request()->param('is_mibao/d', 0); //连击赏下 是否是抽的秘宝池 1是 0否
$pay_type = request()->param('pay_type/d', 1); //1.小程序微信支付2.h5支付宝3.app微信4.app支付宝
#盒子信息
$goods = Goodsmodel::field('id,title,imgurl_detail,type,price,status,is_shou_zhe,choujiang_xianzhi,quanju_xiangou,daily_xiangou')->where(['id' => $goods_id])
$goods = Goodsmodel::field('*')->where(['id' => $goods_id])
->find();
if (!$goods) {
return $this->renderError("盒子不存在");
@ -566,12 +567,6 @@ class Infinite extends Base
return $this->renderError('请求错误');
}
// $is_goodslist = GoodsList::field('id')
// ->where($where)
// ->find();
// if (!$is_goodslist) {
// return $this->renderError('暂无奖品信息');
// }
if ($goods['type'] == 9) {
$prize_num = intval($prize_num);
@ -584,7 +579,7 @@ class Infinite extends Base
return $this->renderError("秘宝池抽奖次数不足");
}
} else {
if ($prize_num != 1 && $prize_num != 3 && $prize_num != 5 && $prize_num != 10 && $prize_num != 50) {
if ($prize_num != 1 && $prize_num != 3 && $prize_num != 5 && $prize_num != 10) {
return $this->renderError("请求参数错误!!!");
}
}
@ -621,23 +616,36 @@ class Infinite extends Base
$num = 1;
}
$res = [];
if ($goods['quanju_xiangou'] > 0) {
// 查看有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
->update(['status' => 2]);
}
if ($goods['daily_xiangou'] > 0) {
// 查看一天内有没有未支付的订单,未支付的订单要先作废
$order_info = Order::where('goods_id', '=', $goods_id)
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('num', '=', $num)
->where('status', '=', 0)
->where('addtime', '>=', time() - 86400)
->find();
if ($order_info) {
$order_info->status = 2;
$order_info->save();
}
->update(['status' => 2]);
}
$res = [];
$order_num = create_order_no('MH_', 'order', 'order_num');
$attach = 'order_wxs';
if ($goods['type'] == 16) {
$attach = 'order_fbs';
}
$title = '购买盒子' . $goods['title'];
$payRes = \app\common\server\platform\PlatformFactory::createPay($user, $paymentResult['price'], $title, $attach, "MH_");
if ($payRes['status'] !== 1) {
Db::rollback();
#删除redis
$redis->del($redis_key);
return $this->renderError("购买失败,请刷新重试");
}
$order_num = $payRes['data']['order_no'];
#创建订单
$res[] = $order_id = Order::insertGetId([
'user_id' => $user['id'],
@ -657,7 +665,7 @@ class Infinite extends Base
'goods_imgurl' => $goods['imgurl_detail'],
'prize_num' => $prize_num,
'status' => 0,
'pay_type' => 1,#1微信 2支付宝
'pay_type' => $pay_type,#1微信 2支付宝
'order_type' => $goods['type'],
'addtime' => time(),
'coupon_id' => $paymentResult['coupon_id'],
@ -667,49 +675,14 @@ class Infinite extends Base
'ad_id' => $ad_id,
'click_id' => $user['click_id']
]);
if ($goods['quanju_xiangou'] > 0) {
// 查看有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
->where('order_num', '!=', $order_num)
->update(['status' => 2]);
}
if ($goods['daily_xiangou'] > 0) {
// 查看一天内有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
->where('addtime', '>=', time() - 86400)
->where('order_num', '!=', $order_num)
->update(['status' => 2]);
}
#微信支付金额大于0
if ($paymentResult['price'] > 0) {
$body = '购买盒子' . $goods['title'];
$attach = 'order_wxs';
if ($goods['type'] == 16) {
$attach = 'order_fbs';
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
// if ($isTest == "1") {
// Db::rollback();
// #删除redis
// $redis->del($redis_key);
// return $this->renderError("支付未开放");
// }
$payRes = (new Pay())->wxCreateOrder($order_num, $paymentResult['price'], $user['id'], $body, $attach);
if ($payRes['status'] == 1) {
#结果集
$new_data = [
'status' => 1,
'order_num' => $order_num,
'res' => $payRes['data'],
'res' => $payRes['data']['res'],
];
} else {
#删除redis

View File

@ -196,10 +196,16 @@ class Login extends Base
$logMessages[] = '用户开始登录: ' . $code;
$click_id = request()->header('clickid');
$wxServer = new \app\common\server\Wx($this->app);
$user_base = $wxServer->getOpenid($code);
$openid = $user_base['openid'];
$wxPlatform = \app\common\server\platform\PlatformFactory::create();
$openInfo = $wxPlatform->getOpenid($code);
if ($openInfo['status'] == 0) {
return $this->renderError($openInfo['msg']);
}
$openid = $openInfo['data']['openid'];
$wx_unionid = null;
if (isset($openInfo['data']['unionid'])) {
$wx_unionid = $openInfo['data']['unionid'];
}
// 添加Redis防抖锁
$redis = (new RedisHelper())->getRedis();
$lockKey = 'login:debounce:' . $openid;
@ -208,11 +214,9 @@ class Login extends Base
Log::warning(end($logMessages));
return $this->renderError('请勿频繁登录,请稍后再试');
}
$user = null;
$wx_unionid = null;
if ($user_base['unionid'] != null && $user_base['unionid'] != '') {
$wx_unionid = $user_base['unionid'];
if ($wx_unionid != null && $wx_unionid != '') {
$user = User::getInfo(['unionid' => $wx_unionid]);
}
if ($user == null) {
@ -257,7 +261,7 @@ class Login extends Base
} else {
}
// 检查UserAccount是否存在
$userAccount = UserAccount::where(['user_id' => $user['id']])->find();
if ($userAccount) {
@ -462,7 +466,7 @@ class Login extends Base
$retrieved_openid = $user_base['openid'];
$openid = $retrieved_openid;
// 添加Redis防抖锁
$redis = (new RedisHelper())->getRedis();
$lockKey = 'login:h5:debounce:' . $openid;
@ -470,7 +474,7 @@ class Login extends Base
Log::warning('用户公众号登录请求过于频繁: ' . $openid);
return $this->renderError('请勿频繁登录,请稍后再试');
}
$user = null;
if ($user_base['unionid'] != null && $user_base['unionid'] != '') {
@ -670,15 +674,18 @@ class Login extends Base
$user = $this->getUser();
$user_id = $user['id'];
$code = request()->param("code", '');
$wxServer = new \app\common\server\Wx($this->app);
$mobile = $wxServer->getMobile($code);
// return $this->renderError($mobile,[$mobile,$code]);
$wxPlatform = \app\common\server\platform\PlatformFactory::create();
$mobileRes = $wxPlatform->getMobile($code);
if ($mobileRes['status'] == 0 ) {
return $this->renderError($mobileRes['msg']);
}
if (!isset($mobileRes['data']['phoneNumber'])) {
return $this->renderError('获取手机号失败');
}
$mobile = $mobileRes['data']['phoneNumber'];
Db::startTrans();
$res = [];
// $res[] = User::where(['id' => $user['id']])->update([
// 'mobile' => $mobile,
// 'update_time' => time(),
// ]);
$data = [];
$user_mobile = User::where(['mobile' => $mobile])->find();
if ($user_mobile) {
@ -707,9 +714,7 @@ class Login extends Base
$res[] = User::where(['id' => $user['id']])->delete();
$res[] = UserAccount::where(['user_id' => $user_id])->delete();
$data['token'] = $account_token;
// $res[] = UserAccount::where(['user_id' => $user['id']])->update([
// 'token_time' => time(),
// ]);
} else {
$res[] = User::where(['id' => $user['id']])->update([
'mobile' => $mobile,
@ -864,7 +869,7 @@ class Login extends Base
$mobile = request()->param("mobile", '');
$code = request()->param("code", '');
$pid_pid = request()->param('pid', 0);
// 验证必要参数
if (empty($mobile) || empty($code)) {
$logMessages[] = '手机号或验证码为空: ' . $mobile;
@ -874,21 +879,21 @@ class Login extends Base
$logMessages[] = '用户开始手机号登录: ' . $mobile;
$click_id = request()->header('clickid');
// 验证手机验证码
$redis = (new RedisHelper())->getRedis();
$redisKey = "VerificationCode:{$mobile}";
$redisCode = $redis->get($redisKey);
if (empty($redisCode) || $redisCode != $code) {
$logMessages[] = '验证码错误: ' . $code . ',正确验证码: ' . $redisCode;
Log::error(end($logMessages));
return $this->renderError('验证码错误');
}
// 验证通过后删除Redis中的验证码
$redis->del($redisKey);
// 添加Redis防抖锁
$lockKey = 'login:mobile:debounce:' . $mobile;
if (!$redis->set($lockKey, 1, ['nx', 'ex' => 3])) {
@ -896,7 +901,7 @@ class Login extends Base
Log::warning(end($logMessages));
return $this->renderError('请勿频繁登录,请稍后再试');
}
// 检查用户是否已存在
$user = User::getInfo(['mobile' => $mobile]);
@ -926,7 +931,7 @@ class Login extends Base
$ip_city = $result['city'];
$ip_adcode = $result['adcode'];
}
// 检查UserAccount是否存在
$userAccount = UserAccount::where(['user_id' => $user['id']])->find();
if ($userAccount) {
@ -982,22 +987,22 @@ class Login extends Base
$pid = $pid_info;
}
}
// 开始事务
Db::startTrans();
// 生成随机昵称和头像
$randx = rand(1000, 9999);
$nickname = "手机用户" . $randx;
$logMessages[] = $nickname;
// 使用Identicon生成默认头像
$randx = rand(10000, 99999);
$identicon = new \Identicon\Identicon();
$imageData = $identicon->getImageData($mobile . $nickname);
$uploadResult = $this->uploader->uploadFile($imageData, "storage/users/icon/default/" . $randx . ".png");
$headimg = $uploadResult['full_url'];
// 插入用户记录
$res[] = $user_id = User::insertGetId([
'openid' => '', // 手机号注册无openid
@ -1015,7 +1020,7 @@ class Login extends Base
if ($uid) {
User::where('id', $user_id)->update(['uid' => $uid]);
}
// 如果是测试环境,给新用户赠送钻石
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
// if ($isTest == "1") {
@ -1035,12 +1040,12 @@ class Login extends Base
// }
// }
// }
// 生成账号token
$time = time();
$token_num = getRandStr(10);
$account_token = user_md5($user_id . $token_num . $time);
// 获取IP和地理位置信息
$last_login_ip1 = $this->getRealIp();
$result = $this->ip_location($last_login_ip1);
@ -1052,7 +1057,7 @@ class Login extends Base
$ip_city = $result['city'];
$ip_adcode = $result['adcode'];
}
// 插入用户账号信息
$res[] = UserAccount::insert([
'user_id' => $user_id,
@ -1078,7 +1083,7 @@ class Login extends Base
$res[] = User::changeDraw($pid, 1, 5, '获得一张抽奖券', $user_id);
}
}
if (resCheck($res)) {
// 新用户注册也要记录登录日志
UserLoginLog::recordLogin(

View File

@ -316,6 +316,28 @@ if (!function_exists('create_order_no')) {
return $order_no;
}
}
if (!function_exists('create_order_no_new')) {
/**
* 生成订单号
* @param string $pre 订单前缀
* @param string $tale 表名
* @param string $field 字段名
* @return string
*/
function create_order_no_new($pre = "", $merchant_prefix = "", $project_prefix = "", $pay_type = "")
{
$pre = $pre . $merchant_prefix . $project_prefix . $pay_type;
$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);
$r = Db::name('order')->field('id')->where(['order_num' => $order_no])->find();
if ($r) {
$order_no = create_order_no_new($pre, $merchant_prefix, $project_prefix, $pay_type);
}
return $order_no;
}
}
if (!function_exists('getPrizeCode')) {
/**
@ -724,20 +746,20 @@ if (!function_exists('order_callback')) {
// 订单支付日志
function writelog($filename, $content)
{
try {
$date = date('Y-m-d');
$file = "./pay_log/" . $date;
if (!is_dir($file)) {
mkdir($file);
try {
$date = date('Y-m-d');
$file = "./pay_log/" . $date;
if (!is_dir($file)) {
mkdir($file);
}
$file = $file . "/" . $filename . ".txt";
$content = "【收到信息" . date("Y-m-d H:i:s", time()) . "" . $content . "\r\n\r\n";
$open = fopen($file, "a");
fwrite($open, $content);
fclose($open);
} catch (\Throwable $th) {
}
$file = $file . "/" . $filename . ".txt";
$content = "【收到信息" . date("Y-m-d H:i:s", time()) . "" . $content . "\r\n\r\n";
$open = fopen($file, "a");
fwrite($open, $content);
fclose($open);
} catch (\Throwable $th) {
}
}
@ -900,10 +922,8 @@ function generatePayNotifySign($data)
foreach ($data as $key => $value) {
$stringA .= "{$key}={$value}&";
}
// 获取密钥
$wxpay_config = getConfig('wxpay');
$key = $wxpay_config['key'] ?? 'l044imysi1vsmobyrkkfnaniu5bhiupd'; // 默认密钥
//加密
$key = 'l044imysi1vsmobyrkkfnaniu5bhiupd'; // 默认密钥
// 加上密钥
$stringA .= "key=" . $key;

View File

@ -42,16 +42,7 @@ class GoodsExtend extends Base
*/
public static function getGoodsExtendByGoodsId($goodsId, $goods_type)
{
// // 生成缓存键
// $redis = (new \app\common\server\RedisHelper())->getRedis();
// $cache_key = "goods_extend:{$goodsId}:{$goods_type}";
// // 尝试从缓存获取数据
// $cached_data = $redis->get($cache_key);
// if ($cached_data) {
// return json_decode($cached_data, true);
// }
$goods_extend = self::where('goods_id', $goodsId)->find();
if (!$goods_extend) {
// 从goods_type表获取默认值
@ -73,12 +64,7 @@ class GoodsExtend extends Base
];
}
}
// // 将数据存入缓存设置5分钟过期时间
// if ($goods_extend) {
// $redis->set($cache_key, json_encode($goods_extend), 300);
// }
return $goods_extend;
}
}

View File

@ -0,0 +1,143 @@
<?php
namespace app\common\server\platform;
/**
* 多端平台支付抽象基类
*/
abstract class BasePlatform
{
protected string $code;
protected string $env;
// 构造函数可被子类继承
public function __construct()
{
$this->code = '';
$this->env = '';
}
/**
* 抽象方法:支付(子类必须实现)
* @param object $data [$user,$price,order_type]
*/
abstract public function pay(array $data): array;
/**
* 订单发货
* @param mixed $user 用户
* @param mixed $order_num 订单号
* @return void
*/
abstract public function post_order($user, $order_num): int;
/**
* 获取urlLinks
* @param mixed $userId
* @return void
*/
abstract public function generateUrlLink($userId): array;
/**
* 获取用户openId
* @param mixed $code
* @return void
*/
abstract public function getOpenid($code): array;
/**
*
* 获取手机号
* @param mixed $code
* @return void
*/
abstract public function getMobile($code = ''): array;
/**
* 普通方法(子类自动继承)
*/
public function getPlatformInfo(): array
{
return [
'code' => $this->code,
'env' => $this->env,
'time' => date('Y-m-d H:i:s')
];
}
/**
* 产生一个指定长度的随机字符串,并返回给用户
* @param type $len 产生字符串的长度
* @return string 随机字符串
*/
protected function genRandomString($len = 32)
{
$chars = array(
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9"
);
$charsLen = count($chars) - 1;
// 将数组打乱
shuffle($chars);
$output = "";
for ($i = 0; $i < $len; $i++) {
$output .= $chars[mt_rand(0, $charsLen)];
}
return $output;
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace app\common\server\platform;
/**
* 多端平台支付抽象基类
*/
class H5Platform extends BasePlatform
{
public function __construct()
{
parent::__construct();
$this->code = 'WEB_H5';
$this->env = 'h5';
}
public function pay(array $data): array
{
// echo "H5支付{$amount}分,订单号:{$orderId}\n";
// 实际项目中这里调用支付SDK
return [];
}
/**
* 发货
* @param mixed $openid
* @param mixed $access_token
* @param mixed $order_num
* @param mixed $title
* @return int
*/
public function post_order($user, $order_num): int
{
return 1;
}
/**
* 生成URL链接
* @param int $userId 用户ID
* @return array
*/
public function generateUrlLink($userId): array
{
return ['status' => 1, 'data' => ""];
}
/**
* 获取手机号
* @param string $code 手机号获取凭证
* @return array
*/
public function getMobile($code = ''): array
{
return ['status' => 1, 'data' => ""];
}
/**
* 获取openid
* @param string $code 登录凭证
* @return array
*/
public function getOpenid($code): array
{
return ['status' => 1, 'data' => ""];
}
}

View File

@ -0,0 +1,732 @@
<?php
namespace app\common\server\platform;
use think\App;
use think\facade\Db;
use think\facade\Log;
use app\common\helper\WxPayHelper;
use Exception;
/**
* 多端平台支付抽象基类
*/
class MiniProgramPlatform extends BasePlatform
{
/**
* 小程序配置
* @var
*/
public $mp_miniprogram = null;
/**
* 微信app_id
* @var
*/
public $wx_appid = null;
/**
* 微信密钥
* @var
*/
public $wx_secret = null;
/**
* 商户配置
* @var
*/
public $mp_merchant = null;
/**
* 商户id
* @var
*/
private $mch_id = null;
/**
* 商户key
* @var
*/
private $key = null;
public function __construct()
{
parent::__construct();
$this->code = 'MP-WEIXIN';
$this->env = 'miniProgram';
$this->mp_miniprogram = \app\common\helper\MiniprogramHelper::getMiniprogramConfig();
if (!empty($this->mp_miniprogram['appid'])) {
$this->wx_appid = $this->mp_miniprogram['appid'];
$this->wx_secret = $this->mp_miniprogram['app_secret'];
}
}
/**
* Summary of pay [$user,$price,title,attach]
* @param mixed $data
* @return array{data: array, status: int|array{msg: string, status: int}}
*/
public function pay(array $data): array
{
$data += ['user' => null, 'price' => 0, 'title' => '', 'attach' => 'order_wxs', 'pre' => 'MH_'];
[
'user' => $user,
'price' => $price,
'title' => $title,
'attach' => $attach,
'pre' => $pre
] = $data;
if ($user == null) {
//抛出异常
throw new \Exception('用户信息为空');
}
$project_prefix = $this->mp_miniprogram['order_prefix'];
if ($price <= 0) {
//生成订单号订单号为ML_DRAYDMP02025018.... ML_ 前缀固定3位DRA 商户号固定3位YD 项目固定2位MP0 表示微信小程序支付
$order_no = create_order_no_new($pre, "MON", $project_prefix, "MP0");
return [
'status' => 1,
'data' => [
'order_no' => $order_no,
'res' => []
]
];
}
$title = mb_substr($title, 0, 30);
$prefix = $this->GetPrefix();
//生成订单号订单号为ML_DRAYDMP02025018.... ML_ 前缀固定3位DRA 商户号固定3位YD 项目固定2位MP0 表示微信小程序支付
$order_no = create_order_no_new($pre, $prefix, $project_prefix, "MP0");
$openid = $user['openid'];
$payment_type = 'wxpay';
$order_type = $attach;
$user_id = $user ? $user['id'] : 0;
// 支付使用的随机数
$nonce_str = $this->genRandomString();
// 回调使用的随机数(与支付随机数分离)
$callback_nonce_str = $this->genRandomString();
// 生成新的支付通知URL
$notifyUrl = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_no, $callback_nonce_str);
$is_test = $user['istest'];
if ($is_test == 2) {
$price = 0.01;
}
$params['appid'] = $this->wx_appid;
$params['mch_id'] = $this->getMchId();
$params['nonce_str'] = $nonce_str;
$params['body'] = $title;
$params['attach'] = $attach;
$params['out_trade_no'] = $order_no;
$params['notify_url'] = $notifyUrl;
$params['total_fee'] = round($price * 100, 2);
$params['spbill_create_ip'] = $this->get_client_ip();
$params['trade_type'] = 'JSAPI';
$params['openid'] = $openid;
$params['sign'] = $this->MakeSign($params);
$xml = $this->data_to_xml($params);
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$response = $this->postXmlCurl($xml, $url);
$result = $this->xml_to_data($response);
if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') {
// 将通知URL和随机字符串保存到order_notify表中
Db::name('order_notify')->insert([
'order_no' => $order_no,
'notify_url' => $notifyUrl,
'nonce_str' => $callback_nonce_str,
'pay_time' => date('Y-m-d H:i:s'),
'pay_amount' => $price,
'status' => 0,
'retry_count' => 0,
'create_time' => date('Y-m-d H:i:s'),
'update_time' => date('Y-m-d H:i:s')
]);
$time = time();
$res['appId'] = $this->wx_appid;
$res["timeStamp"] = (string) $time;
$res["nonceStr"] = $nonce_str;
$res["package"] = "prepay_id=" . $result['prepay_id'];
$res["signType"] = 'MD5';
$res["paySign"] = $this->MakeSign($res);
return [
'status' => 1,
'data' => [
'order_no' => $order_no,
'res' => $res
]
];
}
// 记录支付失败日志
$error_message = '';
if (isset($result['return_code']) && $result['return_code'] === 'FAIL') {
$error_message = $result['return_msg'] ?? '未知错误';
} elseif (isset($result['result_code']) && $result['result_code'] === 'FAIL') {
$error_code = $result['err_code'] ?? '';
$error_message = $result['err_code_des'] ?? $this->error_code($error_code) ?? '未知错误';
} else {
$error_message = '微信支付接口返回异常';
}
// $redis = (new \app\common\server\RedisHelper())->getRedis();
// 记录到ThinkPHP的日志系统中
Log::error('微信支付失败: ' . json_encode([
'order_no' => $order_no,
'user_id' => $user_id,
'openid' => $openid,
'price' => $price,
'error_message' => $error_message,
'result' => $result,
'time' => date('Y-m-d H:i:s')
], JSON_UNESCAPED_UNICODE));
return ['status' => 0, 'msg' => '支付失败:' . $error_message];
}
/**
* 发货
* @param mixed $openid
* @param mixed $access_token
* @param mixed $order_num
* @param mixed $title
* @return int
*/
public function post_order($user, $order_num): int
{
$msg = "本单购买商品已发放至[小程序盒柜]";
if (strpos($order_num, 'FH_') === 0) {
$msg = "本单购买的商品正在打包,请联系客服获取物流信息";
}
$openid = $user['openid'];
// 根据订单号设置正确的商户配置
$this->setMerchantByOrderNo($order_num);
$date = new \DateTime();
// $this->appid
$access_token = $this->get_access_token();
//订单发货时间
$formattedDate = $date->format('Y-m-d\TH:i:s');
$request_url = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" . $access_token;
$param = '{
"order_key": {
"order_number_type": 1,
"mchid":"' . $this->getMchId() . '",
"out_trade_no":"' . $order_num . '"
},
"logistics_type": 4,
"delivery_mode": 1,
"shipping_list": [
{
"item_desc": "' . $msg . '"
}
],
"upload_time": "' . $formattedDate . '+08:00",
"payer": {
"openid":"' . $openid . '"
}
}';
// 记录请求参数日志
writelog('post_order_log', json_encode([
'method' => 'post_order',
'order_num' => $order_num,
'openid' => $openid,
'merchant' => $this->getMchId(),
'request_url' => $request_url,
'param' => $param,
'time' => date('Y-m-d H:i:s')
]));
$res = curlPost($request_url, $param);
$res = json_decode($res, true);
// 记录响应结果日志
writelog('post_order_log', json_encode([
'method' => 'post_order_response',
'order_num' => $order_num,
'response' => $res,
'time' => date('Y-m-d H:i:s')
]));
if ($res['errcode'] == 0 && $res['errmsg'] == 'ok') {
return 1;
} else {
// 发货失败将订单信息存入Redis等待定时任务重试
$redis = (new \app\common\server\RedisHelper())->getRedis();
$key = 'post_order:' . $order_num;
// 存储发货失败的订单信息
$orderData = [
'openid' => $openid,
'appid' => $this->wx_appid,
// 不存储access_token因为可能过期
'order_num' => $order_num,
'title' => '订单发货',
'merchant' => $this->getMchId(),
'error_code' => $res['errcode'] ?? 'unknown',
'error_msg' => $res['errmsg'] ?? 'unknown',
'retry_count' => 0,
'last_retry_time' => time(),
'create_time' => time()
];
// 存入Redis设置过期时间为3天
$redis->set($key, json_encode($orderData));
$redis->expire($key, 3 * 24 * 3600);
// 记录存入Redis的日志
writelog('post_order_log', json_encode([
'method' => 'post_order_redis_save',
'order_num' => $order_num,
'redis_key' => $key,
'data' => $orderData,
'time' => date('Y-m-d H:i:s')
]));
return 2;
}
}
/**
* 生成URL链接
* @param int $userId 用户ID
* @return array
*/
public function generateUrlLink($userId): array
{
// 获取access_token
$access_token = $this->get_access_token();
$url = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=" . $access_token;
$param = [
'path' => 'pages/index/index',
'query' => 'user_id=' . $userId,
// 'expire_type' => 0,
// 'expire_time' => time() + 7 * 86400, // 7天后过期
// 'env_version' => 'release'
];
$result = curlPost($url, json_encode($param));
$result = json_decode($result, true);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return ['status' => 0, 'msg' => $result['errmsg'] ?? '生成链接失败'];
}
return ['status' => 1, 'data' => $result];
}
/**
* 获取手机号
* @param string $code 手机号获取凭证
* @return array
*/
public function getMobile($code = ''): array
{
if (empty($code)) {
return ['status' => 0, 'msg' => '缺少code参数'];
}
// 获取access_token
$access_token = $this->get_access_token();
$url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" . $access_token;
$param = [
'code' => $code
];
$result = $this->post_curl_data($url, $param);
// $result = json_decode($result, true);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return ['status' => 0, 'msg' => $result['errmsg'] ?? '获取手机号失败'];
}
return ['status' => 1, 'data' => $result['phone_info'] ?? []];
}
/**
* 获取openid
* @param string $code 登录凭证
* @return array
*/
public function getOpenid($code): array
{
if (empty($code)) {
return ['status' => 0, 'msg' => '缺少code参数'];
}
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $this->wx_appid . "&secret=" . $this->wx_secret . "&js_code=" . $code . "&grant_type=authorization_code";
$result = $this->get_curl_data($url);
if (isset($result['errcode']) && $result['errcode'] != 0) {
return ['status' => 0, 'msg' => $result['errmsg'] ?? '获取openid失败'];
}
return ['status' => 1, 'data' => $result];
}
/**
* 根据订单号设置商户环境
*
* @param string $orderNo 订单号
* @return bool 设置成功返回 true,失败返回 false
*/
public function setMerchantByOrderNo(string $orderNo): bool
{
if (strlen($orderNo) < 6) {
return false;
}
$merchantCode = substr($orderNo, 3, 3); // 截取商户号部分
// 获取配置中的商户列表
$weixinpay_setting = getConfig('weixinpay_setting');
if (empty($weixinpay_setting['merchants']) || !is_array($weixinpay_setting['merchants'])) {
return false;
}
// 查找对应的商户配置
$merchant = null;
foreach ($weixinpay_setting['merchants'] as $item) {
if ($item['order_prefix'] == $merchantCode) {
$merchant = $item;
break;
}
}
if (!$merchant) {
return false;
}
// 设置商户环境
$this->mp_merchant = $merchant;
$this->mch_id = $merchant['mch_id'];
$this->key = $merchant['keys'];
return true;
}
/**
* 获取access_token
*/
public function get_access_token()
{
return $this->get_access_appid_token($this->wx_appid, $this->wx_secret);
}
/**
* 获取access_token
*/
public function get_access_appid_token($appid, $wx_secret)
{
$redis = (new \app\common\server\RedisHelper())->getRedis();
$redis_key = 'wx_access_token:' . $appid;
$access_token_info = $redis->get($redis_key);
if ($access_token_info) {
$access_token_info = json_decode($access_token_info, true);
if ($access_token_info['access_token_time'] > time()) {
return $access_token_info['access_token'];
}
}
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $wx_secret;
$res_access_token = $this->get_curl_data($url);
if (isset($res_access_token['errcode'])) {
throw new \Exception('获取access_token异常');
}
$access_token = $res_access_token['access_token'];
$expires_in = $res_access_token['expires_in'];
$access_token_time = time() + $expires_in;
$data = [
'access_token' => $access_token,
'access_token_time' => $access_token_time,
];
$redis->set($redis_key, json_encode($data), $expires_in - 1800);
return $access_token;
}
/**
* @param $url 请求链接
*/
public function get_curl_data($url)
{
$headerArray = array("Content-type:application/json;", "Accept:application/json");
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArray);
$response = curl_exec($ch);
if ($response) {
curl_close($ch);
return json_decode($response, true);
} else {
$error = curl_errno($ch);
curl_close($ch);
return ['errcode' => 1];
}
}
/**
* @param $url 链接
* @param $data 参数
* @return mixed
*/
public function post_curl_data($url, $data)
{
$data = json_encode($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json; charset=utf-8',
'Content-Length: ' . strlen($data)
));
$return_content = curl_exec($ch);
if ($return_content) {
curl_close($ch);
$return_content = json_decode($return_content, true);
return $return_content;
} else {
$error = curl_errno($ch);
curl_close($ch);
return ['errcode' => 1];
}
}
// region 商户初始化相关代码
/**
* 获取当前商户信息(延迟加载)
*/
public function getMerchant(): ?array
{
if ($this->mp_merchant === null) {
$this->initMerchant(); // 第一次访问时才加载
}
return $this->mp_merchant;
}
/**
* 获取 mch_id延迟加载
*/
public function getMchId(): ?string
{
if ($this->mch_id === null) {
$this->initMerchant();
}
return $this->mch_id;
}
/**
* 获取 key延迟加载
*/
public function getKey(): ?string
{
if ($this->key === null) {
$this->initMerchant();
}
return $this->key;
}
public function GetPrefix(): ?string
{
if ($this->mp_merchant === null) {
$this->initMerchant();
}
return $this->mp_merchant['order_prefix'];
}
/**
* 实际加载商户信息
*/
private function initMerchant(): void
{
if (
empty($this->mp_miniprogram) ||
empty($this->mp_miniprogram['merchants']) ||
!is_array($this->mp_miniprogram['merchants'])
) {
return;
}
$weixinpay_setting = getConfig('weixinpay_setting');
if (empty($weixinpay_setting['merchants']) || !is_array($weixinpay_setting['merchants'])) {
return;
}
$associatedMerchants = array_values(array_filter($weixinpay_setting['merchants'], function ($merchant) {
return isset($merchant['is_enabled'], $merchant['mch_id']) &&
$merchant['is_enabled'] === "1" &&
in_array($merchant['mch_id'], $this->mp_miniprogram['merchants']);
}));
if (!empty($associatedMerchants)) {
$merchant = WxPayHelper::getRandomMerchant($associatedMerchants);
$this->mp_merchant = $merchant;
$this->mch_id = $merchant['mch_id'];
$this->key = $merchant['keys'];
}
}
// endregion
/**
* 生成签名
* @return 签名
*/
public function MakeSign($params)
{
//签名步骤一:按字典序排序数组参数
ksort($params);
$string = $this->ToUrlParams($params);
//签名步骤二在string后加入KEY
$string = $string . "&key=" . $this->getKey();
//签名步骤三MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 将参数拼接为url: key=value&key=value
* @param $params
* @return string
*/
public function ToUrlParams($params)
{
$string = '';
if (!empty($params)) {
$array = array();
foreach ($params as $key => $value) {
$array[] = $key . '=' . $value;
}
$string = implode("&", $array);
}
return $string;
}
/**
* 输出xml字符
* @param $params 参数名称
* return string 返回组装的xml
**/
public function data_to_xml($params)
{
if (!is_array($params) || count($params) <= 0) {
return false;
}
$xml = "<xml>";
foreach ($params as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
/**
* 将xml转为array
* @param string $xml
* return array
*/
public function xml_to_data($xml)
{
if (!$xml) {
return false;
}
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $data;
}
/**
* 以post方式提交xml到对应的接口url
*
* @param string $xml 需要post的xml数据
* @param string $url url
* @param bool $useCert 是否需要证书,默认不需要
* @param int $second url执行超时时间默认30s
* @throws WxPayException
*/
private function postXmlCurl($xml, $url, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
return false;
}
}
/**
* 错误代码
* @param $code 服务器输出的错误代码
* return string
*/
public function error_code($code)
{
$errList = array(
'NOAUTH' => '商户未开通此接口权限',
'NOTENOUGH' => '用户帐号余额不足',
'ORDERNOTEXIST' => '订单号不存在',
'ORDERPAID' => '商户订单已支付,无需重复操作',
'ORDERCLOSED' => '当前订单已关闭,无法支付',
'SYSTEMERROR' => '系统错误!系统超时',
'APPID_NOT_EXIST' => '参数中缺少APPID',
'MCHID_NOT_EXIST' => '参数中缺少MCHID',
'APPID_MCHID_NOT_MATCH' => 'appid和mch_id不匹配',
'LACK_PARAMS' => '缺少必要的请求参数',
'OUT_TRADE_NO_USED' => '同一笔交易不能多次提交',
'SIGNERROR' => '参数签名结果不正确',
'XML_FORMAT_ERROR' => 'XML格式错误',
'REQUIRE_POST_METHOD' => '未使用post传递参数 ',
'POST_DATA_EMPTY' => 'post数据不能为空',
'NOT_UTF8' => '未使用指定编码格式',
);
if (array_key_exists($code, $errList)) {
return $errList[$code];
}
}
function get_client_ip()
{
if (isset($_SERVER['REMOTE_ADDR'])) {
$cip = $_SERVER['REMOTE_ADDR'];
} elseif (getenv("REMOTE_ADDR")) {
$cip = getenv("REMOTE_ADDR");
} elseif (getenv("HTTP_CLIENT_IP")) {
$cip = getenv("HTTP_CLIENT_IP");
} else {
$cip = "127.0.0.1";
}
return $cip;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace app\common\server\platform;
use think\facade\Request;
/**
* 多端平台支付抽象基类
*/
class PlatformFactory
{
/**
* 创建平台实例
* @param string $client 客户端类型
* @return BasePlatform 平台实例
*/
public static function create($client = null): BasePlatform
{
if (!isset($client)) {
$client = request()->header('client', 'MP-WEIXIN');
}
if ($client == "WEB_H5") {
return new H5Platform();
}
if ($client == "MP-WEIXIN") {
return new MiniProgramPlatform();
}
return new MiniProgramPlatform();
}
/**
* 创建平台实例
* @param string $client 客户端类型
* @return
*/
public static function createPay($user, $price, $title, $attach, $pre = "MH_", $pay_type = 1, $client = null): array
{
$data = [
'user' => $user,
'price' => $price,
'title' => $title,
'attach' => $attach,
'pre' => $pre,
'pay_type' => $pay_type
];
$platform = self::create($client);
return $platform->pay($data);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace app\common\service;
use app\common\server\platform\MiniProgramPlatform;
use app\common\model\User;
use app\common\model\Reward;
use app\common\model\CouponReceive;
use app\common\model\Coupon;
use app\common\model\Order;
use think\facade\Request;
use think\App;
/**
* 通用服务类
*/
class PayService
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
protected $client;
//构造函数
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
//客户端
$this->client = $this->request->header('client', 'MP-WEIXIN');
//client
$m = new MiniProgramPlatform();
}
}

View File

@ -31,7 +31,8 @@
"qcloud/cos-sdk-v5": "^2.6",
"yzalis/identicon": "^2.0",
"topthink/think-image": "^1.0",
"topthink/think-migration": "^3.1"
"topthink/think-migration": "^3.1",
"alipaysdk/easysdk": "^2.2"
},
"require-dev": {
"symfony/var-dumper": "^4.2",

219
composer.lock generated
View File

@ -4,8 +4,225 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d87efcd19adbf32a7bf40402f4f4d2e9",
"content-hash": "0f1ac8c679e075e85ffcd1bd713e8421",
"packages": [
{
"name": "adbario/php-dot-notation",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/adbario/php-dot-notation.git",
"reference": "081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/adbario/php-dot-notation/zipball/081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae",
"reference": "081e2cca50c84bfeeea2e3ef9b2c8d206d80ccae",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^5.5 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.7|^6.6|^7.5|^8.5|^9.5",
"squizlabs/php_codesniffer": "^3.6"
},
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Adbar\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Riku Särkinen",
"email": "riku@adbar.io"
}
],
"description": "PHP dot notation access to arrays",
"homepage": "https://github.com/adbario/php-dot-notation",
"keywords": [
"ArrayAccess",
"dotnotation"
],
"support": {
"issues": "https://github.com/adbario/php-dot-notation/issues",
"source": "https://github.com/adbario/php-dot-notation/tree/2.5.0"
},
"time": "2022-10-14T20:31:46+00:00"
},
{
"name": "alibabacloud/tea",
"version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/aliyun/tea-php.git",
"reference": "1619cb96c158384f72b873e1f85de8b299c9c367"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aliyun/tea-php/zipball/1619cb96c158384f72b873e1f85de8b299c9c367",
"reference": "1619cb96c158384f72b873e1f85de8b299c9c367",
"shasum": ""
},
"require": {
"adbario/php-dot-notation": "^2.4",
"ext-curl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"guzzlehttp/guzzle": "^6.3|^7.0",
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "*",
"symfony/dotenv": "^3.4",
"symfony/var-dumper": "^3.4"
},
"suggest": {
"ext-sockets": "To use client-side monitoring"
},
"type": "library",
"autoload": {
"psr-4": {
"AlibabaCloud\\Tea\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com",
"homepage": "http://www.alibabacloud.com"
}
],
"description": "Client of Tea for PHP",
"homepage": "https://www.alibabacloud.com/",
"keywords": [
"alibabacloud",
"client",
"cloud",
"tea"
],
"support": {
"issues": "https://github.com/aliyun/tea-php/issues",
"source": "https://github.com/aliyun/tea-php"
},
"time": "2023-05-16T06:43:41+00:00"
},
{
"name": "alibabacloud/tea-fileform",
"version": "0.3.4",
"source": {
"type": "git",
"url": "https://github.com/alibabacloud-sdk-php/tea-fileform.git",
"reference": "4bf0c75a045c8115aa8cb1a394bd08d8bb833181"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alibabacloud-sdk-php/tea-fileform/zipball/4bf0c75a045c8115aa8cb1a394bd08d8bb833181",
"reference": "4bf0c75a045c8115aa8cb1a394bd08d8bb833181",
"shasum": ""
},
"require": {
"alibabacloud/tea": "^3.0",
"php": ">5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|^5.4.3"
},
"type": "library",
"autoload": {
"psr-4": {
"AlibabaCloud\\Tea\\FileForm\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com"
}
],
"description": "Alibaba Cloud Tea File Library for PHP",
"support": {
"issues": "https://github.com/alibabacloud-sdk-php/tea-fileform/issues",
"source": "https://github.com/alibabacloud-sdk-php/tea-fileform/tree/0.3.4"
},
"time": "2020-12-01T07:24:35+00:00"
},
{
"name": "alipaysdk/easysdk",
"version": "2.2.3",
"source": {
"type": "git",
"url": "https://github.com/alipay/alipay-easysdk.git",
"reference": "c6008839a22a5fca08e9f8536730f7abfed522d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alipay/alipay-easysdk/zipball/c6008839a22a5fca08e9f8536730f7abfed522d5",
"reference": "c6008839a22a5fca08e9f8536730f7abfed522d5",
"shasum": ""
},
"require": {
"alibabacloud/tea": "^3.1",
"alibabacloud/tea-fileform": "^0.3.2",
"ext-ctype": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"ext-xmlwriter": "*",
"guzzlehttp/guzzle": ">=6.3",
"php": ">=7.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Alipay\\EasySDK\\": "php/src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "junying.wjy",
"email": "junying.wjy@antfin.com"
}
],
"description": "支付宝官方 Alipay Easy SDK",
"support": {
"source": "https://github.com/alipay/alipay-easysdk/tree/v2.2.3"
},
"time": "2022-11-28T14:04:57+00:00"
},
{
"name": "bacon/bacon-qr-code",
"version": "2.0.8",

View File

@ -238,10 +238,18 @@ return [
'url' => '/admin/weixinpay',
'name' => '微信商户设置',
],
[
'url' => '/admin/alipay',
'name' => '支付宝商户设置',
],
[
'url' => '/admin/miniprogram',
'name' => '小程序设置',
],
[
'url' => '/admin/h5',
'name' => 'H5设置',
],
[
'url' => '/admin/systemconfig',
'name' => '系统配置',