user()->balance; $balances = (new Balance)->thisUser()->latest()->paginate(20); return view('balances.index', compact('balance', 'balances')); } public function store(Request $request): RedirectResponse { $this->validate($request, [ 'amount' => 'required|numeric|min:0.1|max:10000', 'payment' => 'required|in:wechat,alipay', ]); $balance = (new Balance)->create([ 'user_id' => auth('web')->id(), 'amount' => $request->input('amount'), 'payment' => $request->input('payment'), ]); return redirect()->route('balances.show', compact('balance')); } /** * 显示充值页面和状态(ajax) */ public function show(Request $request, Balance $balance): RedirectResponse|JsonResponse|View { if ($balance->isPaid()) { if ($request->ajax()) { return $this->success($balance); } return view('balances.process', compact('balance')); } else { if ($request->ajax()) { return $this->success($balance); } } if ($balance->isOverdue()) { if (now()->diffInDays($balance->created_at) > 1) { if ($request->ajax()) { return $this->forbidden($balance); } return redirect()->route('index')->with('error', '订单已逾期。'); } } $balance->load('user'); $subject = config('app.display_name') . ' 充值'; $order = [ 'out_trade_no' => $balance->order_id, ]; $code = QrCode::size(150); if ($balance->payment === 'wechat') { $pay = $this->xunhu_wechat($balance, $subject); $qr_code = $code->generate($pay['url']); } else { $order['subject'] = $subject; $order['total_amount'] = $balance->amount; $pay = Pay::alipay()->web($order); return view('balances.alipay', ['html' => (string)$pay->getBody()]); } if (!isset($qr_code)) { return redirect()->route('index')->with('error', '支付方式错误。'); } return view('balances.pay', compact('balance', 'qr_code')); } private function xunhu_wechat( Balance $balance, $subject = '支付' ) { $data = [ 'version' => '1.1', 'lang' => 'zh-cn', 'plugins' => config('app.name'), 'appid' => config('pay.xunhu.app_id'), 'trade_order_id' => $balance->order_id, 'payment' => 'wechat', 'type' => 'WAP', 'wap_url' => config('app.url'), 'wap_name' => config('app.display_name'), 'total_fee' => $balance->amount, 'title' => $subject, 'time' => time(), 'notify_url' => route('balances.notify', 'wechat'), 'return_url' => route('balances.notify', 'wechat'), 'callback_url' => route('balances.show', $balance), 'modal' => null, 'nonce_str' => str_shuffle(time()), ]; $data['hash'] = $this->xunhu_hash($data); $response = Http::post(config('pay.xunhu.gateway'), $data); if (!$response->successful()) { return redirect()->route('index')->with('error', '支付网关错误。'); } $response = $response->json(); $hash = $this->xunhu_hash($response); if (!isset($response['hash']) || $response['hash'] !== $hash) { return redirect()->route('index')->with('error', '无法校验支付网关返回数据。'); } return $response; } private function xunhu_hash( array $arr ): string { ksort($arr); $pre = []; foreach ($arr as $key => $data) { if (is_null($data) || $data === '') { continue; } if ($key == 'hash') { continue; } $pre[$key] = stripslashes($data); } $arg = ''; $qty = count($pre); $index = 0; foreach ($pre as $key => $val) { $arg .= "$key=$val"; if ($index++ < ($qty - 1)) { $arg .= '&'; } } return md5($arg . config('pay.xunhu.app_secret')); } /** * @throws ValidationException */ public function notify( Request $request, $payment ): View|JsonResponse { $is_paid = false; if ($payment === 'alipay') { $out_trade_no = $request->input('out_trade_no'); } else if ($payment === 'wechat') { $out_trade_no = $request->input('trade_order_id'); } else { abort(400, '支付方式错误'); } // 检测订单是否存在 $balance = (new Balance)->where('order_id', $out_trade_no)->with('user')->first(); if (!$balance) { abort(404, '找不到订单。'); } // 检测订单是否已支付 if ($balance->paid_at !== null) { if ($request->ajax()) { return $this->success($balance); } return view('balances.process', compact('balance')); } // 处理验证 if ($payment === 'wechat') { if (!($request->filled('hash') || $request->filled('trade_order_id'))) { return $this->error('参数错误。'); } if ($request->filled('plugins') && $request->input('plugins') != config('app.name')) { return $this->error('插件不匹配。'); } $hash = $this->xunhu_hash($request->toArray()); if ($request->input('hash') != $hash) { Log::debug('hash error', $request->toArray()); } if ($request->input('status') === 'OD') { $is_paid = true; } } if ($is_paid) { // $balance->user->charge($balance->amount, $balance->payment, $balance->order_id); $balance->update([ 'paid_at' => now(), ]); } if ($request->ajax()) { return $this->success($balance); } return view('balances.process', compact('balance')); } /** * 获取交易记录 * * @param mixed $request * * @return View */ public function transactions( Request $request ): View { $modules = Module::all(); $transactions = (new Transaction)->where('user_id', auth('web')->id())->where('payment', '!=', 'module_balance'); if ($request->has('type')) { $transactions = $transactions->where('type', $request->type); } if ($request->has('payment')) { $transactions = $transactions->where('payment', $request->payment); } $transactions = $transactions->latest()->paginate(100)->withQueryString(); return view('balances.transactions', compact('transactions', 'modules')); } }