paymentCenterClient->isEnabled() || ! $this->paymentCenterClient->hasValidConfig()) { return redirect()->route('vip.center')->with('error', 'VIP 支付暂未开放,请联系管理员完成后台配置。'); } $vipLevel = VipLevel::query()->findOrFail((int) $request->validated('vip_level_id')); try { // 先创建本地订单,再向支付中心发起下单,确保回调时有本地单据可追踪。 $vipPaymentOrder = $this->vipPaymentService->createLocalOrder($request->user(), $vipLevel); $remoteOrder = $this->vipPaymentService->createRemoteOrder($vipPaymentOrder); $payUrl = (string) ($remoteOrder['pay_url'] ?? ''); if ($payUrl === '') { throw new RuntimeException('支付中心未返回可用的支付地址。'); } return redirect()->away($payUrl); } catch (\Throwable $exception) { return redirect()->route('vip.center')->with('error', '创建 VIP 支付订单失败:'.$exception->getMessage()); } } /** * 处理支付完成后的同步回调 * * @param Request $request 支付中心跳转返回参数 */ public function handleReturn(Request $request): RedirectResponse { $payload = $request->all(); $vipPaymentOrder = $this->vipPaymentService->findByPaymentOrderNo($request->string('payment_order_no')->toString()) ?? $this->vipPaymentService->findByMerchantOrderNo($request->string('merchant_order_no')->toString()); if (! $vipPaymentOrder) { return redirect()->route('vip.center')->with('error', '未找到对应的 VIP 支付订单,请稍后在后台核对。'); } $this->vipPaymentService->recordSyncReturn($vipPaymentOrder, $payload); try { // 同步回调只做页面回跳,但这里补查一次可让用户尽快看到最终结果。 $vipPaymentOrder = $this->vipPaymentService->syncRemoteStatus($vipPaymentOrder); } catch (\Throwable $exception) { return redirect()->route('vip.center')->with('error', '支付结果正在确认中,请稍后刷新查看。'); } if ($vipPaymentOrder->isVipOpened()) { return redirect()->route('vip.center')->with('success', 'VIP 支付成功,会员已开通。'); } return redirect()->route('vip.center')->with('success', '支付页面已返回,系统正在确认支付结果,请稍后刷新查看。'); } /** * 接收 NovaLink 支付中心的异步通知 * * @param Request $request 支付中心回调请求 */ public function notify(Request $request): Response { $rawBody = $request->getContent(); $signature = (string) $request->header('X-Payment-Signature', ''); if (! $this->paymentCenterClient->isValidWebhookSignature($signature, $rawBody)) { return response('invalid signature', 401); } $payload = $request->json()->all(); $vipPaymentOrder = $this->vipPaymentService->findByPaymentOrderNo($payload['payment_order_no'] ?? null) ?? $this->vipPaymentService->findByMerchantOrderNo($payload['merchant_order_no'] ?? null); if (! $vipPaymentOrder) { return response('order not found', 404); } if (($payload['status'] ?? '') !== 'paid') { return response('ignored', 200); } try { // 异步回调才是最终支付成功依据,这里完成幂等开通 VIP 的核心逻辑。 $this->vipPaymentService->markOrderAsPaid($vipPaymentOrder, $payload, 'async'); return response('success', 200); } catch (\Throwable $exception) { return response('error: '.$exception->getMessage(), 500); } } }