mirror of
				https://gitee.com/technical-laohu/mpay.git
				synced 2025-11-04 08:43:42 +08:00 
			
		
		
		
	Update .gitignore rules
This commit is contained in:
		
							
								
								
									
										11
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								.env
									
									
									
									
									
								
							@@ -1,11 +0,0 @@
 | 
			
		||||
APP_DEBUG = false
 | 
			
		||||
 | 
			
		||||
DB_TYPE = mysql
 | 
			
		||||
DB_HOST = 127.0.0.1
 | 
			
		||||
DB_NAME = mpay
 | 
			
		||||
DB_USER = mpay
 | 
			
		||||
DB_PASS = 123456
 | 
			
		||||
DB_PORT = 3306
 | 
			
		||||
DB_PREFIX = mpay_
 | 
			
		||||
 | 
			
		||||
DEFAULT_LANG = zh-cn
 | 
			
		||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -12,4 +12,7 @@ vendor/*
 | 
			
		||||
runtime/*
 | 
			
		||||
 | 
			
		||||
app/*
 | 
			
		||||
!app/controller/TestController.php
 | 
			
		||||
!app/controller/TestController.php
 | 
			
		||||
 | 
			
		||||
# 忽略 config/extend 目录下所有文件
 | 
			
		||||
config/extend/*
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare (strict_types = 1);
 | 
			
		||||
 | 
			
		||||
namespace app;
 | 
			
		||||
 | 
			
		||||
use think\Service;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 应用服务类
 | 
			
		||||
 */
 | 
			
		||||
class AppService extends Service
 | 
			
		||||
{
 | 
			
		||||
    public function register()
 | 
			
		||||
    {
 | 
			
		||||
        // 服务注册
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function boot()
 | 
			
		||||
    {
 | 
			
		||||
        // 服务启动
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,93 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app;
 | 
			
		||||
 | 
			
		||||
use think\App;
 | 
			
		||||
use think\exception\ValidateException;
 | 
			
		||||
use think\Validate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 控制器基础类
 | 
			
		||||
 */
 | 
			
		||||
abstract class BaseController
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Request实例
 | 
			
		||||
     * @var \think\Request
 | 
			
		||||
     */
 | 
			
		||||
    protected $request;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 应用实例
 | 
			
		||||
     * @var \think\App
 | 
			
		||||
     */
 | 
			
		||||
    protected $app;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否批量验证
 | 
			
		||||
     * @var bool
 | 
			
		||||
     */
 | 
			
		||||
    protected $batchValidate = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 控制器中间件
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    protected $middleware = ['Auth'];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构造方法
 | 
			
		||||
     * @access public
 | 
			
		||||
     * @param  App  $app  应用对象
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(App $app)
 | 
			
		||||
    {
 | 
			
		||||
        $this->app     = $app;
 | 
			
		||||
        $this->request = $this->app->request;
 | 
			
		||||
 | 
			
		||||
        // 控制器初始化
 | 
			
		||||
        $this->initialize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 初始化
 | 
			
		||||
    protected function initialize() {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证数据
 | 
			
		||||
     * @access protected
 | 
			
		||||
     * @param  array        $data     数据
 | 
			
		||||
     * @param  string|array $validate 验证器名或者验证规则数组
 | 
			
		||||
     * @param  array        $message  提示信息
 | 
			
		||||
     * @param  bool         $batch    是否批量验证
 | 
			
		||||
     * @return array|string|true
 | 
			
		||||
     * @throws ValidateException
 | 
			
		||||
     */
 | 
			
		||||
    protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array($validate)) {
 | 
			
		||||
            $v = new Validate();
 | 
			
		||||
            $v->rule($validate);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (strpos($validate, '.')) {
 | 
			
		||||
                // 支持场景
 | 
			
		||||
                [$validate, $scene] = explode('.', $validate);
 | 
			
		||||
            }
 | 
			
		||||
            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
 | 
			
		||||
            $v     = new $class();
 | 
			
		||||
            if (!empty($scene)) {
 | 
			
		||||
                $v->scene($scene);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $v->message($message);
 | 
			
		||||
 | 
			
		||||
        // 是否批量验证
 | 
			
		||||
        if ($batch || $this->batchValidate) {
 | 
			
		||||
            $v->batch(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $v->failException(true)->check($data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app;
 | 
			
		||||
 | 
			
		||||
use think\Model;
 | 
			
		||||
use think\model\concern\SoftDelete;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @mixin \think\Model
 | 
			
		||||
 */
 | 
			
		||||
class BaseModel extends Model
 | 
			
		||||
{
 | 
			
		||||
    use SoftDelete;
 | 
			
		||||
    protected $deleteTime = 'delete_time';
 | 
			
		||||
    protected $autoWriteTimestamp = 'timestamp';
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace app;
 | 
			
		||||
 | 
			
		||||
use think\db\exception\DataNotFoundException;
 | 
			
		||||
use think\db\exception\ModelNotFoundException;
 | 
			
		||||
use think\exception\Handle;
 | 
			
		||||
use think\exception\HttpException;
 | 
			
		||||
use think\exception\HttpResponseException;
 | 
			
		||||
use think\exception\ValidateException;
 | 
			
		||||
use think\Response;
 | 
			
		||||
use Throwable;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 应用异常处理类
 | 
			
		||||
 */
 | 
			
		||||
class ExceptionHandle extends Handle
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * 不需要记录信息(日志)的异常类列表
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    protected $ignoreReport = [
 | 
			
		||||
        HttpException::class,
 | 
			
		||||
        HttpResponseException::class,
 | 
			
		||||
        ModelNotFoundException::class,
 | 
			
		||||
        DataNotFoundException::class,
 | 
			
		||||
        ValidateException::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 记录异常信息(包括日志或者其它方式记录)
 | 
			
		||||
     *
 | 
			
		||||
     * @access public
 | 
			
		||||
     * @param  Throwable $exception
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function report(Throwable $exception): void
 | 
			
		||||
    {
 | 
			
		||||
        // 使用内置的方式记录异常日志
 | 
			
		||||
        parent::report($exception);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Render an exception into an HTTP response.
 | 
			
		||||
     *
 | 
			
		||||
     * @access public
 | 
			
		||||
     * @param \think\Request   $request
 | 
			
		||||
     * @param Throwable $e
 | 
			
		||||
     * @return Response
 | 
			
		||||
     */
 | 
			
		||||
    public function render($request, Throwable $e): Response
 | 
			
		||||
    {
 | 
			
		||||
        // 添加自定义异常处理机制
 | 
			
		||||
 | 
			
		||||
        // 其他错误交给系统处理
 | 
			
		||||
        return parent::render($request, $e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace app;
 | 
			
		||||
 | 
			
		||||
// 应用请求对象类
 | 
			
		||||
class Request extends \think\Request
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
// 应用公共文件
 | 
			
		||||
function backMsg($code = 0, $msg = '', $data = []): array
 | 
			
		||||
{
 | 
			
		||||
    $back_msg = ['code' => $code, 'msg' => $msg];
 | 
			
		||||
    if ($data) {
 | 
			
		||||
        $back_msg['data'] = $data;
 | 
			
		||||
    }
 | 
			
		||||
    return $back_msg;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use app\model\Order;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
 | 
			
		||||
class ConsoleController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    // 后台主页
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        View::assign('version', 'V1');
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 管理菜单
 | 
			
		||||
    public function menu()
 | 
			
		||||
    {
 | 
			
		||||
        // 加载菜单配置
 | 
			
		||||
        $menu = \think\facade\Config::load("extend/menu", 'extend');
 | 
			
		||||
        return json($menu);
 | 
			
		||||
    }
 | 
			
		||||
    // 管理菜单
 | 
			
		||||
    public function message()
 | 
			
		||||
    {
 | 
			
		||||
        // 加载菜单配置
 | 
			
		||||
        $message = \Plugin::getNotifyMessage();
 | 
			
		||||
        if (empty($message)) {
 | 
			
		||||
            $message = [
 | 
			
		||||
                ["id" => 1, "title" => "应用更新", "children" => []],
 | 
			
		||||
                ["id" => 2, "title" => "官方消息", "children" => []],
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
        return json($message);
 | 
			
		||||
    }
 | 
			
		||||
    // 首页仪表盘
 | 
			
		||||
    public function console()
 | 
			
		||||
    {
 | 
			
		||||
        // 查询近32天的订单
 | 
			
		||||
        $orders = Order::where([['state', '=', 1], ['create_time', '>', date('Y-m-d 00:00:00', strtotime('-32 days'))]])->select();
 | 
			
		||||
        $income = $this->getRevenueData($orders);
 | 
			
		||||
        View::assign($income);
 | 
			
		||||
        $servertime = date('Y-m-d H:i:s', time());
 | 
			
		||||
        View::assign('servertime', $servertime);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 获取收入数据总览
 | 
			
		||||
    private function getRevenueData($orders)
 | 
			
		||||
    {
 | 
			
		||||
        // 时间段
 | 
			
		||||
        $month_start = date('Y-m-01 00:00:00');
 | 
			
		||||
        $month_end = date('Y-m-d 23:59:59', strtotime('last day of this month'));
 | 
			
		||||
        $week_start = date('Y-m-d 00:00:00', strtotime('monday this week'));
 | 
			
		||||
        $week_end = date('Y-m-d 23:59:59', strtotime('next monday') - 1);
 | 
			
		||||
        $yesterday_start = date('Y-m-d 00:00:00', strtotime('yesterday'));
 | 
			
		||||
        $yesterday_end = date('Y-m-d 23:59:59', strtotime('yesterday'));
 | 
			
		||||
        $today_start = date('Y-m-d 00:00:00');
 | 
			
		||||
        $today_end = date('Y-m-d 23:59:59');
 | 
			
		||||
        // 本月流水
 | 
			
		||||
        $month_income = $orders->whereBetween('create_time', [$month_start, $month_end])->column('really_price');
 | 
			
		||||
        // 本周流水
 | 
			
		||||
        $week_income = $orders->whereBetween('create_time', [$week_start, $week_end])->column('really_price');
 | 
			
		||||
        // 昨日流水
 | 
			
		||||
        $yesterday_income = $orders->whereBetween('create_time', [$yesterday_start, $yesterday_end])->column('really_price');
 | 
			
		||||
        // 今天流水
 | 
			
		||||
        $today_income = $orders->whereBetween('create_time', [$today_start, $today_end])->column('really_price');
 | 
			
		||||
        // 收入数据
 | 
			
		||||
        $income = [
 | 
			
		||||
            'month_income' => \array_sum($month_income),
 | 
			
		||||
            'week_income' => \array_sum($week_income),
 | 
			
		||||
            'yesterday_income' => \array_sum($yesterday_income),
 | 
			
		||||
            'today_income' => \array_sum($today_income),
 | 
			
		||||
        ];
 | 
			
		||||
        return $income;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use think\facade\Log;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
 | 
			
		||||
class IndexController
 | 
			
		||||
{
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        if (session('?nickname')) {
 | 
			
		||||
            $nickname = session('nickname');
 | 
			
		||||
            View::assign('nickname', $nickname);
 | 
			
		||||
        }
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    public function doc()
 | 
			
		||||
    {
 | 
			
		||||
        View::assign('domain', \request()->domain());
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,335 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use think\facade\Db;
 | 
			
		||||
use think\Request;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
use think\facade\Log;
 | 
			
		||||
use think\exception\ValidateException;
 | 
			
		||||
use think\Validate;
 | 
			
		||||
 | 
			
		||||
class InstallController
 | 
			
		||||
{
 | 
			
		||||
    private const INSTALL_LOCK_FILE = 'install.lock';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 连接数据库
 | 
			
		||||
     * @return \think\db\Connection
 | 
			
		||||
     */
 | 
			
		||||
    private function connectDatabase()
 | 
			
		||||
    {
 | 
			
		||||
        return Db::connect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 首页,检查是否已安装,若已安装则跳转到登录页,否则显示安装页面
 | 
			
		||||
     * @return \think\response\Redirect|\think\response\View
 | 
			
		||||
     */
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->checkLock()) {
 | 
			
		||||
            return redirect('User/login');
 | 
			
		||||
        }
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 安装操作,检查环境、保存数据库配置信息
 | 
			
		||||
     * @param Request $request
 | 
			
		||||
     * @return \think\response\Json
 | 
			
		||||
     */
 | 
			
		||||
    public function install(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->checkLock()) {
 | 
			
		||||
            return json(backMsg(1, '已经安装'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $envCheck = $this->checkEnvironment();
 | 
			
		||||
        if ($envCheck !== true) {
 | 
			
		||||
            return json(backMsg(1, $envCheck));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $dbConfig = $request->post();
 | 
			
		||||
        try {
 | 
			
		||||
            $this->validateDbConfig($dbConfig);
 | 
			
		||||
            $this->saveDbConfig($dbConfig);
 | 
			
		||||
            return json(backMsg(0, '配置保存成功'));
 | 
			
		||||
        } catch (ValidateException $e) {
 | 
			
		||||
            return json(backMsg(1, $e->getMessage()));
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            Log::error("保存数据库配置失败: " . $e->getMessage());
 | 
			
		||||
            return json(backMsg(1, '配置保存失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化数据库,创建表并初始化数据
 | 
			
		||||
     * @param Request $request
 | 
			
		||||
     * @return \think\response\Json
 | 
			
		||||
     */
 | 
			
		||||
    public function init(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->checkLock()) {
 | 
			
		||||
            return json(backMsg(1, '已经安装'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $dbConfig = $request->post();
 | 
			
		||||
        $startTime = microtime(true);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $this->validateInitData($dbConfig);
 | 
			
		||||
            $this->connectDatabase()->transaction(function () use ($dbConfig) {
 | 
			
		||||
                $this->createTables();
 | 
			
		||||
                $this->initData($dbConfig);
 | 
			
		||||
            });
 | 
			
		||||
            $this->setLock();
 | 
			
		||||
            $endTime = microtime(true);
 | 
			
		||||
            Log::info("数据库初始化完成,耗时: " . ($endTime - $startTime) . " 秒");
 | 
			
		||||
            return json(backMsg(0, '安装成功'));
 | 
			
		||||
        } catch (ValidateException $e) {
 | 
			
		||||
            return json(backMsg(1, $e->getMessage()));
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            Log::error("数据库初始化失败: " . $e->getMessage());
 | 
			
		||||
            return json(backMsg(1, '数据库初始化失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查环境,包括 PHP 版本、文件上传写入权限、Fileinfo 扩展
 | 
			
		||||
     * @return bool|string
 | 
			
		||||
     */
 | 
			
		||||
    private function checkEnvironment()
 | 
			
		||||
    {
 | 
			
		||||
        if (version_compare(PHP_VERSION, '8.0', '<')) {
 | 
			
		||||
            return 'PHP 版本必须大于等于 8.0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!is_writable(sys_get_temp_dir())) {
 | 
			
		||||
            return '文件上传目录没有写入权限';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!extension_loaded('fileinfo')) {
 | 
			
		||||
            return 'Fileinfo 扩展未安装';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证数据库配置信息
 | 
			
		||||
     * @param array $dbConfig
 | 
			
		||||
     * @throws ValidateException
 | 
			
		||||
     */
 | 
			
		||||
    private function validateDbConfig(array $dbConfig)
 | 
			
		||||
    {
 | 
			
		||||
        $validate = new Validate();
 | 
			
		||||
        $rule = [
 | 
			
		||||
            'host' => 'require',
 | 
			
		||||
            'name' => 'require',
 | 
			
		||||
            'user' => 'require',
 | 
			
		||||
            'pass' => 'require',
 | 
			
		||||
            'port' => 'require|integer',
 | 
			
		||||
        ];
 | 
			
		||||
        if (!$validate->rule($rule)->check($dbConfig)) {
 | 
			
		||||
            throw new ValidateException($validate->getError());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证初始化数据信息
 | 
			
		||||
     * @param array $dbConfig
 | 
			
		||||
     * @throws ValidateException
 | 
			
		||||
     */
 | 
			
		||||
    private function validateInitData(array $dbConfig)
 | 
			
		||||
    {
 | 
			
		||||
        $validate = new Validate();
 | 
			
		||||
        $rule = [
 | 
			
		||||
            'nickname' => 'require',
 | 
			
		||||
            'username' => 'require',
 | 
			
		||||
            'password' => 'require'
 | 
			
		||||
        ];
 | 
			
		||||
        if (!$validate->rule($rule)->check($dbConfig)) {
 | 
			
		||||
            throw new ValidateException($validate->getError());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 保存数据库配置信息到 .env 文件
 | 
			
		||||
     * @param array $dbConfig
 | 
			
		||||
     * @throws \Exception
 | 
			
		||||
     */
 | 
			
		||||
    private function saveDbConfig(array $dbConfig)
 | 
			
		||||
    {
 | 
			
		||||
        $envPath = app()->getRootPath() . '.env';
 | 
			
		||||
        $envContent = $this->generateEnvContent($dbConfig);
 | 
			
		||||
        if (file_put_contents($envPath, $envContent) === false) {
 | 
			
		||||
            throw new \Exception("无法写入 .env 文件");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成 .env 文件内容
 | 
			
		||||
     * @param array $dbConfig
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    private function generateEnvContent(array $dbConfig): string
 | 
			
		||||
    {
 | 
			
		||||
        return <<<EOT
 | 
			
		||||
APP_DEBUG = false
 | 
			
		||||
 | 
			
		||||
DB_TYPE = mysql
 | 
			
		||||
DB_HOST = {$dbConfig['host']}
 | 
			
		||||
DB_NAME = {$dbConfig['name']}
 | 
			
		||||
DB_USER = {$dbConfig['user']}
 | 
			
		||||
DB_PASS = {$dbConfig['pass']}
 | 
			
		||||
DB_PORT = {$dbConfig['port']}
 | 
			
		||||
DB_PREFIX = mpay_
 | 
			
		||||
 | 
			
		||||
DEFAULT_LANG = zh-cn
 | 
			
		||||
EOT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建数据库表
 | 
			
		||||
     * @throws \Exception
 | 
			
		||||
     */
 | 
			
		||||
    private function createTables()
 | 
			
		||||
    {
 | 
			
		||||
        $db = $this->connectDatabase();
 | 
			
		||||
        $tables = $this->getTableCreationSqls();
 | 
			
		||||
 | 
			
		||||
        foreach ($tables as $tableName => $sql) {
 | 
			
		||||
            try {
 | 
			
		||||
                $db->execute("DROP TABLE IF EXISTS `$tableName`;");
 | 
			
		||||
                $db->execute($sql);
 | 
			
		||||
                Log::info("$tableName 表创建成功");
 | 
			
		||||
            } catch (\Exception $e) {
 | 
			
		||||
                throw new \Exception("创建 $tableName 表失败: " . $e->getMessage());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取表创建的 SQL 语句
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    private function getTableCreationSqls(): array
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'mpay_order' => "CREATE TABLE `mpay_order` (
 | 
			
		||||
                `id` int(11) NOT NULL AUTO_INCREMENT,
 | 
			
		||||
                `pid` int(11) NOT NULL DEFAULT 0,
 | 
			
		||||
                `order_id` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `type` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `out_trade_no` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `notify_url` varchar(512) NOT NULL DEFAULT '',
 | 
			
		||||
                `return_url` varchar(512) NOT NULL DEFAULT '',
 | 
			
		||||
                `name` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `really_price` decimal(10, 2) NOT NULL DEFAULT 0.00,
 | 
			
		||||
                `money` decimal(10, 2) NOT NULL DEFAULT 0.00,
 | 
			
		||||
                `clientip` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `device` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `param` varchar(720) NOT NULL DEFAULT '',
 | 
			
		||||
                `state` tinyint(4) NOT NULL DEFAULT 0,
 | 
			
		||||
                `patt` tinyint(4) NOT NULL DEFAULT 0,
 | 
			
		||||
                `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
                `close_time` datetime DEFAULT NULL,
 | 
			
		||||
                `pay_time` datetime DEFAULT NULL,
 | 
			
		||||
                `platform` varchar(15) NOT NULL DEFAULT '',
 | 
			
		||||
                `platform_order` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `aid` int(11) NOT NULL DEFAULT 0,
 | 
			
		||||
                `cid` int(11) NOT NULL DEFAULT 0,
 | 
			
		||||
                `delete_time` datetime DEFAULT NULL,
 | 
			
		||||
                PRIMARY KEY (`id`),
 | 
			
		||||
                INDEX `idx_order_id` (`order_id`)
 | 
			
		||||
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;",
 | 
			
		||||
            'mpay_pay_account' => "CREATE TABLE `mpay_pay_account` (
 | 
			
		||||
                `id` int(11) NOT NULL AUTO_INCREMENT,
 | 
			
		||||
                `pid` int(11) NOT NULL DEFAULT 0,
 | 
			
		||||
                `platform` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `account` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `password` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `state` tinyint(4) NOT NULL DEFAULT 1,
 | 
			
		||||
                `pattern` tinyint(4) NOT NULL DEFAULT 1,
 | 
			
		||||
                `params` text NOT NULL,
 | 
			
		||||
                `delete_time` datetime DEFAULT NULL,
 | 
			
		||||
                PRIMARY KEY (`id`)
 | 
			
		||||
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;",
 | 
			
		||||
            'mpay_pay_channel' => "CREATE TABLE `mpay_pay_channel` (
 | 
			
		||||
                `id` int(11) NOT NULL AUTO_INCREMENT,
 | 
			
		||||
                `account_id` int(11) NOT NULL DEFAULT 0,
 | 
			
		||||
                `channel` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `type` tinyint(4) NOT NULL DEFAULT 0,
 | 
			
		||||
                `qrcode` varchar(512) NOT NULL DEFAULT '',
 | 
			
		||||
                `last_time` datetime DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
                `state` tinyint(4) NOT NULL DEFAULT 1,
 | 
			
		||||
                `delete_time` datetime DEFAULT NULL,
 | 
			
		||||
                PRIMARY KEY (`id`)
 | 
			
		||||
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;",
 | 
			
		||||
            'mpay_user' => "CREATE TABLE `mpay_user` (
 | 
			
		||||
                `id` int(11) NOT NULL AUTO_INCREMENT,
 | 
			
		||||
                `pid` int(11) NOT NULL DEFAULT 0,
 | 
			
		||||
                `secret_key` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `nickname` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `username` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `password` varchar(255) NOT NULL DEFAULT '',
 | 
			
		||||
                `state` tinyint(4) NOT NULL DEFAULT 1,
 | 
			
		||||
                `role` tinyint(4) NOT NULL DEFAULT 0,
 | 
			
		||||
                `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
                `delete_time` datetime DEFAULT NULL,
 | 
			
		||||
                PRIMARY KEY (`id`)
 | 
			
		||||
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;",
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化数据
 | 
			
		||||
     * @param array $dbConfig
 | 
			
		||||
     * @throws \Exception
 | 
			
		||||
     */
 | 
			
		||||
    private function initData(array $dbConfig)
 | 
			
		||||
    {
 | 
			
		||||
        $db = $this->connectDatabase();
 | 
			
		||||
        $info = [
 | 
			
		||||
            'secret_key' => md5(1000 . time() . mt_rand()),
 | 
			
		||||
            'nickname' => $dbConfig['nickname'],
 | 
			
		||||
            'username' => $dbConfig['username'],
 | 
			
		||||
            'password' => password_hash($dbConfig['password'], PASSWORD_DEFAULT),
 | 
			
		||||
            'create_time' => date('Y-m-d H:i:s'),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $sql = "INSERT INTO `mpay_user` (`id`, `pid`, `secret_key`, `nickname`, `username`, `password`, `state`, `role`, `create_time`) VALUES (1, 1000, :secret_key, :nickname, :username, :password, 1, 1, :create_time);";
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $db->execute($sql, $info);
 | 
			
		||||
            Log::info("mpay_user 表数据初始化成功");
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            throw new \Exception("初始化 mpay_user 表数据失败: " . $e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查是否已安装
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    private function checkLock()
 | 
			
		||||
    {
 | 
			
		||||
        $path = runtime_path() . self::INSTALL_LOCK_FILE;
 | 
			
		||||
        return file_exists($path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置安装锁
 | 
			
		||||
     * @throws \Exception
 | 
			
		||||
     */
 | 
			
		||||
    private function setLock()
 | 
			
		||||
    {
 | 
			
		||||
        $path = runtime_path() . self::INSTALL_LOCK_FILE;
 | 
			
		||||
        if (file_put_contents($path, time()) === false) {
 | 
			
		||||
            throw new \Exception("无法写入安装锁文件");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use app\model\Order;
 | 
			
		||||
use app\model\User;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
 | 
			
		||||
class OrderController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        $servertime = date('Y-m-d H:i:s', time());
 | 
			
		||||
        View::assign('servertime', $servertime);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    public function showOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $id = $this->request->get('id');
 | 
			
		||||
        $order = Order::showOrderDetail($id);
 | 
			
		||||
        if ($order) {
 | 
			
		||||
            View::assign($order);
 | 
			
		||||
            return View::fetch();
 | 
			
		||||
        } else {
 | 
			
		||||
            return '订单不存在';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public function testPay()
 | 
			
		||||
    {
 | 
			
		||||
        $pid = 1000;
 | 
			
		||||
        if (session('?pid')) {
 | 
			
		||||
            $pid = session('pid');
 | 
			
		||||
        }
 | 
			
		||||
        View::assign('pid', $pid);
 | 
			
		||||
        $key = User::where('pid', $pid)->where('state', 1)->value('secret_key');
 | 
			
		||||
        if (!$key) {
 | 
			
		||||
            return '用户禁用或不存在';
 | 
			
		||||
        }
 | 
			
		||||
        View::assign('key', $key);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,369 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use think\Request;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
use app\model\User;
 | 
			
		||||
use app\model\Order;
 | 
			
		||||
use app\model\PayAccount;
 | 
			
		||||
use app\model\PayChannel;
 | 
			
		||||
 | 
			
		||||
class PayController
 | 
			
		||||
{
 | 
			
		||||
    // 提交订单
 | 
			
		||||
    public function submit(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $req_method = $request->method();
 | 
			
		||||
        $req_data = match ($req_method) {
 | 
			
		||||
            'GET' => $request->get(),
 | 
			
		||||
            'POST' => $request->post(),
 | 
			
		||||
            default => []
 | 
			
		||||
        };
 | 
			
		||||
        if (!$req_data) return '参数错误';
 | 
			
		||||
        // 验证签名
 | 
			
		||||
        $key = User::where('pid', $req_data['pid'])->where('state', 1)->value('secret_key');
 | 
			
		||||
        if (!$key) return '用户禁用或不存在';
 | 
			
		||||
        $sign_str = self::getSign($req_data, $key);
 | 
			
		||||
        if ($req_data['sign'] !== $sign_str) return '签名错误';
 | 
			
		||||
        // 检查商户订单
 | 
			
		||||
        $out_trade_no = Order::where('out_trade_no', $req_data['out_trade_no'])->value('out_trade_no');
 | 
			
		||||
        if ($out_trade_no) return '订单提交重复';
 | 
			
		||||
        // 创建新订单
 | 
			
		||||
        $order_info = Order::createOrder($req_data);
 | 
			
		||||
        if ($order_info['code'] !== 0) return $order_info['msg'];
 | 
			
		||||
        return redirect("/Pay/console/{$order_info['data']['order_id']}");
 | 
			
		||||
    }
 | 
			
		||||
    // api提交订单
 | 
			
		||||
    public function mapi(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$request->isPost()) return json(backMsg(0, '请求方式错误'));
 | 
			
		||||
        $req_data = $request->post();
 | 
			
		||||
        if (!$req_data) $req_data = $request->get();
 | 
			
		||||
        if (!$req_data) return json(backMsg(0, '参数错误'));
 | 
			
		||||
        // 验证签名
 | 
			
		||||
        $key = User::where('pid', $req_data['pid'])->where('state', 1)->value('secret_key');
 | 
			
		||||
        if (!$key) return json(backMsg(0, '用户禁用或不存在'));
 | 
			
		||||
        $sign_str = self::getSign($req_data, $key);
 | 
			
		||||
        if ($req_data['sign'] !== $sign_str) return json(backMsg(0, '签名错误'));
 | 
			
		||||
        // 检查商户订单
 | 
			
		||||
        $out_trade_no = Order::where('out_trade_no', $req_data['out_trade_no'])->value('out_trade_no');
 | 
			
		||||
        if ($out_trade_no) return json(backMsg(0, '订单提交重复'));
 | 
			
		||||
        // 创建新订单
 | 
			
		||||
        $order_info = Order::createOrder($req_data);
 | 
			
		||||
        if ($order_info['code'] !== 0) return json(backMsg(0, $order_info['msg']));
 | 
			
		||||
        $payurl = $request->domain() . "/Pay/console/{$order_info['data']['order_id']}";
 | 
			
		||||
        $info = ['code' => 1, 'msg' => '订单创建成功', 'trade_no' => $order_info['data']['order_id'], 'payurl' => $payurl];
 | 
			
		||||
        return json($info);
 | 
			
		||||
    }
 | 
			
		||||
    // 收银台
 | 
			
		||||
    public function console($order_id = '')
 | 
			
		||||
    {
 | 
			
		||||
        if ($order_id) {
 | 
			
		||||
            $act_order = Order::where('order_id', $order_id)->find();
 | 
			
		||||
            if ($act_order) {
 | 
			
		||||
                $channel = PayChannel::where('id', $act_order->cid)->find();
 | 
			
		||||
                View::assign($act_order->toArray());
 | 
			
		||||
                $passtime = strtotime($act_order->close_time) - time();
 | 
			
		||||
                View::assign('passtime', $passtime > 0 ? $passtime : 0);
 | 
			
		||||
                // Alipay免输
 | 
			
		||||
                if (preg_match('/^alipay4#\d+$/', $channel->channel)) {
 | 
			
		||||
                    $payurl = \payclient\AliPayf::getPayUrl($act_order->order_id, $act_order->money, $channel->qrcode);
 | 
			
		||||
                    View::assign('payUrl', $payurl['data'] ?? $payurl['msg']);
 | 
			
		||||
                } else {
 | 
			
		||||
                    View::assign('payUrl', $channel->qrcode);
 | 
			
		||||
                }
 | 
			
		||||
                View::assign('code_type', $channel->type);
 | 
			
		||||
                return View::fetch();
 | 
			
		||||
            } else {
 | 
			
		||||
                return '订单不存在';
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return '订单号参数错误';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 查询订单状态
 | 
			
		||||
    public function getOrderState($order_id = '')
 | 
			
		||||
    {
 | 
			
		||||
        if ($order_id) {
 | 
			
		||||
            $act_order = Order::where('order_id', $order_id)->find();
 | 
			
		||||
            if ($act_order) {
 | 
			
		||||
                $passtime = strtotime($act_order->close_time) - time();
 | 
			
		||||
                $data = [];
 | 
			
		||||
                if ($act_order->state === 0) {
 | 
			
		||||
                    $data['order_id'] = $act_order->order_id;
 | 
			
		||||
                    $data['passtime'] = $passtime > 0 ? $passtime : 0;
 | 
			
		||||
                    $data['state'] = $act_order->state;
 | 
			
		||||
                    return json($data);
 | 
			
		||||
                } elseif ($act_order->state === 1) {
 | 
			
		||||
                    // 通知参数
 | 
			
		||||
                    $notify = self::crateNotify($act_order);
 | 
			
		||||
                    // 字符串签名
 | 
			
		||||
                    $user_key = User::where('pid', $act_order->pid)->value('secret_key');
 | 
			
		||||
                    $sign = self::getSign($notify, $user_key);
 | 
			
		||||
                    $notify['sign'] = $sign;
 | 
			
		||||
                    // 跳转通知URL
 | 
			
		||||
                    $res_return_url = $act_order->return_url . '?' . http_build_query($notify);
 | 
			
		||||
                    if (strpos($act_order->return_url, '?')) $res_return_url = $act_order->return_url . '&' . http_build_query($notify);
 | 
			
		||||
                    // 响应消息
 | 
			
		||||
                    $data['order_id'] = $act_order->order_id;
 | 
			
		||||
                    $data['passtime'] = $passtime > 0 ? $passtime : 0;
 | 
			
		||||
                    $data['state'] = $act_order->state;
 | 
			
		||||
                    $data['return_url'] = $res_return_url;
 | 
			
		||||
                    return json($data);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                return '订单不存在';
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return '订单号参数错误';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 验证支付结果
 | 
			
		||||
    public function validatePayResult(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $data = $request->post();
 | 
			
		||||
        $order = Order::find($data['id']);
 | 
			
		||||
        if (\strtotime($order->close_time) < \time()) {
 | 
			
		||||
            return \json(\backMsg(1, '订单已关闭'));
 | 
			
		||||
        }
 | 
			
		||||
        $up_data = ['id' => $data['id'], 'patt' => $data['patt']];
 | 
			
		||||
        $up_res = Order::update($up_data);
 | 
			
		||||
        if ($up_res) {
 | 
			
		||||
            return \json(\backMsg(0, '更新成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return \json(\backMsg(1, '更新失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 处理收款通知
 | 
			
		||||
    private function payHeart(array $records, array $config)
 | 
			
		||||
    {
 | 
			
		||||
        $pid = $config['pid'];
 | 
			
		||||
        $aid = $config['aid'];
 | 
			
		||||
        // 检测收款通知
 | 
			
		||||
        if (!$records) {
 | 
			
		||||
            return json(['code' => 0, 'msg' => '空收款通知']);
 | 
			
		||||
        }
 | 
			
		||||
        // 当前用户账号
 | 
			
		||||
        $query = ['pid' => $pid, 'aid' => $aid];
 | 
			
		||||
        // 排除已支付订单
 | 
			
		||||
        $doneOrders = Order::scope('dealOrder')->where($query)->column('platform_order');
 | 
			
		||||
        $new_orders = [];
 | 
			
		||||
        foreach ($records as $order) {
 | 
			
		||||
            if (!in_array($order['order_no'], $doneOrders)) $new_orders[] = $order;
 | 
			
		||||
        }
 | 
			
		||||
        if (!count($new_orders)) return json(['code' => 0, 'msg' => '收款通知无新消息']);
 | 
			
		||||
        // 有效订单列表
 | 
			
		||||
        $activeOrders = Order::scope('activeOrder')->where($query)->select();
 | 
			
		||||
        if (!count($activeOrders)) return json(['code' => 0, 'msg' => '数据库无有效期订单']);
 | 
			
		||||
        // 查找所有支付渠道
 | 
			
		||||
        $channels = $activeOrders->column('cid');
 | 
			
		||||
        $cids = PayChannel::whereIn('id', $channels)->column('channel', 'id');
 | 
			
		||||
        // 订单处理
 | 
			
		||||
        $notify = [];
 | 
			
		||||
        foreach ($new_orders as $new_order) {
 | 
			
		||||
            foreach ($activeOrders as $order) {
 | 
			
		||||
                // 支付方式核对
 | 
			
		||||
                $is_payway = $order->type == $new_order['payway'];
 | 
			
		||||
                if ($new_order['payway'] == '') $is_payway = true;
 | 
			
		||||
                // 支付渠道核对
 | 
			
		||||
                $is_channel = $cids[$order->cid] == $new_order['channel'];
 | 
			
		||||
                // 金额核对
 | 
			
		||||
                $is_money = $order->really_price == $new_order['price'];
 | 
			
		||||
                // 订单核对
 | 
			
		||||
                if ($is_payway && $is_channel && $is_money) {
 | 
			
		||||
                    // 是否免输
 | 
			
		||||
                    if (isset($new_order['remark'])) {
 | 
			
		||||
                        if ($new_order['remark'] == $order->order_id) {
 | 
			
		||||
                            $res = $this->updateOrderState($order, $new_order['order_no']);
 | 
			
		||||
                            $notify[] = $res;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $res = $this->updateOrderState($order, $new_order['order_no']);
 | 
			
		||||
                        $notify[] = $res;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!$notify) $notify = ['code' => 0, 'msg' => '收款通知无匹配订单'];
 | 
			
		||||
        return json($notify);
 | 
			
		||||
    }
 | 
			
		||||
    // 修改订单状态并通知
 | 
			
		||||
    private function updateOrderState(Order $order, string $order_no = ''): array
 | 
			
		||||
    {
 | 
			
		||||
        // 支付成功
 | 
			
		||||
        $set_order_state = $order->save(['state' => 1, 'pay_time' => date('Y-m-d H:i:s', time()), 'platform_order' => $order_no]);
 | 
			
		||||
        if (!$set_order_state) {
 | 
			
		||||
            return ['order' => $order->order_id, 'code' => 0, 'msg' => '修改订单状态失败'];
 | 
			
		||||
        }
 | 
			
		||||
        // 订单成交通知
 | 
			
		||||
        $notify = self::crateNotify($order);
 | 
			
		||||
        // 字符串签名
 | 
			
		||||
        $user_key = User::where('pid', $order->pid)->value('secret_key');
 | 
			
		||||
        $sign = self::getSign($notify, $user_key);
 | 
			
		||||
        $notify['sign'] = $sign;
 | 
			
		||||
        // 异步通知
 | 
			
		||||
        $notify_url = $order->notify_url . '?' . http_build_query($notify);
 | 
			
		||||
        if (strpos($order->notify_url, '?')) $notify_url = $order->notify_url . '&' . http_build_query($notify);
 | 
			
		||||
        $res_notify = self::getHttpResponse($notify_url);
 | 
			
		||||
        if ($res_notify === 'success') {
 | 
			
		||||
            return ['order' => $order->order_id, 'code' => 1, 'msg' => 'notify success'];
 | 
			
		||||
        } else {
 | 
			
		||||
            return ['order' => $order->order_id, 'code' => 0, 'msg' => 'notify fail'];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // [定时任务]获取收款明细,提交收款通知
 | 
			
		||||
    public function checkPayResult(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $req_info = $request->get();
 | 
			
		||||
        $req_pid = $req_info['pid'];
 | 
			
		||||
        $req_aid = $req_info['aid'];
 | 
			
		||||
        // 获取订单
 | 
			
		||||
        $new_order = cache('order');
 | 
			
		||||
        if (!$new_order) return json(['code' => 3, 'msg' => '没有找到新订单缓存']);
 | 
			
		||||
        // 检测新订单
 | 
			
		||||
        if ($new_order['code'] !== 1) return json($new_order);
 | 
			
		||||
        // 订单列表
 | 
			
		||||
        $order_list = $new_order['orders'];
 | 
			
		||||
        // 检测本账号订单
 | 
			
		||||
        $orders = [];
 | 
			
		||||
        foreach ($order_list as $key => $val) {
 | 
			
		||||
            if ($req_pid == $val['pid'] && $req_aid == $val['aid'] && $val['patt'] == 1) {
 | 
			
		||||
                $orders[] = $order_list[$key];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!$orders) return json(['code' => 0, 'msg' => '非本账号订单或监听模式不对']);
 | 
			
		||||
        // 加载配置文件
 | 
			
		||||
        $config = PayAccount::getAccountConfig($req_aid);
 | 
			
		||||
        if ($config === false) return json(['code' => 4, 'msg' => '监听收款配置错误']);
 | 
			
		||||
        // 登陆账号
 | 
			
		||||
        $pay_config = ['username' => $config['account'], 'password' => $config['password']];
 | 
			
		||||
        // 配置参数
 | 
			
		||||
        $params = $config['params'];
 | 
			
		||||
        // 实例监听客户端
 | 
			
		||||
        $payclient_name = $config['payclass'];
 | 
			
		||||
        // 插件类文件是否存在
 | 
			
		||||
        $payclient_path = root_path() . '/extend/payclient/' . $payclient_name . '.php';
 | 
			
		||||
        if (!file_exists($payclient_path)) return json(['code' => 5, 'msg' => '监听客户端文件不存在']);
 | 
			
		||||
        $payclient_path = "\\payclient\\{$payclient_name}";
 | 
			
		||||
        $Payclient = new $payclient_path($pay_config);
 | 
			
		||||
        // 获取支付明细
 | 
			
		||||
        $records = $Payclient->getOrderInfo($params);
 | 
			
		||||
        if ($records['code'] === 0) {
 | 
			
		||||
            // 提交收款记录
 | 
			
		||||
            $upres = $this->payHeart($records['data'], $config);
 | 
			
		||||
            return $upres;
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(['code' => 0, 'msg' => $records['msg']], 320);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // [定时任务]监听新订单,生成缓存
 | 
			
		||||
    public function checkOrder($pid = '', $sign = '')
 | 
			
		||||
    {
 | 
			
		||||
        if (!($pid && $sign)) return '参数错误';
 | 
			
		||||
        $is_user = User::checkUser($pid, $sign);
 | 
			
		||||
        if ($is_user) {
 | 
			
		||||
            $orders = Order::scope('activeOrder')->field('id,pid,aid,cid,patt')->select();
 | 
			
		||||
            $old_info = cache('order');
 | 
			
		||||
            $num = count($orders);
 | 
			
		||||
            if ($num > 0) {
 | 
			
		||||
                $info = ['code' => 1, 'msg' => "有{$num}个新订单"];
 | 
			
		||||
                $order_list = ['code' => 1, 'msg' => "有{$num}个新订单", 'orders' => $orders];
 | 
			
		||||
                if ($old_info !== $order_list) {
 | 
			
		||||
                    cache('order', $order_list);
 | 
			
		||||
                }
 | 
			
		||||
                return json($info);
 | 
			
		||||
            } else {
 | 
			
		||||
                $info = ['code' => 0, 'msg' => '没有新订单'];
 | 
			
		||||
                if ($old_info !== $info) {
 | 
			
		||||
                    cache('order', $info);
 | 
			
		||||
                }
 | 
			
		||||
                return json($info);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $info = ['code' => 2, 'msg' => '签名错误'];
 | 
			
		||||
            return json($info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 处理微信/支付宝收款通知
 | 
			
		||||
    public function mpayNotify(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $info = $request->post();
 | 
			
		||||
        $action = isset($info['action']) ? $info['action'] : '';
 | 
			
		||||
        if ($action !== 'mpay' && $action !== 'mpaypc') return '非mpay的访问请求';
 | 
			
		||||
        $data = json_decode($info['data'], true);
 | 
			
		||||
        if (!is_array($data)) return '通知数据为空';
 | 
			
		||||
        if (!isset($data['aid']) || !isset($data['pid'])) return 'aid和pid参数错误';
 | 
			
		||||
        $config = PayAccount::getAccountConfig($data['aid'], $data['pid']);
 | 
			
		||||
        $payclient_path = "\\payclient\\{$config['payclass']}";
 | 
			
		||||
        $Payclient = new $payclient_path($info, $config);
 | 
			
		||||
        if ($action == 'mpay') $res = $Payclient->notify();
 | 
			
		||||
        if ($action == 'mpaypc') $res = $Payclient->pcNotify();
 | 
			
		||||
        if ($res['code'] !== 0) return $res['msg'];
 | 
			
		||||
        $this->payHeart($res['data'], $config);
 | 
			
		||||
        return 200;
 | 
			
		||||
    }
 | 
			
		||||
    // 签名
 | 
			
		||||
    private static function getSign(array $param = [], string $key = ''): string
 | 
			
		||||
    {
 | 
			
		||||
        ksort($param);
 | 
			
		||||
        reset($param);
 | 
			
		||||
        $signstr = '';
 | 
			
		||||
        foreach ($param as $k => $v) {
 | 
			
		||||
            if ($k != "sign" && $k != "sign_type" && $v != '') {
 | 
			
		||||
                $signstr .= $k . '=' . $v . '&';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $signstr = substr($signstr, 0, -1);
 | 
			
		||||
        $signstr .= $key;
 | 
			
		||||
        $sign = md5($signstr);
 | 
			
		||||
        return $sign;
 | 
			
		||||
    }
 | 
			
		||||
    // 构建通知
 | 
			
		||||
    private static function crateNotify($param): array
 | 
			
		||||
    {
 | 
			
		||||
        $notify = [
 | 
			
		||||
            'pid' => $param->pid,
 | 
			
		||||
            'trade_no' => $param->order_id,
 | 
			
		||||
            'out_trade_no' => $param->out_trade_no,
 | 
			
		||||
            'type' => $param->type,
 | 
			
		||||
            'name' => $param->name,
 | 
			
		||||
            'money' => $param->money,
 | 
			
		||||
            'trade_status' => 'TRADE_SUCCESS',
 | 
			
		||||
            'sign_type' => 'MD5',
 | 
			
		||||
        ];
 | 
			
		||||
        // 添加扩展参数
 | 
			
		||||
        // $notify = array_merge($notify, unserialize($param->param));
 | 
			
		||||
        $notify['param'] = unserialize($param->param);
 | 
			
		||||
        // 删除空值
 | 
			
		||||
        foreach ($notify as $key => $val) {
 | 
			
		||||
            if ($val === '') unset($notify[$key]);
 | 
			
		||||
        }
 | 
			
		||||
        return $notify;
 | 
			
		||||
    }
 | 
			
		||||
    // 请求外部资源
 | 
			
		||||
    private static function getHttpResponse($url, $header = [], $post = null, $timeout = 10)
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
 | 
			
		||||
        if ($header) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
 | 
			
		||||
        } else {
 | 
			
		||||
            $httpheader[] = "Accept: */*";
 | 
			
		||||
            $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
 | 
			
		||||
            $httpheader[] = "Connection: close";
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
 | 
			
		||||
        }
 | 
			
		||||
        curl_setopt($ch, CURLOPT_HEADER, false);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 | 
			
		||||
        if ($post) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POST, true);
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
 | 
			
		||||
        }
 | 
			
		||||
        $response = curl_exec($ch);
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use app\model\PayAccount;
 | 
			
		||||
use app\model\PayChannel;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
 | 
			
		||||
class PayManageController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        $domain = $this->request->domain();
 | 
			
		||||
        View::assign('domain', $domain);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 编辑账号
 | 
			
		||||
    public function editAccount()
 | 
			
		||||
    {
 | 
			
		||||
        $id = $this->request->get('id');
 | 
			
		||||
        $account = PayAccount::find($id);
 | 
			
		||||
        View::assign([
 | 
			
		||||
            'id' => $id,
 | 
			
		||||
            'platform' => $account->getData('platform'),
 | 
			
		||||
            'account' => $account->account,
 | 
			
		||||
            'password' => $account->password,
 | 
			
		||||
            'state' => $account->state,
 | 
			
		||||
            'pattern' => $account->getData('pattern'),
 | 
			
		||||
            'params' => $account->params,
 | 
			
		||||
        ]);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 添加账号
 | 
			
		||||
    public function addAccount()
 | 
			
		||||
    {
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 添加收款终端
 | 
			
		||||
    public function addChannel()
 | 
			
		||||
    {
 | 
			
		||||
        $aid = $this->request->get('aid');
 | 
			
		||||
        $account = PayAccount::find($aid);
 | 
			
		||||
        $platform = $account->getData('platform');
 | 
			
		||||
        View::assign(['aid' => $aid, 'platform' => $platform, 'account' => $account->account]);
 | 
			
		||||
        if ($platform == 'wxpay' || $platform == 'alipay') {
 | 
			
		||||
            return View::fetch('add_channel_code');
 | 
			
		||||
        }
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 编辑收款终端
 | 
			
		||||
    public function editChannel()
 | 
			
		||||
    {
 | 
			
		||||
        $cid = $this->request->get('cid');
 | 
			
		||||
        $channel = PayChannel::with('payAccount')->where('id', $cid)->find();
 | 
			
		||||
        View::assign([
 | 
			
		||||
            'cid' => $channel->id,
 | 
			
		||||
            'platform' => $channel->payAccount->platform,
 | 
			
		||||
            'account' => $channel->payAccount->account,
 | 
			
		||||
            'channel' => $channel->channel,
 | 
			
		||||
            'qrcode' => $channel->qrcode,
 | 
			
		||||
            'last_time' => $channel->last_time,
 | 
			
		||||
            'state' => $channel->state,
 | 
			
		||||
            'type' => $channel->type,
 | 
			
		||||
        ]);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 收款终端列表
 | 
			
		||||
    public function channelList()
 | 
			
		||||
    {
 | 
			
		||||
        $id = $this->request->get('id');
 | 
			
		||||
        View::assign(['id' => $id]);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
use think\Request;
 | 
			
		||||
 | 
			
		||||
class PluginController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    // 插件管理页
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use think\facade\View;
 | 
			
		||||
use app\model\User;
 | 
			
		||||
 | 
			
		||||
class UserController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    protected $middleware = ['Auth' => ['except' => ['login']]];
 | 
			
		||||
    // 用户中心
 | 
			
		||||
    public function index()
 | 
			
		||||
    {
 | 
			
		||||
        $userinfo = User::find(\session('userid'))->toArray();
 | 
			
		||||
        View::assign($userinfo);
 | 
			
		||||
        View::assign('url', $this->request->domain().'/');
 | 
			
		||||
        $sign = md5($userinfo['pid'] . $userinfo['secret_key']);
 | 
			
		||||
        View::assign('orderurl', $this->request->domain() . "/checkOrder/{$userinfo['pid']}/{$sign}");
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 登陆视图
 | 
			
		||||
    public function login()
 | 
			
		||||
    {
 | 
			
		||||
        if (session('?islogin')) {
 | 
			
		||||
            return redirect('/Console/index');
 | 
			
		||||
        }
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
    // 修改用户
 | 
			
		||||
    public function setUser()
 | 
			
		||||
    {
 | 
			
		||||
        $userinfo = User::find(session('userid'))->toArray();
 | 
			
		||||
        View::assign($userinfo);
 | 
			
		||||
        return View::fetch();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller\api;
 | 
			
		||||
 | 
			
		||||
use think\Request;
 | 
			
		||||
use app\model\Order;
 | 
			
		||||
 | 
			
		||||
class ConsoleController
 | 
			
		||||
{
 | 
			
		||||
    public function orderinfo(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $date = (int)$request->get('time') ?: 0;
 | 
			
		||||
        $time = match ($date) {
 | 
			
		||||
            0 => [date('Y') . '-01-01 00:00:00', date('Y-m-d 23:59:59')],
 | 
			
		||||
            1 => [date('Y-m-d H:i:s', strtotime('-30 days')), date('Y-m-d 23:59:59')],
 | 
			
		||||
            2 => [date('Y-m-d H:i:s', strtotime('-6 months')), date('Y-m-d 23:59:59')],
 | 
			
		||||
            3 => [date('Y-m-d H:i:s', strtotime('-1 year')), date('Y-m-d 23:59:59')],
 | 
			
		||||
            default => []
 | 
			
		||||
        };
 | 
			
		||||
        if (!$time) {
 | 
			
		||||
            return json(['code' => 400, 'msg' => '参数错误']);
 | 
			
		||||
        }
 | 
			
		||||
        $orders = Order::whereBetweenTime('create_time', $time[0], $time[1])->where('state', 1)->field('id,type,really_price')->select();
 | 
			
		||||
        $data = [
 | 
			
		||||
            'ordernum' => count($orders),
 | 
			
		||||
            'totalmoney' => \number_format(array_sum(array_column($orders->toArray(), 'really_price')), 2),
 | 
			
		||||
            'wxpay' => [
 | 
			
		||||
                'num' => count($orders->where('type', 'wxpay')),
 | 
			
		||||
                'money' => \number_format(array_sum(array_column($orders->where('type', 'wxpay')->toArray(), 'really_price')), 2)
 | 
			
		||||
            ],
 | 
			
		||||
            'alipay' => [
 | 
			
		||||
                'num' => count($orders->where('type', 'alipay')),
 | 
			
		||||
                'money' => \number_format(array_sum(array_column($orders->where('type', 'alipay')->toArray(), 'really_price')), 2)
 | 
			
		||||
            ],
 | 
			
		||||
            'unionpay' => [
 | 
			
		||||
                'num' => count($orders->where('type', 'unionpay')),
 | 
			
		||||
                'money' => \number_format(array_sum(array_column($orders->where('type', 'unionpay')->toArray(), 'really_price')), 2)
 | 
			
		||||
            ]
 | 
			
		||||
        ];
 | 
			
		||||
        return json($data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,196 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller\api;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use app\model\Order;
 | 
			
		||||
use app\model\User;
 | 
			
		||||
 | 
			
		||||
class OrderController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    // 查询订单
 | 
			
		||||
    public function getOrders()
 | 
			
		||||
    {
 | 
			
		||||
        $query = $this->request->get();
 | 
			
		||||
        $orders = Order::serchOrders($query)->order('id', 'desc')->paginate(['list_rows' => $query['limit'], 'page' => $query['page']]);
 | 
			
		||||
        if ($orders) {
 | 
			
		||||
            return json(['code' => 0, 'msg' => 'OK', 'count' => $orders->total(), 'data' => $orders->items()]);
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(['code' => 1, 'msg' => '无数据记录', 'count' => 0, 'data' => []]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 修改订单支付状态
 | 
			
		||||
    public function changeOrderState()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $uporder_res = Order::update(['state' => $info['state'], 'id' => $info['id']]);
 | 
			
		||||
        if ($uporder_res) {
 | 
			
		||||
            return json(\backMsg(0, '修改成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(\backMsg(1, '修改失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 手动补单
 | 
			
		||||
    public function doPayOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        // 修改支付状态
 | 
			
		||||
        $order = Order::find($info['id']);
 | 
			
		||||
        $order->state = $info['state'];
 | 
			
		||||
        $res = $order->save();
 | 
			
		||||
        if ($res) {
 | 
			
		||||
            // 创建通知
 | 
			
		||||
            $notify = self::crateNotify($order);
 | 
			
		||||
            // 字符串签名
 | 
			
		||||
            $user_key = User::where('pid', $order->pid)->value('secret_key');
 | 
			
		||||
            $sign = self::getSign($notify, $user_key);
 | 
			
		||||
            $notify['sign'] = $sign;
 | 
			
		||||
            // 异步通知
 | 
			
		||||
            $notify_url = $order->notify_url . '?' . http_build_query($notify);
 | 
			
		||||
            if (strpos($order->notify_url, '?')) $notify_url = $order->notify_url . '&' . http_build_query($notify);
 | 
			
		||||
            $res_notify = self::getHttpResponse($notify_url);
 | 
			
		||||
            if ($res_notify === 'success') {
 | 
			
		||||
                return json(\backMsg(0, '订单通知成功'));
 | 
			
		||||
            } else {
 | 
			
		||||
                return json(\backMsg(1, '异步通知失败'));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(\backMsg(1, '支付状态修改失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 重新通知
 | 
			
		||||
    public function redoPayOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $id = $this->request->post('id');
 | 
			
		||||
        // 修改支付状态
 | 
			
		||||
        $order = Order::find($id);
 | 
			
		||||
        if ($order) {
 | 
			
		||||
            // 创建通知
 | 
			
		||||
            $notify = self::crateNotify($order);
 | 
			
		||||
            // 字符串签名
 | 
			
		||||
            $user_key = User::where('pid', $order->pid)->value('secret_key');
 | 
			
		||||
            $sign = self::getSign($notify, $user_key);
 | 
			
		||||
            $notify['sign'] = $sign;
 | 
			
		||||
            // 异步通知
 | 
			
		||||
            $notify_url = $order->notify_url . '?' . http_build_query($notify);
 | 
			
		||||
            if (strpos($order->notify_url, '?')) $notify_url = $order->notify_url . '&' . http_build_query($notify);
 | 
			
		||||
            $res_notify = self::getHttpResponse($notify_url);
 | 
			
		||||
            if ($res_notify === 'success') {
 | 
			
		||||
                return json(\backMsg(0, '订单通知成功'));
 | 
			
		||||
            } else {
 | 
			
		||||
                return json(\backMsg(1, '异步通知失败'));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(\backMsg(1, '订单不存在'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 删除订单
 | 
			
		||||
    public function deleteOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $id = $this->request->post('id');
 | 
			
		||||
        $del_res = Order::destroy($id);
 | 
			
		||||
        if ($del_res) {
 | 
			
		||||
            return json(\backMsg(0, '删除成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(\backMsg(1, '删除失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 批量删除订单
 | 
			
		||||
    public function batchRemove()
 | 
			
		||||
    {
 | 
			
		||||
        $ids = $this->request->post('ids');
 | 
			
		||||
        if (!$ids) {
 | 
			
		||||
            return json(\backMsg(1, '参数错误'));
 | 
			
		||||
        }
 | 
			
		||||
        $del_res = Order::destroy($ids);
 | 
			
		||||
        if ($del_res) {
 | 
			
		||||
            return json(\backMsg(0, '删除成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(\backMsg(1, '删除失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 清空超时订单
 | 
			
		||||
    public function batchTimeout()
 | 
			
		||||
    {
 | 
			
		||||
        $ids = Order::scope('timeoutOrder')->column('id');
 | 
			
		||||
        if (!$ids) {
 | 
			
		||||
            return json(\backMsg(1, '无过期订单'));
 | 
			
		||||
        }
 | 
			
		||||
        $batch_del_res = Order::destroy($ids);
 | 
			
		||||
        if ($batch_del_res) {
 | 
			
		||||
            return json(\backMsg(0, '清理成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(\backMsg(1, '清理失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 签名方法
 | 
			
		||||
    private static function getSign(array $param = [], string $key = ''): string
 | 
			
		||||
    {
 | 
			
		||||
        if (!$param)
 | 
			
		||||
            return '参数错误';
 | 
			
		||||
        if (!$key)
 | 
			
		||||
            return '密钥错误';
 | 
			
		||||
        ksort($param);
 | 
			
		||||
        reset($param);
 | 
			
		||||
        $signstr = '';
 | 
			
		||||
        foreach ($param as $k => $v) {
 | 
			
		||||
            if ($k != "sign" && $k != "sign_type" && $v != '') {
 | 
			
		||||
                $signstr .= $k . '=' . $v . '&';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $signstr = substr($signstr, 0, -1);
 | 
			
		||||
        $signstr .= $key;
 | 
			
		||||
        $sign = md5($signstr);
 | 
			
		||||
        return $sign;
 | 
			
		||||
    }
 | 
			
		||||
    // 构建通知参数
 | 
			
		||||
    private static function crateNotify($param): array
 | 
			
		||||
    {
 | 
			
		||||
        $notify = [
 | 
			
		||||
            'pid' => $param->pid,
 | 
			
		||||
            'trade_no' => $param->order_id,
 | 
			
		||||
            'out_trade_no' => $param->out_trade_no,
 | 
			
		||||
            'type' => $param->type,
 | 
			
		||||
            'name' => $param->name,
 | 
			
		||||
            'money' => $param->money,
 | 
			
		||||
            'trade_status' => 'TRADE_SUCCESS',
 | 
			
		||||
            'sign_type' => 'MD5',
 | 
			
		||||
        ];
 | 
			
		||||
        // 添加扩展参数
 | 
			
		||||
        // $notify = array_merge($notify, unserialize($param->param));
 | 
			
		||||
        $notify['param'] = unserialize($param->param);
 | 
			
		||||
        // 删除空值
 | 
			
		||||
        foreach ($notify as $key => $val) {
 | 
			
		||||
            if ($val === '') unset($notify[$key]);
 | 
			
		||||
        }
 | 
			
		||||
        return $notify;
 | 
			
		||||
    }
 | 
			
		||||
    // 请求外部资源
 | 
			
		||||
    private static function getHttpResponse($url, $header = [], $post = null, $timeout = 10)
 | 
			
		||||
    {
 | 
			
		||||
        $ch = curl_init($url);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
 | 
			
		||||
        if ($header) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
 | 
			
		||||
        } else {
 | 
			
		||||
            $httpheader[] = "Accept: */*";
 | 
			
		||||
            $httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
 | 
			
		||||
            $httpheader[] = "Connection: close";
 | 
			
		||||
            curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
 | 
			
		||||
        }
 | 
			
		||||
        curl_setopt($ch, CURLOPT_HEADER, false);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 | 
			
		||||
        if ($post) {
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POST, true);
 | 
			
		||||
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
 | 
			
		||||
        }
 | 
			
		||||
        $response = curl_exec($ch);
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,181 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller\api;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use app\model\PayAccount;
 | 
			
		||||
use app\model\PayChannel;
 | 
			
		||||
 | 
			
		||||
class PayManageController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    // 获取账号列表
 | 
			
		||||
    public function getPayAccount()
 | 
			
		||||
    {
 | 
			
		||||
        $query = $this->request->get();
 | 
			
		||||
        $accounts = PayAccount::serchAccount($query)->order('id', 'desc')->paginate(['list_rows' => $query['limit'], 'page' => $query['page']]);
 | 
			
		||||
        if ($accounts) {
 | 
			
		||||
            return json(['code' => 0, 'msg' => 'OK', 'count' => $accounts->total(), 'data' => $accounts->items()]);
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(['code' => 1, 'msg' => '无数据记录', 'count' => 0, 'data' => []]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 收款终端列表
 | 
			
		||||
    public function getChannelList()
 | 
			
		||||
    {
 | 
			
		||||
        $aid = $this->request->post('aid');
 | 
			
		||||
        $res = PayChannel::where(['account_id' => $aid])->order('last_time', 'desc')->select();
 | 
			
		||||
        if ($res) {
 | 
			
		||||
            return json(backMsg(0, '获取成功', $res));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 账号状态
 | 
			
		||||
    public function accountEnable()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $up_res = PayAccount::update($info);
 | 
			
		||||
        if ($up_res) {
 | 
			
		||||
            return json(backMsg(0, '成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 添加账号
 | 
			
		||||
    public function addAccount()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $pid = $this->request->session('pid');
 | 
			
		||||
        $info['pid'] = $pid;
 | 
			
		||||
        $info['params'] = '{}';
 | 
			
		||||
        $check_acc = PayAccount::where(['account' => $info['account'], 'platform' => $info['platform'], 'pid' => $pid])->find();
 | 
			
		||||
        if ($check_acc) {
 | 
			
		||||
            return json(backMsg(1, '账号已存在'));
 | 
			
		||||
        }
 | 
			
		||||
        $acc = PayAccount::create($info);
 | 
			
		||||
        if ($acc) {
 | 
			
		||||
            return json(backMsg(0, '添加成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '添加失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 编辑账号
 | 
			
		||||
    public function editAccount()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $up_res = PayAccount::update($info);
 | 
			
		||||
        if ($up_res) {
 | 
			
		||||
            return json(backMsg(0, '修改成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '修改失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 删除账号
 | 
			
		||||
    public function delAccount()
 | 
			
		||||
    {
 | 
			
		||||
        $ids = $this->request->post('ids');
 | 
			
		||||
        $res = PayAccount::destroy($ids);
 | 
			
		||||
        $res2 = PayChannel::whereIn('account_id', $ids)->select()->delete();
 | 
			
		||||
        if ($res && $res2) {
 | 
			
		||||
            return json(backMsg(0, '已删除'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 添加收款终端
 | 
			
		||||
    public function addChannel()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $check = PayChannel::where(['account_id' => $info['account_id'], 'channel' => $info['channel']])->count();
 | 
			
		||||
        if ($check) {
 | 
			
		||||
            return json(backMsg(1, '编号已存在'));
 | 
			
		||||
        }
 | 
			
		||||
        $info['last_time'] = date('Y-m-d H:i:s');
 | 
			
		||||
        $res = PayChannel::create($info);
 | 
			
		||||
        if ($res) {
 | 
			
		||||
            return json(backMsg(0, '添加成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '添加失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 编辑收款终端
 | 
			
		||||
    public function editChannel()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $up_res = PayChannel::update($info);
 | 
			
		||||
        if ($up_res) {
 | 
			
		||||
            return json(backMsg(0, '修改成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '修改失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 删除收款终端
 | 
			
		||||
    public function delChannel()
 | 
			
		||||
    {
 | 
			
		||||
        $cid = $this->request->post('id');
 | 
			
		||||
        $res = PayChannel::destroy($cid);
 | 
			
		||||
        if ($res) {
 | 
			
		||||
            return json(backMsg(0, '已删除'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 上传二维码图片
 | 
			
		||||
    public function uploadQrcode()
 | 
			
		||||
    {
 | 
			
		||||
        $img = $this->request->file('codeimg');
 | 
			
		||||
        if (!$img) {
 | 
			
		||||
            return json(backMsg(1, '请选择要上传的文件'));
 | 
			
		||||
        }
 | 
			
		||||
        // 验证文件类型
 | 
			
		||||
        $allowedTypes = ['image/png', 'image/jpeg', 'image/gif'];
 | 
			
		||||
        $fileMimeType = $img->getMime();
 | 
			
		||||
        if (!in_array($fileMimeType, $allowedTypes)) {
 | 
			
		||||
            return json(backMsg(1, '只允许上传PNG、JPEG或GIF格式的图片'));
 | 
			
		||||
        }
 | 
			
		||||
        // 生成唯一文件名
 | 
			
		||||
        $filename = 'img_' . time() . '_' . uniqid() . '.' . $img->getOriginalExtension();
 | 
			
		||||
        // 设置文件保存路径
 | 
			
		||||
        $path = public_path() . '/files/qrcode/';
 | 
			
		||||
        if (!is_dir($path)) {
 | 
			
		||||
            mkdir($path, 0755, true);
 | 
			
		||||
        }
 | 
			
		||||
        // 移动文件到指定目录
 | 
			
		||||
        $info = $img->move($path, $filename);
 | 
			
		||||
        if ($info) {
 | 
			
		||||
            $imgpath = '/files/qrcode/' . $filename;
 | 
			
		||||
            return json(backMsg(0, '上传成功', ['imgpath' => $imgpath]));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '上传失败'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 获取账号交易流水
 | 
			
		||||
    public function getAccountTrade()
 | 
			
		||||
    {
 | 
			
		||||
        $req_info = $this->request->get();
 | 
			
		||||
        $req_pid = $req_info['pid'];
 | 
			
		||||
        $req_aid = $req_info['aid'];
 | 
			
		||||
        // 加载配置文件
 | 
			
		||||
        $config = PayAccount::getAccountConfig($req_aid);
 | 
			
		||||
        if ($config === false) return json(backMsg(1, '账号配置文件错误'));
 | 
			
		||||
        if ($req_aid != $config['aid'] || $req_pid != session('pid')) return json(backMsg(1, '监听收款配置不一致'));
 | 
			
		||||
        // 登陆账号
 | 
			
		||||
        $pay_config = ['username' => $config['account'], 'password' => $config['password']];
 | 
			
		||||
        // 收款查询
 | 
			
		||||
        $params = $config['params'];
 | 
			
		||||
        // 实例监听客户端
 | 
			
		||||
        $payclient_name = $config['payclass'];
 | 
			
		||||
        $payclient_path = "\\payclient\\{$payclient_name}";
 | 
			
		||||
        $Payclient = new $payclient_path($pay_config);
 | 
			
		||||
        // 获取支付明细
 | 
			
		||||
        $records = $Payclient->getOrderInfo($params);
 | 
			
		||||
        if ($records['code'] === 0) {
 | 
			
		||||
            // 收款流水
 | 
			
		||||
            return json(backMsg(0, '查询成功', $records['data']));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(['code' => 1, 'msg' => $records['msg']]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,235 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller\api;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
 | 
			
		||||
class PluginController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    // 获取插件列表
 | 
			
		||||
    public function getPluginList()
 | 
			
		||||
    {
 | 
			
		||||
        $local_plugin_config = self::getPluginConfig();
 | 
			
		||||
        $show = $this->request->get('show', 1);
 | 
			
		||||
        $plugin_config = match ((int)$show) {
 | 
			
		||||
            0 => \Plugin::getAllPlugins($local_plugin_config),
 | 
			
		||||
            1 => \Plugin::getInstall($local_plugin_config),
 | 
			
		||||
            2 => \Plugin::getUninstallPlugins($local_plugin_config),
 | 
			
		||||
            default => []
 | 
			
		||||
        };
 | 
			
		||||
        if ($plugin_config) {
 | 
			
		||||
            return json(['code' => 0, 'msg' => 'OK', 'count' => count($plugin_config), 'data' => $plugin_config]);
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(['code' => 1, 'msg' => '无数据记录', 'count' => 0, 'data' => []]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 安装插件
 | 
			
		||||
    public function installPlugin()
 | 
			
		||||
    {
 | 
			
		||||
        $platform = $this->request->post('platform');
 | 
			
		||||
        if (!$platform) return json(backMsg(1, '请选择插件'));
 | 
			
		||||
        $intall_info = \Plugin::installPlugin($platform);
 | 
			
		||||
        if ($intall_info['code'] !== 0) return json(backMsg(1, $intall_info['msg']));
 | 
			
		||||
        // 需要授权
 | 
			
		||||
        if ($intall_info['data']['status'] === 0) {
 | 
			
		||||
            return json(['code' => 0, 'msg' => '请支付', 'state' => 0, 'data' => $intall_info['data']]);
 | 
			
		||||
        }
 | 
			
		||||
        $saved = $this->saveNewPluginConfig($intall_info['data']);
 | 
			
		||||
        if ($saved['code'] !== 0) return json(backMsg(1, $saved['msg']));
 | 
			
		||||
        return json(['code' => 0, 'msg' => '授权成功', 'state' => 1]);
 | 
			
		||||
    }
 | 
			
		||||
    // 更新插件
 | 
			
		||||
    public function updatePlugin()
 | 
			
		||||
    {
 | 
			
		||||
        $platform = $this->request->post('platform');
 | 
			
		||||
        if (!$platform) return json(backMsg(1, '请选择插件'));
 | 
			
		||||
        $update_info = \Plugin::updatePlugin($platform);
 | 
			
		||||
        if ($update_info['code'] !== 0) return json(backMsg(1, $update_info['msg']));
 | 
			
		||||
        $saved = $this->saveNewPluginConfig($update_info['data']);
 | 
			
		||||
        if ($saved['code'] !== 0) return json(backMsg(1, $saved['msg']));
 | 
			
		||||
        return json(['code' => 0, 'msg' => '更新成功']);
 | 
			
		||||
    }
 | 
			
		||||
    // 保存全部插件信息
 | 
			
		||||
    private function saveNewPluginConfig(array $config = [])
 | 
			
		||||
    {
 | 
			
		||||
        $plugin_config = $config['config'];
 | 
			
		||||
        $plugin_auth = $config['authcode'];
 | 
			
		||||
        $plugin_file = $config['file'];
 | 
			
		||||
        if (!$this->savePluginFile($plugin_file, $plugin_config)) return backMsg(1, '保存插件文件失败');
 | 
			
		||||
        if (!$this->saveAuthCode($plugin_auth, $plugin_config)) return backMsg(1, '保存插件授权码失败');
 | 
			
		||||
        if (!$this->addPlugin($plugin_config)) return backMsg(1, '保存插件配置失败');
 | 
			
		||||
        return backMsg(0, 'ok');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 卸载插件
 | 
			
		||||
    public function uninstallPlugin()
 | 
			
		||||
    {
 | 
			
		||||
        $platform = $this->request->post('platform');
 | 
			
		||||
        if (!$platform) return json(backMsg(1, '请选择插件'));
 | 
			
		||||
        $this->delPluginFile($platform);
 | 
			
		||||
        $this->delPlugin($platform);
 | 
			
		||||
        return json(backMsg(0, '卸载成功'));
 | 
			
		||||
    }
 | 
			
		||||
    // 添加或更新插件
 | 
			
		||||
    public function addPlugin(array $option = [])
 | 
			
		||||
    {
 | 
			
		||||
        $keys = ['platform', 'name', 'class_name', 'price', 'describe', 'website', 'helplink', 'version'];
 | 
			
		||||
        $config = [];
 | 
			
		||||
        foreach ($option as $key => $value) {
 | 
			
		||||
            if (in_array($key, $keys)) $config[$key] = $value;
 | 
			
		||||
        }
 | 
			
		||||
        $config['state'] = 1;
 | 
			
		||||
        $plugin_config = self::getPluginConfig();
 | 
			
		||||
        $plugin_platform = $config['platform'] ?: '';
 | 
			
		||||
        foreach ($plugin_config as $i => $value) {
 | 
			
		||||
            if ($plugin_platform == $value['platform']) {
 | 
			
		||||
                $plugin_config[$i] = $config;
 | 
			
		||||
                $this->savePluginConfig($plugin_config, '支付插件列表');
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $plugin_config[] = $config;
 | 
			
		||||
        $this->savePluginConfig($plugin_config, '支付插件列表');
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    // 删除插件配置
 | 
			
		||||
    private function delPlugin(string $plugin_name = '')
 | 
			
		||||
    {
 | 
			
		||||
        $plugin_config = self::getPluginConfig();
 | 
			
		||||
        $index = null;
 | 
			
		||||
        foreach ($plugin_config as $i => $value) {
 | 
			
		||||
            if ($value['platform'] == $plugin_name) {
 | 
			
		||||
                $index = $i;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if ($index === null) return false;
 | 
			
		||||
        unset($plugin_config[$index]);
 | 
			
		||||
        $config = array_values($plugin_config);
 | 
			
		||||
        $this->savePluginConfig($config, '支付插件列表');
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    // 删除插件类库文件
 | 
			
		||||
    private function delPluginFile(string $platform = '')
 | 
			
		||||
    {
 | 
			
		||||
        $file_name = self::getPluginInfo($platform)['class_name'];
 | 
			
		||||
        if (!$file_name) return false;
 | 
			
		||||
        $plugin_path = root_path() . '/extend/payclient/' . $file_name . '.php';
 | 
			
		||||
        if (!file_exists($plugin_path)) return false;
 | 
			
		||||
        unlink($plugin_path);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    // 修改插件
 | 
			
		||||
    public function setPlugin($platform = '', $option = [])
 | 
			
		||||
    {
 | 
			
		||||
        $config = self::getPluginConfig();
 | 
			
		||||
        if (!$platform) return 1;
 | 
			
		||||
        if (!$option) return 2;
 | 
			
		||||
        foreach ($config as $index => $options) {
 | 
			
		||||
            if ($options['platform'] == $platform) {
 | 
			
		||||
                foreach ($options as $key => $value) {
 | 
			
		||||
                    if (\array_key_exists($key, $option)) {
 | 
			
		||||
                        $config[$index][$key] = $option[$key];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                $this->savePluginConfig($config, '支付插件列表');
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 插件启用
 | 
			
		||||
    public function pluginEnable()
 | 
			
		||||
    {
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        if (!$this->isPluginInstall($info['platform'])) return json(backMsg(1, '插件未安装'));
 | 
			
		||||
        $up_res = $this->setPlugin($info['platform'], ['state' => $info['state']]);
 | 
			
		||||
        if ($up_res) {
 | 
			
		||||
            return json(backMsg(1, '失败'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(0, '成功'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 检测插件是否安装
 | 
			
		||||
    public function isPluginInstall(string $platform): bool
 | 
			
		||||
    {
 | 
			
		||||
        $config = self::getPluginConfig();
 | 
			
		||||
        $platforms = [];
 | 
			
		||||
        foreach ($config as $key => $value) {
 | 
			
		||||
            $platforms[] = $value['platform'];
 | 
			
		||||
        }
 | 
			
		||||
        if (in_array($platform, $platforms)) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 插件选项
 | 
			
		||||
    public function pluginOption()
 | 
			
		||||
    {
 | 
			
		||||
        // 加载平台配置
 | 
			
		||||
        $config = self::getPluginConfig();
 | 
			
		||||
        $option = [];
 | 
			
		||||
        foreach ($config as $value) {
 | 
			
		||||
            if ($value['state'] == 0) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            $option[] = ['platform' => $value['platform'], 'name' => $value['name']];
 | 
			
		||||
        }
 | 
			
		||||
        return json($option);
 | 
			
		||||
    }
 | 
			
		||||
    // 获取指定插件配置
 | 
			
		||||
    public static function getPluginInfo($platform = '')
 | 
			
		||||
    {
 | 
			
		||||
        $config = self::getPluginConfig();
 | 
			
		||||
        $info = [];
 | 
			
		||||
        foreach ($config as $item) {
 | 
			
		||||
            if ($item['platform'] == $platform) {
 | 
			
		||||
                $info = $item;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $info;
 | 
			
		||||
    }
 | 
			
		||||
    // 保存授权码
 | 
			
		||||
    private function saveAuthCode(string $authcode = '', array $config = [])
 | 
			
		||||
    {
 | 
			
		||||
        $dir_path = runtime_path() . "auth/";
 | 
			
		||||
        if (!is_dir($dir_path)) mkdir($dir_path, 755, true);
 | 
			
		||||
        $auth_path = $dir_path . md5("{$config['platform']}payclient\\{$config['class_name']}") . '.json';
 | 
			
		||||
        return file_put_contents($auth_path, json_encode(['authcode' => $authcode])) !== false ? true : false;
 | 
			
		||||
    }
 | 
			
		||||
    // 保存插件类库文件
 | 
			
		||||
    private function savePluginFile($file_url = '', array $config = [])
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($file_url))  return false;
 | 
			
		||||
        $file_content = @file_get_contents($file_url);
 | 
			
		||||
        if ($file_content === false) return false;
 | 
			
		||||
        $save_dir = root_path() . 'extend/payclient/';
 | 
			
		||||
        if (!is_dir($save_dir)) mkdir($save_dir, 0755, true);
 | 
			
		||||
        $save_path = $save_dir . $config['class_name'] . '.php';
 | 
			
		||||
        return file_put_contents($save_path, $file_content) !== false ? true : false;
 | 
			
		||||
    }
 | 
			
		||||
    // 获取插件配置
 | 
			
		||||
    private static function getPluginConfig(): array
 | 
			
		||||
    {
 | 
			
		||||
        $payplugin_path = config_path() . '/extend/payplugin.php';
 | 
			
		||||
        if (!file_exists($payplugin_path)) return [];
 | 
			
		||||
        // 加载插件配置
 | 
			
		||||
        $payplugin_config = require $payplugin_path;
 | 
			
		||||
        return $payplugin_config;
 | 
			
		||||
    }
 | 
			
		||||
    // 保存插件配置
 | 
			
		||||
    private function savePluginConfig(array $config, string $note = '说明')
 | 
			
		||||
    {
 | 
			
		||||
        $payplugin_path = config_path() . '/extend/payplugin.php';
 | 
			
		||||
        $note_tpl = <<<EOF
 | 
			
		||||
// +----------------------------------------------------------------------
 | 
			
		||||
// | $note
 | 
			
		||||
// +----------------------------------------------------------------------
 | 
			
		||||
EOF;
 | 
			
		||||
        $config_str = "<?php\n" . $note_tpl . "\n\nreturn " . var_export($config, true) . ";\n";
 | 
			
		||||
        \file_put_contents($payplugin_path, $config_str);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\controller\api;
 | 
			
		||||
 | 
			
		||||
use app\BaseController;
 | 
			
		||||
use think\facade\Session;
 | 
			
		||||
use app\model\User;
 | 
			
		||||
 | 
			
		||||
class UserController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    protected $middleware = ['Auth' => ['except' => ['login']]];
 | 
			
		||||
 | 
			
		||||
    public function login()
 | 
			
		||||
    {
 | 
			
		||||
        $login_info = $this->request->post();
 | 
			
		||||
        $userinfo = self::checkUser($login_info);
 | 
			
		||||
        if ($userinfo['code'] === 0) {
 | 
			
		||||
            Session::set('userid', $userinfo['data']->id);
 | 
			
		||||
            Session::set('pid', $userinfo['data']->pid);
 | 
			
		||||
            Session::set('nickname', $userinfo['data']->nickname);
 | 
			
		||||
            Session::set('userrole', $userinfo['data']->role);
 | 
			
		||||
            Session::set('islogin', true);
 | 
			
		||||
            return json(backMsg(0, 'ok'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json($userinfo);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public function logout()
 | 
			
		||||
    {
 | 
			
		||||
        Session::clear();
 | 
			
		||||
        return json(backMsg(0, '注销成功'));
 | 
			
		||||
    }
 | 
			
		||||
    public function editUser()
 | 
			
		||||
    {
 | 
			
		||||
        $userid = session('userid');
 | 
			
		||||
        $info = $this->request->post();
 | 
			
		||||
        $res = User::update($info, ['id' => $userid]);
 | 
			
		||||
        if (!$res) {
 | 
			
		||||
            return json(backMsg(1, '修改失败'));
 | 
			
		||||
        }
 | 
			
		||||
        return json(backMsg(0, '重置成功'));
 | 
			
		||||
    }
 | 
			
		||||
    public function changePassword()
 | 
			
		||||
    {
 | 
			
		||||
        $userid = session('userid');
 | 
			
		||||
        $user_info = User::find($userid);
 | 
			
		||||
        $post_info = $this->request->post();
 | 
			
		||||
        if (password_verify($post_info['old_password'], $user_info->password)) {
 | 
			
		||||
            $new_password = password_hash($post_info['new_password'], PASSWORD_DEFAULT);
 | 
			
		||||
            $res = User::update(['password' => $new_password], ['id' => $userid]);
 | 
			
		||||
            if (!$res) {
 | 
			
		||||
                return json(backMsg(1, '修改失败'));
 | 
			
		||||
            }
 | 
			
		||||
            return json(backMsg(0, '修改成功'));
 | 
			
		||||
        } else {
 | 
			
		||||
            return json(backMsg(1, '原密码错误'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public function resetKey()
 | 
			
		||||
    {
 | 
			
		||||
        $userid = session('userid');
 | 
			
		||||
        $res = User::update(['secret_key' => $this->generateKey()], ['id' => $userid]);
 | 
			
		||||
        if (!$res) {
 | 
			
		||||
            return json(backMsg(1, '重置失败'));
 | 
			
		||||
        }
 | 
			
		||||
        return json(backMsg(0, '重置成功'));
 | 
			
		||||
    }
 | 
			
		||||
    private function checkUser(array $login_info): array
 | 
			
		||||
    {
 | 
			
		||||
        $username = $login_info['username'];
 | 
			
		||||
        $password = $login_info['password'];
 | 
			
		||||
        $userinfo = User::where('username', $username)->find();
 | 
			
		||||
        if ($userinfo) {
 | 
			
		||||
            if (password_verify($password, $userinfo->password)) {
 | 
			
		||||
                return ['code' => 0, 'data' => $userinfo];
 | 
			
		||||
            } else {
 | 
			
		||||
                return backMsg(1, '登陆密码错误');
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return backMsg(2, '用户不存在');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private function generateKey(bool $strong = true)
 | 
			
		||||
    {
 | 
			
		||||
        $bytes = openssl_random_pseudo_bytes(16, $strong);
 | 
			
		||||
        if ($strong) {
 | 
			
		||||
            $key = bin2hex($bytes);
 | 
			
		||||
            return md5($key);
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
// 事件定义文件
 | 
			
		||||
return [
 | 
			
		||||
    'bind'      => [
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'listen'    => [
 | 
			
		||||
        'AppInit'  => [],
 | 
			
		||||
        'HttpRun'  => [],
 | 
			
		||||
        'HttpEnd'  => [],
 | 
			
		||||
        'LogLevel' => [],
 | 
			
		||||
        'LogWrite' => [],
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'subscribe' => [
 | 
			
		||||
    ],
 | 
			
		||||
];
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
// 全局中间件定义文件
 | 
			
		||||
return [
 | 
			
		||||
    // 全局请求缓存
 | 
			
		||||
    // \think\middleware\CheckRequestCache::class,
 | 
			
		||||
    // 多语言加载
 | 
			
		||||
    // \think\middleware\LoadLangPack::class,
 | 
			
		||||
    // Session初始化
 | 
			
		||||
    \think\middleware\SessionInit::class
 | 
			
		||||
];
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\middleware;
 | 
			
		||||
 | 
			
		||||
class Auth
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * 处理请求
 | 
			
		||||
     *
 | 
			
		||||
     * @param \think\Request $request
 | 
			
		||||
     * @param \Closure       $next
 | 
			
		||||
     */
 | 
			
		||||
    public function handle($request, \Closure $next)
 | 
			
		||||
    {
 | 
			
		||||
        // 登陆状态
 | 
			
		||||
        $islogin = session('?islogin');
 | 
			
		||||
 | 
			
		||||
        if ($islogin) {
 | 
			
		||||
            return $next($request);
 | 
			
		||||
        } else {
 | 
			
		||||
            $method = $request->isJson();
 | 
			
		||||
            if ($method) {
 | 
			
		||||
                return \json(\backMsg(404, '身份过期,请重新登陆'));
 | 
			
		||||
            }
 | 
			
		||||
            return redirect('/User/login');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,211 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\model;
 | 
			
		||||
 | 
			
		||||
use app\BaseModel;
 | 
			
		||||
use app\model\PayAccount;
 | 
			
		||||
use app\model\PayChannel;
 | 
			
		||||
 | 
			
		||||
class Order extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    // 订单有效期
 | 
			
		||||
    private static $activity_time = 180;
 | 
			
		||||
    // 新建订单
 | 
			
		||||
    public static function createOrder($data): array
 | 
			
		||||
    {
 | 
			
		||||
        $my_time = time();
 | 
			
		||||
        $channel = self::setChannel($data['pid'], $data['type']);
 | 
			
		||||
        if ($channel['code'] !== 0) return $channel;
 | 
			
		||||
        $channel = $channel['data'];
 | 
			
		||||
        $new_order = [
 | 
			
		||||
            // 订单号
 | 
			
		||||
            'order_id'      => self::createOrderID('H'),
 | 
			
		||||
            // 商户ID
 | 
			
		||||
            'pid'           => $data['pid'],
 | 
			
		||||
            // 支付类型
 | 
			
		||||
            'type'          => $data['type'],
 | 
			
		||||
            // 商户订单号
 | 
			
		||||
            'out_trade_no'  => $data['out_trade_no'],
 | 
			
		||||
            // 异步通知
 | 
			
		||||
            'notify_url'    => $data['notify_url'],
 | 
			
		||||
            // 跳转通知
 | 
			
		||||
            'return_url'    => isset($data['return_url']) ? $data['return_url'] : '',
 | 
			
		||||
            // 商品名称
 | 
			
		||||
            'name'          => $data['name'],
 | 
			
		||||
            // 商品金额
 | 
			
		||||
            'money'         => $data['money'],
 | 
			
		||||
            // 实际成交金额
 | 
			
		||||
            'really_price'  => self::checkMoney($data['money'], $data['type'], $channel['aid'], $channel['cid'], $channel['chan']),
 | 
			
		||||
            // 用户IP
 | 
			
		||||
            'clientip'      => isset($data['clientip']) ? $data['clientip'] : '',
 | 
			
		||||
            // 设备类型
 | 
			
		||||
            'device'        => isset($data['device']) ? $data['device'] : '',
 | 
			
		||||
            // 业务扩展参数
 | 
			
		||||
            'param'         => serialize(isset($data['param']) ? $data['param'] : ''),
 | 
			
		||||
            // 等待/过期:0, 支付成功:1
 | 
			
		||||
            'state'         => 0,
 | 
			
		||||
            // 开启监听:1, 关闭监听:0
 | 
			
		||||
            'patt'          => $channel['patt'],
 | 
			
		||||
            // 订单创建时间
 | 
			
		||||
            'create_time'   => self::getFormatTime($my_time),
 | 
			
		||||
            // 订单关闭时间
 | 
			
		||||
            'close_time'    => self::getFormatTime($my_time + self::$activity_time),
 | 
			
		||||
            // 支付时间
 | 
			
		||||
            'pay_time'      => self::getFormatTime($my_time),
 | 
			
		||||
            // 收款账号id
 | 
			
		||||
            'aid'           => $channel['aid'],
 | 
			
		||||
            // 交易终端id
 | 
			
		||||
            'cid'           => $channel['cid'],
 | 
			
		||||
        ];
 | 
			
		||||
        $res = self::create($new_order);
 | 
			
		||||
        if ($res->order_id) {
 | 
			
		||||
            return backMsg(0, 'ok', ['order_id' => $res->order_id]);
 | 
			
		||||
        } else {
 | 
			
		||||
            return backMsg(4, '创建订单记录失败');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 查询订单列表
 | 
			
		||||
    public static function serchOrders($query)
 | 
			
		||||
    {
 | 
			
		||||
        $select = [];
 | 
			
		||||
        $allow_field = ['id', 'order_id', 'pid', 'type', 'out_trade_no', 'name', 'really_price', 'money', 'state', 'create_time_start', 'create_time_end', 'close_time', 'pay_time', 'platform', 'platform_order', 'aid', 'cid',];
 | 
			
		||||
        foreach ($query as $key => $value) {
 | 
			
		||||
            if (in_array($key, $allow_field) && isset($value)) {
 | 
			
		||||
                if ($key === 'name') {
 | 
			
		||||
                    $select[] = [$key, 'like', '%' . $value . '%'];
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                if ($key === 'create_time_start') {
 | 
			
		||||
                    $select[] = ['create_time', '>', $value];
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                if ($key === 'create_time_end') {
 | 
			
		||||
                    $select[] = ['create_time', '<', $value];
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                $select[] = [$key, '=', $value];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return self::where($select);
 | 
			
		||||
    }
 | 
			
		||||
    // 查询订单详细
 | 
			
		||||
    public static function showOrderDetail($id)
 | 
			
		||||
    {
 | 
			
		||||
        $order = self::find($id);
 | 
			
		||||
        $a_list = PayAccount::withTrashed()->find($order->aid);
 | 
			
		||||
        $c_list = PayChannel::withTrashed()->find($order->cid);
 | 
			
		||||
        if (!$order) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        $order->platform = $a_list['platform'] ?? '···';
 | 
			
		||||
        $order->account = $a_list['account'] ?? '···';
 | 
			
		||||
        $order->channel = $c_list['channel'] ?? '···';
 | 
			
		||||
        $order->qrcode = $c_list['qrcode'] ?? '···';
 | 
			
		||||
        $order->url_type = $c_list['type'] ?? '···';
 | 
			
		||||
        return $order->toArray();
 | 
			
		||||
    }
 | 
			
		||||
    // 选择收款通道
 | 
			
		||||
    private static function setChannel($pid, $type): array
 | 
			
		||||
    {
 | 
			
		||||
        // 查询有效收款账户及通道
 | 
			
		||||
        $aids = PayAccount::where('pid', $pid)->where('state', 1)->column('id');
 | 
			
		||||
        if (!$aids) return backMsg(1, '用户无可用收款账户');
 | 
			
		||||
        $channel_infos = PayChannel::whereIn('account_id', $aids)->where('state', 1)->order('last_time', 'asc')->select();
 | 
			
		||||
        if ($channel_infos->isEmpty()) return backMsg(2, '用户账户无可用收款码');
 | 
			
		||||
        // 微信/支付宝收款处理
 | 
			
		||||
        $channel_info = null;
 | 
			
		||||
        foreach ($channel_infos as $key => $value) {
 | 
			
		||||
            $check_wx = preg_match('/^wxpay\d+#/i', $value->channel);
 | 
			
		||||
            $check_ali = preg_match('/^alipay\d+#/i', $value->channel);
 | 
			
		||||
            if ($check_wx && $type === 'wxpay') {
 | 
			
		||||
                $channel_info = $channel_infos[$key];
 | 
			
		||||
                break;
 | 
			
		||||
            } elseif ($check_ali && $type === 'alipay') {
 | 
			
		||||
                $channel_info = $channel_infos[$key];
 | 
			
		||||
                break;
 | 
			
		||||
            } else {
 | 
			
		||||
                if ($check_wx || $check_ali) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                $channel_info = $channel_infos[$key];
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!$channel_info) return backMsg(3, '用户账户无可用收款通道');
 | 
			
		||||
        // 选取收款通道
 | 
			
		||||
        $patt = PayAccount::find($channel_info->account_id);
 | 
			
		||||
        $channel = ['aid' => $channel_info->account_id, 'cid' => $channel_info->id, 'patt' => $patt->getData('pattern'), 'chan' => $channel_info->channel];
 | 
			
		||||
        PayChannel::update(['last_time' => self::getFormatTime(), 'id' => $channel['cid']]);
 | 
			
		||||
        return backMsg(0, 'ok', $channel);
 | 
			
		||||
    }
 | 
			
		||||
    // 获取扩展参数数组
 | 
			
		||||
    // private static function getParams(array $data): array
 | 
			
		||||
    // {
 | 
			
		||||
    //     $keys = ['pid', 'type', 'out_trade_no', 'notify_url', 'return_url', 'name', 'money', 'sign', 'sign_type'];
 | 
			
		||||
    //     $params = [];
 | 
			
		||||
    //     foreach ($data as $key => $value) {
 | 
			
		||||
    //         if (!in_array($key, $keys)) {
 | 
			
		||||
    //             $params[$key] = $value;
 | 
			
		||||
    //         }
 | 
			
		||||
    //     }
 | 
			
		||||
    //     return $params;
 | 
			
		||||
    // }
 | 
			
		||||
    // 检查金额
 | 
			
		||||
    private static function checkMoney($money, $type, $aid, $cid, $chan): float
 | 
			
		||||
    {
 | 
			
		||||
        $money = (float) $money;
 | 
			
		||||
        // Alipay免输
 | 
			
		||||
        if (preg_match('/^alipay4#\d+$/', $chan)) {
 | 
			
		||||
            return $money;
 | 
			
		||||
        }
 | 
			
		||||
        // 查询有效订单
 | 
			
		||||
        $query = self::scope('activeOrder')->where(['type' => $type, 'aid' => $aid, 'cid' => $cid]);
 | 
			
		||||
        $activeOrders = $query->column('really_price');
 | 
			
		||||
        $num = count($activeOrders);
 | 
			
		||||
        if ($num > 0) {
 | 
			
		||||
            for ($i = 0; $i < $num; $i++) {
 | 
			
		||||
                if (in_array($money, $activeOrders)) {
 | 
			
		||||
                    $money += 0.01;
 | 
			
		||||
                } else {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $money;
 | 
			
		||||
    }
 | 
			
		||||
    // 获取格式时间
 | 
			
		||||
    private static function getFormatTime($time = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if ($time) {
 | 
			
		||||
            return date('Y-m-d H:i:s', $time);
 | 
			
		||||
        }
 | 
			
		||||
        return date('Y-m-d H:i:s', time());
 | 
			
		||||
    }
 | 
			
		||||
    // 生成订单号
 | 
			
		||||
    private static function createOrderID(string $prefix = ''): string
 | 
			
		||||
    {
 | 
			
		||||
        return $prefix . date('Ymd') . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
 | 
			
		||||
    }
 | 
			
		||||
    // 查询有效期内的未支付订单
 | 
			
		||||
    public function scopeActiveOrder($query)
 | 
			
		||||
    {
 | 
			
		||||
        $query->where('close_time', '>', self::getFormatTime())->where('state', 0);
 | 
			
		||||
    }
 | 
			
		||||
    // 查询有效期内的成交订单
 | 
			
		||||
    public function scopeDealOrder($query)
 | 
			
		||||
    {
 | 
			
		||||
        $query->where('close_time', '>', self::getFormatTime(time() - self::$activity_time))->where('state', 1);
 | 
			
		||||
    }
 | 
			
		||||
    // 查询超时过期订单
 | 
			
		||||
    public function scopeTimeoutOrder($query)
 | 
			
		||||
    {
 | 
			
		||||
        $query->where('close_time', '<', self::getFormatTime())->where('state', 0);
 | 
			
		||||
    }
 | 
			
		||||
    // 模型多对一关联
 | 
			
		||||
    public function payAccount()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsTo(PayAccount::class, 'aid', 'id');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,89 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\model;
 | 
			
		||||
 | 
			
		||||
use app\BaseModel;
 | 
			
		||||
use app\model\User;
 | 
			
		||||
use app\controller\api\PluginController;
 | 
			
		||||
 | 
			
		||||
class PayAccount extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    // 查询账号列表
 | 
			
		||||
    public static function serchAccount($query)
 | 
			
		||||
    {
 | 
			
		||||
        $select = [];
 | 
			
		||||
        $allow_field = ['state', 'platform', 'account', 'pattern'];
 | 
			
		||||
        foreach ($query as $key => $value) {
 | 
			
		||||
            if (in_array($key, $allow_field) && isset($value)) {
 | 
			
		||||
                if ($key === 'account') {
 | 
			
		||||
                    $select[] = [$key, 'like', '%' . $value . '%'];
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                $select[] = [$key, '=', $value];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return self::withCount(['payChannel' => 'channel'])->where($select);
 | 
			
		||||
    }
 | 
			
		||||
    // 获取账号配置
 | 
			
		||||
    public static function getAccountConfig($aid, $pid = null): array|bool
 | 
			
		||||
    {
 | 
			
		||||
        $aid_info = self::find($aid);
 | 
			
		||||
        if (!$aid_info) return false;
 | 
			
		||||
        // 插件配置
 | 
			
		||||
        $platform = PluginController::getPluginInfo($aid_info->getData('platform'));
 | 
			
		||||
        // 查询参数
 | 
			
		||||
        $params = json_decode($aid_info->params, true);
 | 
			
		||||
        if ($aid_info && $platform) {
 | 
			
		||||
            $config = [
 | 
			
		||||
                'pid'       =>  $aid_info->pid,
 | 
			
		||||
                // 账号id
 | 
			
		||||
                'aid'       =>  $aid_info->id,
 | 
			
		||||
                // 收款平台
 | 
			
		||||
                'platform'  =>  $aid_info->getData('platform'),
 | 
			
		||||
                // 插件类名
 | 
			
		||||
                'payclass'  =>  $platform['class_name'],
 | 
			
		||||
                // 账号
 | 
			
		||||
                'account'   =>  $aid_info->account,
 | 
			
		||||
                // 密码
 | 
			
		||||
                'password'  =>  $aid_info->password,
 | 
			
		||||
                // 配置参数
 | 
			
		||||
                'params'     =>  $params,
 | 
			
		||||
            ];
 | 
			
		||||
            if ($pid !== null) {
 | 
			
		||||
                $pid_info = User::where('pid', $pid)->find();
 | 
			
		||||
                $config['key'] = $pid_info->secret_key;
 | 
			
		||||
            }
 | 
			
		||||
            return $config;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 获取器
 | 
			
		||||
    public function getPlatformAttr($value)
 | 
			
		||||
    {
 | 
			
		||||
        $payplugin_path = config_path() . '/extend/payplugin.php';
 | 
			
		||||
        if (!file_exists($payplugin_path)) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        // 加载插件配置
 | 
			
		||||
        $payplugin_config = require $payplugin_path;
 | 
			
		||||
        $option = [];
 | 
			
		||||
        foreach ($payplugin_config as $config) {
 | 
			
		||||
            $option[$config['platform']] =  $config['name'];
 | 
			
		||||
        }
 | 
			
		||||
        return isset($option[$value]) ? $option[$value] : '[已卸载,请停用]';
 | 
			
		||||
    }
 | 
			
		||||
    public function getPatternAttr($value)
 | 
			
		||||
    {
 | 
			
		||||
        // 监听模式
 | 
			
		||||
        $pattern = ['0' => '单次监听·被动', '1' => '连续监听·主动'];
 | 
			
		||||
        return $pattern[$value];
 | 
			
		||||
    }
 | 
			
		||||
    // 一对多关联
 | 
			
		||||
    public function payChannel()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->hasMany(PayChannel::class, 'account_id', 'id');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\model;
 | 
			
		||||
 | 
			
		||||
use app\BaseModel;
 | 
			
		||||
 | 
			
		||||
class PayChannel extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    // 模型多对一关联
 | 
			
		||||
    public function payAccount()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->belongsTo(PayAccount::class, 'account_id', 'id');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,46 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace app\model;
 | 
			
		||||
 | 
			
		||||
use app\BaseModel;
 | 
			
		||||
 | 
			
		||||
class User extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    // 验证用户
 | 
			
		||||
    public static function checkUser($pid, $sign)
 | 
			
		||||
    {
 | 
			
		||||
        $user = self::where('pid', $pid)->find();
 | 
			
		||||
        $sign2 = md5($user->pid . $user->secret_key);
 | 
			
		||||
        if ($sign === $sign2) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 创建用户
 | 
			
		||||
    public static function createUser(array $userinfo)
 | 
			
		||||
    {
 | 
			
		||||
        $last_pid = self::withTrashed()->max('pid');
 | 
			
		||||
        $find_username = self::withTrashed()->where(['username' => $userinfo['username']])->find();
 | 
			
		||||
        if ($find_username) {
 | 
			
		||||
            return 1; // 账户已注册
 | 
			
		||||
        }
 | 
			
		||||
        $pid = $last_pid ? $last_pid + 1 : 1000;
 | 
			
		||||
        $secret = md5($pid . time() . mt_rand());
 | 
			
		||||
        $res = self::create(['pid' => $pid, 'secret_key' => $secret, 'username' => $userinfo['username'], 'password' => $userinfo['password'], 'nickname' => self::getNickname('小可爱', 5)]);
 | 
			
		||||
        return $res;
 | 
			
		||||
    }
 | 
			
		||||
    // 随机用户昵称
 | 
			
		||||
    private static function getNickname($pre = '', $length = 8)
 | 
			
		||||
    {
 | 
			
		||||
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.';
 | 
			
		||||
        $charactersLength = strlen($characters);
 | 
			
		||||
        $randomString = '';
 | 
			
		||||
        for ($i = 0; $i < $length; $i++) {
 | 
			
		||||
            $randomString .= $characters[rand(0, $charactersLength - 1)];
 | 
			
		||||
        }
 | 
			
		||||
        return $pre . $randomString;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
use app\ExceptionHandle;
 | 
			
		||||
use app\Request;
 | 
			
		||||
 | 
			
		||||
// 容器Provider定义文件
 | 
			
		||||
return [
 | 
			
		||||
    'think\Request'          => Request::class,
 | 
			
		||||
    'think\exception\Handle' => ExceptionHandle::class,
 | 
			
		||||
];
 | 
			
		||||
@@ -1,9 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use app\AppService;
 | 
			
		||||
 | 
			
		||||
// 系统服务定义文件
 | 
			
		||||
// 服务在完成全局初始化之后执行
 | 
			
		||||
return [
 | 
			
		||||
    AppService::class,
 | 
			
		||||
];
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
// +----------------------------------------------------------------------
 | 
			
		||||
// | 后台菜单配置
 | 
			
		||||
// +----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
return [
 | 
			
		||||
  [
 | 
			
		||||
    'id' => 'console',
 | 
			
		||||
    'title' => '平台首页',
 | 
			
		||||
    'icon' => 'icon pear-icon pear-icon-home',
 | 
			
		||||
    'type' => 1,
 | 
			
		||||
    'openType' => '_iframe',
 | 
			
		||||
    'href' => 'Console/console',
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'id' => 'order',
 | 
			
		||||
    'title' => '订单管理',
 | 
			
		||||
    'icon' => 'icon pear-icon pear-icon-survey',
 | 
			
		||||
    'type' => 1,
 | 
			
		||||
    'openType' => '_iframe',
 | 
			
		||||
    'href' => '/Order/index',
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'id' => 'payManage',
 | 
			
		||||
    'title' => '账号管理',
 | 
			
		||||
    'icon' => 'icon pear-icon pear-icon-security',
 | 
			
		||||
    'type' => 1,
 | 
			
		||||
    'openType' => '_iframe',
 | 
			
		||||
    'href' => '/PayManage/index',
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'id' => 'pluginManage',
 | 
			
		||||
    'title' => '插件管理',
 | 
			
		||||
    'icon' => 'icon pear-icon pear-icon-modular',
 | 
			
		||||
    'type' => 1,
 | 
			
		||||
    'openType' => '_iframe',
 | 
			
		||||
    'href' => '/Plugin/index',
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'id' => 'userCenter',
 | 
			
		||||
    'title' => '用户中心',
 | 
			
		||||
    'icon' => 'icon pear-icon pear-icon-user',
 | 
			
		||||
    'type' => 1,
 | 
			
		||||
    'openType' => '_iframe',
 | 
			
		||||
    'href' => '/User/index',
 | 
			
		||||
  ],
 | 
			
		||||
  // [
 | 
			
		||||
  //   'id' => 'system',
 | 
			
		||||
  //   'title' => '系统设置',
 | 
			
		||||
  //   'icon' => 'icon pear-icon pear-icon-import',
 | 
			
		||||
  //   'type' => 1,
 | 
			
		||||
  //   'openType' => '_iframe',
 | 
			
		||||
  //   'href' => '/System/index',
 | 
			
		||||
  // ],
 | 
			
		||||
  // [
 | 
			
		||||
  //   'id' => 'pay',
 | 
			
		||||
  //   'title' => '支付管理',
 | 
			
		||||
  //   'icon' => 'icon pear-icon pear-icon-import',
 | 
			
		||||
  //   'type' => 0,
 | 
			
		||||
  //   'href' => '',
 | 
			
		||||
  //   'children' =>    [
 | 
			
		||||
  //     [
 | 
			
		||||
  //       'id' => 'pay_qrcode_list',
 | 
			
		||||
  //       'title' => '收款账户',
 | 
			
		||||
  //       'icon' => 'icon pear-icon pear-icon-import',
 | 
			
		||||
  //       'type' => 1,
 | 
			
		||||
  //       'openType' => '_iframe',
 | 
			
		||||
  //       'href' => '/PayQrcode/index',
 | 
			
		||||
  //     ],
 | 
			
		||||
  //   ],
 | 
			
		||||
  // ],
 | 
			
		||||
];
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
// +----------------------------------------------------------------------
 | 
			
		||||
// | 支付插件列表
 | 
			
		||||
// +----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
return array (
 | 
			
		||||
  0 => 
 | 
			
		||||
  array (
 | 
			
		||||
    'platform' => 'wxpay',
 | 
			
		||||
    'name' => '微信支付',
 | 
			
		||||
    'class_name' => 'WxPay',
 | 
			
		||||
    'price' => NULL,
 | 
			
		||||
    'describe' => '支持微信个人收款码、赞赏码、经营码、商家码收款,监听回调',
 | 
			
		||||
    'website' => 'https://weixin.qq.com',
 | 
			
		||||
    'helplink' => 'https://f0bmwzqjtq2.feishu.cn/docx/JBshdZWokoWzLmxSKQWcSd4Jncg',
 | 
			
		||||
    'version' => '1.0',
 | 
			
		||||
    'state' => 1,
 | 
			
		||||
  ),
 | 
			
		||||
  1 => 
 | 
			
		||||
  array (
 | 
			
		||||
    'platform' => 'alipay',
 | 
			
		||||
    'name' => '支付宝',
 | 
			
		||||
    'class_name' => 'AliPay',
 | 
			
		||||
    'price' => NULL,
 | 
			
		||||
    'describe' => '支持支付宝个人收款码、经营码收款,监听回调',
 | 
			
		||||
    'website' => 'https://www.alipay.com',
 | 
			
		||||
    'helplink' => 'https://f0bmwzqjtq2.feishu.cn/docx/GfltdDrjxoYXwexhtX8ckDCBn9f',
 | 
			
		||||
    'version' => '1.0',
 | 
			
		||||
    'state' => 1,
 | 
			
		||||
  ),
 | 
			
		||||
  2 => 
 | 
			
		||||
  array (
 | 
			
		||||
    'platform' => 'sqbpay',
 | 
			
		||||
    'name' => '收钱吧',
 | 
			
		||||
    'class_name' => 'ShouQianBa',
 | 
			
		||||
    'price' => NULL,
 | 
			
		||||
    'describe' => '主流移动支付全能收 信用卡,花呗都能用,生意帮手收钱吧,移动收款就用它!',
 | 
			
		||||
    'website' => 'https://www.shouqianba.com',
 | 
			
		||||
    'helplink' => 'https://f0bmwzqjtq2.feishu.cn/docx/WwN7dRqmuoozlIxgiuFclZhCnte',
 | 
			
		||||
    'version' => '1.0',
 | 
			
		||||
    'state' => 1,
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
class Plugin
 | 
			
		||||
{
 | 
			
		||||
    private static $siteUrl = 'https://api.zhaidashi.cn';
 | 
			
		||||
    private static $siteUrl = 'https://api.qcjy.cc';
 | 
			
		||||
    // 获取全部插件(含本地)
 | 
			
		||||
    public static function getAllPlugins(array $local_plugin = []): array
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -128,15 +128,6 @@
 | 
			
		||||
			清空过期
 | 
			
		||||
		</button>
 | 
			
		||||
	</script>
 | 
			
		||||
	<script type="text/html" id="platformTpl">
 | 
			
		||||
		<div>
 | 
			
		||||
			{{# 
 | 
			
		||||
				let platforms = JSON.parse(sessionStorage.getItem('platforms')) || {};
 | 
			
		||||
				let platformName = platforms[d.platform] || '已卸载';
 | 
			
		||||
				return `${platformName} [${d.aid}:${d.cid}]`;
 | 
			
		||||
			}}
 | 
			
		||||
		</div>
 | 
			
		||||
    </script>
 | 
			
		||||
	<script src="/component/layui/layui.js"></script>
 | 
			
		||||
	<script src="/component/pear/pear.js"></script>
 | 
			
		||||
	<script>
 | 
			
		||||
@@ -223,6 +214,11 @@
 | 
			
		||||
				form.render('select');
 | 
			
		||||
				// 会话存储
 | 
			
		||||
				sessionStorage.setItem('platforms', JSON.stringify(platforms));
 | 
			
		||||
				let platformTpl = (d) => {
 | 
			
		||||
					let platforms = JSON.parse(sessionStorage.getItem('platforms')) || {};
 | 
			
		||||
					let platformName = platforms[d.platform] || '已卸载';
 | 
			
		||||
					return `${platformName} [${d.aid}:${d.cid}]`;
 | 
			
		||||
				}
 | 
			
		||||
				// 表格列参数
 | 
			
		||||
				let cols = [[
 | 
			
		||||
					{ type: 'checkbox' },
 | 
			
		||||
@@ -235,7 +231,7 @@
 | 
			
		||||
					{ title: '支付状态', field: 'state', align: 'center', minWidth: 85, templet: '<div>{{# if(d.state==1){return`<span class="layui-badge layui-bg-green">成功</span>`}else{if(new Date(d.close_time)>new Date("<?php echo $servertime ?>")){return`<span class="layui-badge layui-bg-orange">等待</span>`}else{return`<span class="layui-badge layui-bg-gray">过期</span>`} } }}</div>' },
 | 
			
		||||
					{ title: '支付时间', field: 'pay_time', align: 'center', minWidth: 160, templet: '<div>{{= d.pay_time == d.create_time ? "- -" : d.pay_time}}</div>' },
 | 
			
		||||
					{ title: '支付平台', field: 'type', align: 'center', width: 120, templet: '<div>{{# if(d.type=="wxpay"){return`<div class="paytype"><img src="/static/img/wxpay.ico"width="15"><span>微信支付</span></div>`}if(d.type=="alipay"){return`<div class="paytype"><img src="/static/img/alipay.ico"width="15"><span>支付宝</span></div>`}if(d.type=="unionpay"){return`<div class="paytype"><img src="/static/img/unionpay.ico"width="15"><span>云闪付</span></div>`} }}</div>' },
 | 
			
		||||
					{ title: '收款平台[账号:终端]', field: 'platform', align: 'center', minWidth: 160, templet: '#platformTpl' },
 | 
			
		||||
					{ title: '收款平台[账号:终端]', field: 'platform', align: 'center', minWidth: 160, templet: platformTpl },
 | 
			
		||||
					{ title: '操作', align: 'center', width: 120, fixed: 'right', templet: '<div><strong><a href="javascript:;" data-id="{{= d.id }}" class="layui-font-green {{= d.state==1 ? "orderSet-paid" : "orderSet-paying" }}">设置</a></strong></div>' }
 | 
			
		||||
				]]
 | 
			
		||||
				// 表格渲染
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user