vnpayService = $vnpayService; } public function handle(DepositRequest $request) { $user = $request->user(); $transRef = time(); $uuid = Str::uuid(); $publicCode = 'DEP-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)); $transaction = Transaction::create([ 'uuid' => $uuid, 'public_code' => $publicCode, 'trans' => 'DEP' . $transRef, 'amount' => $request->amount, 'customer_id' => $user->id, 'status' => TransactionStatus::PENDING->value, ]); $paymentUrl = $this->vnpayService->createPaymentUrl([ 'amount' => $request->amount, 'transaction_ref' => $transRef, 'transaction_uuid' => $uuid, ]); return response()->json([ 'error' => false, 'message' => 'Tạo yêu cầu nạp tiền thành công', 'data' => [ 'transaction_id' => $transaction->trans, 'transaction_uuid' => $uuid, 'transaction_code' => $publicCode, 'payment_url' => $paymentUrl, ], ]); } protected function redirectToFrontend(string $status = 'failed', ?string $transactionCode = null): \Illuminate\Http\RedirectResponse { $frontendUrl = config('vnpay.frontend_url', 'http://localhost:3000'); $url = $frontendUrl . '/nap-tien/return'; $params = ['status' => $status]; if ($transactionCode) { $params['code'] = $transactionCode; } return redirect()->away($url . '?' . http_build_query($params)); } public function returnCallback(Request $request) { $data = $request->all(); $vnp_TxnRef = $data['vnp_TxnRef'] ?? null; $vnp_Amount = $data['vnp_Amount'] ?? 0; FacadesLog::info('VNPay callback received', [ 'vnp_TxnRef' => $vnp_TxnRef, 'vnp_Amount' => $vnp_Amount, 'user' => $request->user() ? $request->user()->id : 'not authenticated', ]); $transaction = Transaction::where('trans', 'DEP' . $vnp_TxnRef)->first(); if (!$transaction) { return $this->redirectToFrontend('failed'); } if ($transaction->status == TransactionStatus::COMPLETED->value) { return $this->redirectToFrontend('success', $transaction->public_code); } $verify = $this->vnpayService->verifyPayment($data); FacadesLog::info('VNPay callback debug', [ 'isValid' => $verify['isValid'], 'isSuccess' => $verify['isSuccess'] ?? false, 'responseCode' => $data['vnp_ResponseCode'] ?? 'N/A', ]); if (!$verify['isValid']) { return $this->redirectToFrontend('failed', $transaction->public_code); } if (!$verify['isSuccess']) { $transaction->update(['status' => TransactionStatus::FAILED->value]); return $this->redirectToFrontend('failed', $transaction->public_code); } $originalAmount = $transaction->amount * 100; if ($vnp_Amount != $originalAmount) { $transaction->update(['status' => TransactionStatus::FAILED->value]); return $this->redirectToFrontend('failed', $transaction->public_code); } try { DB::beginTransaction(); $transaction->update(['status' => TransactionStatus::COMPLETED->value]); $customer = Customer::find($transaction->customer_id); FacadesLog::info('Customer update debug', [ 'customer_id' => $transaction->customer_id, 'customer_found' => $customer ? true : false, 'transaction_amount' => $transaction->amount, 'current_total' => $customer->total ?? 'N/A', 'current_credit' => $customer->credit ?? 'N/A', ]); $customer->total += $transaction->amount; $customer->credit += $transaction->amount / 100; $customer->save(); FacadesLog::info('Customer after update', [ 'new_total' => $customer->total, 'new_credit' => $customer->credit, ]); Log::create([ 'agent' => 'api', 'client_ip' => $request->ip(), 'description' => 'Nạp tiền vào tài khoản: ' . number_format($transaction->amount) . ' VNĐ (+' . number_format($transaction->amount / 100) . ' credit)', 'customer_id' => $transaction->customer_id, ]); DepositSuccessJob::dispatch([ 'customer_email' => $customer->email, 'customer_name' => $customer->fullname, 'transaction_code' => $transaction->public_code, 'amount' => $transaction->amount, 'credit' => $transaction->amount / 100, 'current_total' => $customer->total, 'current_credit' => $customer->credit, 'deposit_date' => now()->format('d/m/Y H:i:s'), ]); DB::commit(); } catch (\Exception $e) { DB::rollBack(); FacadesLog::error('Payment processing error: ' . $e->getMessage()); return $this->redirectToFrontend('failed', $transaction->public_code); } return $this->redirectToFrontend('success', $transaction->public_code); } public function getTransactions(Request $request) { $user = $request->user(); $transactions = Transaction::where('customer_id', $user->id) ->orderBy('created_at', 'desc') ->get(); return response()->json([ 'error' => false, 'message' => 'Lấy danh sách giao dịch thành công', 'data' => $transactions, ]); } public function getDetailTransaction(Request $request, $slug) { $user = $request->user(); $transaction = Transaction::where('trans', $slug) ->where('customer_id', $user->id) ->first(); if (!$transaction) { return response()->json([ 'error' => true, 'message' => 'Giao dịch không tồn tại', ], 404); } return response()->json([ 'error' => false, 'message' => 'Lấy chi tiết giao dịch thành công', 'data' => $transaction, ]); } public function verifyPayment(Request $request) { $transRef = $request->query('vnp_TxnRef'); if (!$transRef) { return response()->json([ 'error' => true, 'message' => 'Thiếu tham số vnp_TxnRef', ], 400); } $transaction = Transaction::where('trans', 'DEP' . $transRef)->first(); if (!$transaction) { return response()->json([ 'error' => true, 'message' => 'Giao dịch không tồn tại', ], 404); } return response()->json([ 'error' => false, 'message' => 'Lấy thông tin giao dịch thành công', 'data' => [ 'trans' => $transaction->trans, 'amount' => $transaction->amount, 'amount_vnp' => $transaction->amount * 100, 'status' => $transaction->status, 'status_label' => TransactionStatus::from($transaction->status)->label(), 'created_at' => $transaction->created_at, 'updated_at' => $transaction->updated_at, ], ]); } public function getByPublicCode(Request $request) { $code = $request->query('code'); if (!$code) { return response()->json([ 'error' => true, 'message' => 'Thiếu mã giao dịch', ], 400); } $transaction = Transaction::where('public_code', $code)->first(); if (!$transaction) { return response()->json([ 'error' => true, 'message' => 'Giao dịch không tồn tại', ], 404); } return response()->json([ 'error' => false, 'message' => 'Lấy thông tin giao dịch thành công', 'data' => [ 'transaction' => [ 'public_code' => $transaction->public_code, 'amount' => $transaction->amount, 'amount_formatted' => number_format($transaction->amount) . ' VNĐ', 'status' => $transaction->status, 'status_label' => TransactionStatus::from($transaction->status)->label(), 'created_at' => $transaction->created_at->format('Y-m-d H:i:s'), 'paid_at' => $transaction->updated_at->format('Y-m-d H:i:s'), ], 'customer' => [ 'fullname' => $transaction->customer->fullname, 'email' => $transaction->customer->email, 'phone' => $transaction->customer->phone, 'address' => $transaction->customer->address, ], 'balance' => [ 'total' => $transaction->customer->total, 'credit' => $transaction->customer->credit, 'total_formatted' => number_format($transaction->customer->total) . ' VNĐ', 'credit_formatted' => number_format($transaction->customer->credit) . ' điểm', ], ], ]); } }