user(); $transaction = Transaction::where('customer_id', $user->id) ->where('type', 'manual') ->where('status', TransactionStatus::PENDING->value) ->whereNotNull('proof_image') ->latest() ->first(); if (!$transaction) { return response()->json(['error' => false, 'data' => ['has_pending' => false]]); } return response()->json([ 'error' => false, 'data' => [ 'has_pending' => true, 'transaction' => [ 'public_code' => $transaction->public_code, 'amount' => $transaction->amount, 'created_at' => $transaction->created_at?->toISOString(), ], ], ]); } /** * POST /v1/transaction/manual-deposit/prepare * Tạo transaction PENDING và trả về public_code để show trong QR / nội dung CK. * Client gọi khi chuyển sang bước chọn ngân hàng. */ public function prepare(DepositRequest $request): JsonResponse { $user = $request->user(); // Chặn nếu đã có GD đã submit đang chờ admin duyệt $submitted = Transaction::where('customer_id', $user->id) ->where('type', 'manual') ->where('status', TransactionStatus::PENDING->value) ->whereNotNull('proof_image') ->exists(); if ($submitted) { return response()->json([ 'error' => true, 'message' => 'Bạn đang có yêu cầu nạp tiền chờ duyệt. Vui lòng chờ admin xác nhận trước khi tạo yêu cầu mới.', ], 422); } // Huỷ transaction prepare cũ (chưa có proof) nếu còn tồn tại $old = Transaction::where('customer_id', $user->id) ->where('type', 'manual') ->where('status', TransactionStatus::PENDING->value) ->whereNull('proof_image') ->latest() ->first(); if ($old) { DB::transaction(function () use ($old, $user) { $user->total_pending = max(0, $user->total_pending - $old->amount); $user->save(); $old->forceDelete(); }); } $uuid = Str::uuid()->toString(); $publicCode = 'MAN-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)); DB::beginTransaction(); try { Transaction::create([ 'uuid' => $uuid, 'public_code' => $publicCode, 'trans' => 'MAN' . time(), 'amount' => $request->amount, 'customer_id' => $user->id, 'type' => 'manual', 'status' => TransactionStatus::PENDING->value, ]); $user->total_pending += $request->amount; $user->save(); DB::commit(); } catch (\Exception) { DB::rollBack(); return response()->json(['error' => true, 'message' => 'Có lỗi xảy ra, vui lòng thử lại'], 500); } return response()->json([ 'error' => false, 'data' => [ 'uuid' => $uuid, 'public_code' => $publicCode, ], ]); } /** * DELETE /v1/transaction/manual-deposit/{uuid}/cancel * Huỷ transaction đang prepare (chưa có proof, chưa submit). * Client gọi khi user quay lại step 1 để đổi số tiền. */ public function cancel(Request $request, string $uuid): JsonResponse { $user = $request->user(); $transaction = Transaction::where('uuid', $uuid) ->where('customer_id', $user->id) ->where('type', 'manual') ->where('status', TransactionStatus::PENDING->value) ->whereNull('proof_image') ->firstOrFail(); DB::transaction(function () use ($transaction, $user) { $user->total_pending = max(0, $user->total_pending - $transaction->amount); $user->save(); $transaction->forceDelete(); }); return response()->json(['error' => false, 'message' => 'Đã huỷ yêu cầu']); } /** * POST /v1/transaction/manual-deposit/confirm * Attach proof image vào transaction đã prepare rồi đánh dấu đã submit. * Client gọi sau khi upload proof ở step 3. */ public function confirm(Request $request): JsonResponse { $request->validate([ 'uuid' => 'required|string|uuid', 'proof_image' => 'required|string|max:500', ]); $user = $request->user(); $transaction = Transaction::where('uuid', $request->uuid) ->where('customer_id', $user->id) ->where('type', 'manual') ->where('status', TransactionStatus::PENDING->value) ->whereNull('proof_image') ->first(); if (!$transaction) { return response()->json([ 'error' => true, 'message' => 'Giao dịch không hợp lệ hoặc đã được xử lý', ], 422); } $transaction->update(['proof_image' => $request->proof_image]); try { (new Telegram())->SendManualDepositPendingApproval($user, $transaction); } catch (\Exception) { // Không để lỗi Telegram ảnh hưởng response } Log::create([ 'agent' => 'api', 'client_ip' => $request->ip(), 'description' => 'Gửi yêu cầu nạp tiền thủ công: ' . number_format($transaction->amount) . ' VNĐ (chờ duyệt)', 'customer_id' => $user->id, ]); return response()->json([ 'error' => false, 'message' => 'Yêu cầu nạp tiền đã được ghi nhận, vui lòng chờ admin xác nhận', 'data' => ['public_code' => $transaction->public_code], ]); } /** * POST /v1/transaction/manual-deposit/upload-proof * Upload ảnh chứng từ, trả về path để dùng ở bước confirm. */ public function uploadProof(Request $request): JsonResponse { $request->validate([ 'file' => 'required|image|mimes:jpeg,jpg,png|max:5120', ]); $path = $request->file('file')->store('deposit-proofs', 'public'); return response()->json([ 'error' => false, 'url' => Storage::disk('public')->url($path), 'path' => $path, ]); } }