// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace addon\mqpay\controller;
use think\facade\Db;
use think\facade\Event;
use think\facade\Request;
/**
* 收银台 / 异步回调 / 订单查询(示例插件自有控制器,经 MultiApp 自动路由到 /Mqpay/pay/*)
*/
class Pay
{
/**
* 真实网关异步通知入口:method 来自查询参数,交由 PaymentNotify 事件验签与激活
*/
public function notify()
{
$method = Request::param('method', '');
$result = Event::trigger('PaymentNotify', [
'method' => $method,
'request' => Request::param(),
'get' => Request::get(),
'post' => Request::post(),
]);
foreach ((array) $result as $r) {
if (is_string($r) && $r !== '') {
return $r;
}
}
return 'fail';
}
/**
* 同步跳转页(支付宝 return_url):展示支付结果,引导返回会员中心
*/
public function cashier()
{
$orderSn = Request::param('out_trade_no', '') ?: Request::param('order_sn', '');
$order = $orderSn ? Db::name('Mqpay_order')->where('order_sn', $orderSn)->find() : [];
$paid = !empty($order) && (int) $order['status'] === 1;
$plan = $order['plan_title'] ?? '';
$title = $paid ? '支付成功' : '支付结果确认中';
$tip = $paid ? '会员已开通,可返回会员中心查看。' : '如已支付,请稍候或返回会员中心查看开通状态。';
$html = <<
支付结果
{$title}
套餐:{$plan}
{$tip}
返回会员中心
HTML;
return $html;
}
/**
* 免签支付手动确认到账(站长在管理页点「确认已收款」时调用)
* 请求:POST order_sn=xxx
*/
public function confirm()
{
$orderSn = Request::param('order_sn', '');
// 复用订阅器的确认逻辑(保持单一出口,避免重复实现激活流程)
$subscriber = new \addon\Mqpay\subscribe\Payment();
$result = $subscriber->confirmOrder($orderSn);
return json($result);
}
/**
* 免签支付测试页(用户侧体验 + 联调):选套餐→展示收款码与精确金额→轮询+模拟监听回调
* 路由:/Mqpay/test
*/
public function test()
{
$plans = config('member.plans', []);
$html = $this->renderTestPage(array_values($plans));
return $html;
}
/**
* 安卓监听 App 回调入口(进阶免签自动确认)
* 请求:POST {order_sn?, amount?, sign}
* - order_sn + sign:直接确认指定订单
* - amount + sign:按实际到账金额精确匹配待支付订单(零头防撞单)
* sign = md5(listen_key + amount + order_sn + listen_key)
* 路由:/Mqpay/pay/notify-app
*/
public function notifyApp()
{
$payload = [
'order_sn' => Request::param('order_sn', ''),
'amount' => Request::param('amount', ''),
'sign' => Request::param('sign', ''),
];
$subscriber = new \addon\Mqpay\subscribe\Payment();
$result = $subscriber->notifyApp($payload);
return json($result);
}
/**
* 免签支付管理页:列出待确认订单,一键确认到账
* 路由:/Mqpay/admin
*/
public function admin()
{
$orders = Db::name('Mqpay_order')
->order('id', 'desc')
->limit(50)
->select()
->toArray();
$listenOn = (string) config('mqpay.listen_enable', '0') === '1'
&& (string) config('mqpay.personal_confirm_mode', 'manual') === 'auto';
$html = $this->renderAdminPage($orders, $listenOn);
return $html;
}
/**
* 渲染管理页 HTML(轻量内联,避免引入额外模板引擎依赖)
*/
private function renderAdminPage(array $orders, bool $listenOn = false): string
{
$listenTip = $listenOn
? '已开启「到账自动监听」:安卓监听 App 识别到转账后将自动确认开通,无需手动操作。手动确认仍可用。
'
: '';
$rows = '';
foreach ($orders as $o) {
$status = (int) $o['status'] === 1 ? '已确认' : '待确认';
$confirm = (int) $o['status'] === 1
? ''
: '';
// 监听模拟:仅当开启了监听且订单待支付时显示(按金额触发真实签名校验)
$listen = ((int) $o['status'] === 1 || !$listenOn)
? ''
: $this->listenSimBtn($o['order_sn'], (string) ($o['real_amount'] ?: $o['amount']));
$rows .= <<
| {$o['order_sn']} |
{$o['plan_title']} |
{$o['amount']} |
{$o['method']} |
{$status} |
{$confirm} {$listen} |
ROW;
}
return <<
免签支付管理
免签支付管理:用户扫码转账后,在此确认到账即可开通会员。
{$listenTip}
HTML;
}
/**
* 生成「模拟监听」按钮:用服务端 listen_key 预算签名,避免密钥泄露到前端
*/
private function listenSimBtn(string $orderSn, string $amount): string
{
$key = (string) config('mqpay.listen_key', '');
$sign = $key === '' ? '' : md5($key . $amount . $orderSn . $key);
return '';
}
/**
* 渲染免签测试页 HTML
*/
private function renderTestPage(array $plans): string
{
$planOpts = '';
foreach ($plans as $p) {
if ((float) ($p['price'] ?? 0) <= 0) {
continue; // 测试页只列付费套餐
}
$planOpts .= '';
}
return <<
免签支付测试
免签支付体验
选套餐→扫码转账(精确金额)→自动/手动确认开通。下方可模拟安卓监听回调联调。
HTML;
}
/**
* 前端轮询订单支付状态(供会员中心收银台弹窗调用)
*/
public function query()
{
$orderSn = Request::param('order_sn', '');
$order = Db::name('Mqpay_order')->where('order_sn', $orderSn)->find();
if (!$order) {
return json(['code' => 1, 'message' => '订单不存在']);
}
return json([
'code' => 0,
'data' => [
'paid' => (int) $order['status'] === 1,
'plan' => $order['plan_title'],
'order_sn' => $orderSn,
],
]);
}
}