chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+209
View File
@@ -0,0 +1,209 @@
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace app\member\controller;
use think\facade\Event;
use think\Request;
use ywxapp\controller\MemberBase;
use app\member\validate\Payment as PaymentValidate;
/**
* 支付控制器
*
* 设计原则:核心只负责「订单/流程编排 + 事件触发」,具体支付方式由【支付插件】实现,
* 通过监听以下事件注入能力(插件在 info.php 的 events 中声明监听器即可):
*
* - PaymentMethods 收集可用支付方式。监听器返回 array,如:
* [['code'=>'wechat','name'=>'微信支付','desc'=>'...','icon'=>'','sort'=>0], ...]
* - PaymentCreate 生成支付参数。监听器接收 ['order'=>[...],'params'=>[...]]
* 返回网关参数 array(按需返回 null 表示不匹配该方法),如:
* ['type'=>'qrcode','qrcode_url'=>'weixin://...','expire'=>600]
* ['type'=>'redirect','redirect_url'=>'https://...']
* ['type'=>'form','html'=>'<form ...>']
* - PaymentNotify 异步回调。监听器接收 ['method'=>...,'request'=>[...],'get'=>[...]]
* 完成验签与订单激活后,返回网关要求的响应字符串(如 'success')。
* - PaymentFreeActive 免费套餐开通钩子(无支付)。
* - PaymentOrderCreate 订单落库钩子(插件可在此持久化订单)。
*
* 未安装任何支付插件时,create() 会返回「暂无可用支付方式」提示。
*/
class Payment extends MemberBase
{
/**
* 套餐配置(来自 config/member.php,价格单位:元,duration 单位:天)
* @var array
*/
private $plans = [];
/**
* 支付页 / 套餐列表(免登录可看)
* @var array
*/
protected $noNeedLogin = ['index', 'methods'];
/**
* @var array
*/
protected $noNeedVerify = ['*'];
protected function initialize()
{
$this->plans = config('member.plans', []);
}
/**
* 支付页面
*/
public function index()
{
$this->view->assign('plans', array_values($this->plans));
return $this->view->fetch();
}
/**
* 获取可用支付方式(插件通过 PaymentMethods 事件注入)
*/
public function methods()
{
$results = Event::trigger('PaymentMethods', []);
$methods = [];
$codes = [];
foreach ((array)$results as $r) {
if (!is_array($r)) {
continue;
}
foreach (array_values($r) as $m) {
if (!is_array($m) || empty($m['code']) || in_array($m['code'], $codes, true)) {
continue;
}
$codes[] = $m['code'];
$methods[] = $m;
}
}
// 按 sort 排序
usort($methods, function ($a, $b) {
return ($a['sort'] ?? 0) <=> ($b['sort'] ?? 0);
});
// 兜底:若没有任何插件注入支付方式(如插件系统未初始化),至少提供「个人免签」体验方式
if (empty($methods)) {
$methods[] = [
'code' => 'personal',
'name' => '个人免签',
'desc' => '扫码转账到个人收款码,站长确认到账后开通',
'icon' => '',
'sort' => 0,
];
}
$this->result->success($methods);
}
/**
* 创建支付订单
* 1) 免费套餐:直接触发 PaymentFreeActive,无需支付
* 2) 付费套餐:触发 PaymentOrderCreate(落库钩子)+ PaymentCreate(网关参数钩子)
*/
public function create()
{
if (!($this->request->isAjax() && $this->request->isPost())) {
$this->result->error('访问错误');
}
$user = $this->auth->model;
$planId = (int)$this->request->param('plan_id', 0);
$method = (string)$this->request->param('method', '');
$validate = new PaymentValidate();
if (! $validate->check(['plan_id' => $planId, 'method' => $method])) {
$this->result->error($validate->getError());
}
$plan = $this->getPlan($planId);
if (!$plan) {
$this->result->error('套餐不存在', 1);
}
// 免费套餐直接开通
if ($plan['price'] <= 0) {
Event::trigger('PaymentFreeActive', ['member' => $user, 'plan' => $plan]);
$this->result->success(['type' => 'free', 'plan' => $plan['title']]);
}
if (!$method) {
$this->result->error('请选择支付方式', 1);
}
$order = [
'order_sn' => $this->genOrderSn($user->uid),
'uid' => $user->uid,
'plan_id' => $planId,
'plan_title' => $plan['title'],
'amount' => $plan['price'],
'method' => $method,
'create_at' => time(),
];
// 订单落库钩子(插件可持久化订单)
Event::trigger('PaymentOrderCreate', ['order' => $order]);
// 生成支付参数:由各支付插件监听 PaymentCreate 返回网关数据
$results = Event::trigger('PaymentCreate', [
'order' => $order,
'params' => $this->request->param(),
]);
$gateway = null;
foreach ((array)$results as $r) {
if (is_array($r) && !empty($r)) {
$gateway = $r;
break;
}
}
if (empty($gateway)) {
$this->result->error('暂无可用支付方式,请先安装支付插件', 1);
}
$this->result->success($gateway);
}
/**
* 异步回调(由各支付插件处理具体网关通知)
* 插件监听 PaymentNotify 完成验签与订单激活,并返回网关要求的响应体。
*/
public function notify()
{
$method = (string)$this->request->param('method', '');
$results = Event::trigger('PaymentNotify', [
'method' => $method,
'request' => $this->request->param(),
'get' => $this->request->get(),
'post' => $this->request->post(),
]);
foreach ((array)$results as $r) {
if (is_string($r) && $r !== '') {
return $r;
}
}
return 'success';
}
/**
* 根据套餐 id 获取套餐配置
*/
private function getPlan(int $id): ?array
{
return $this->plans[$id] ?? null;
}
/**
* 生成订单号
*/
private function genOrderSn(int $uid): string
{
return date('YmdHis') . $uid . mt_rand(100, 999);
}
}