// +---------------------------------------------------------------------- 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} {$rows}
订单号套餐金额方式状态操作
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, ], ]); } }