validate([ 'email' => 'required|email|exists:users,email', ], [ 'email.exists' => 'Email tidak terdaftar dalam sistem.' ]); $user = User::where('email', $request->email)->first(); // Generate OTP 6 digit $otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT); // Simpan OTP ke database (expired 5 menit) PasswordOtp::updateOrCreate( ['email' => $request->email], [ 'otp' => $otp, 'expires_at' => Carbon::now()->addMinutes(5), ] ); // Kirim email OTP try { $this->sendOtpEmail($user->email, $user->name, $otp); return redirect()->route('password.otp.verify.form', ['email' => $request->email]) ->with('success', 'Kode OTP telah dikirim ke email Anda. Cek inbox/spam folder.'); } catch (\Exception $e) { // Log error \Log::error('OTP Email error: ' . $e->getMessage()); // Untuk development, tampilkan OTP di session if (app()->environment('local')) { return redirect()->route('password.otp.verify.form', ['email' => $request->email]) ->with('dev_otp', $otp) ->with('success', '🔧 Mode Development - Kode OTP Anda:'); } return back()->with('error', 'Gagal mengirim email. Silakan coba lagi.'); } } /** * Tampilkan form verifikasi OTP */ public function showVerifyForm(Request $request) { return view('auth.verify-otp', ['email' => $request->email]); } /** * Verifikasi OTP */ public function verifyOtp(Request $request) { $request->validate([ 'email' => 'required|email', 'otp' => 'required|string|size:6', ]); $otpRecord = PasswordOtp::where('email', $request->email) ->where('otp', $request->otp) ->first(); if (!$otpRecord) { return back()->with('error', 'Kode OTP tidak valid.'); } // Cek apakah OTP sudah expired if (Carbon::now()->gt($otpRecord->expires_at)) { $otpRecord->delete(); return back()->with('error', 'Kode OTP sudah kadaluarsa. Silakan minta kode baru.'); } // OTP valid, arahkan ke form reset password return redirect()->route('password.otp.reset.form', [ 'email' => $request->email, 'otp' => $request->otp ])->with('success', 'Kode valid. Silakan buat password baru.'); } /** * Tampilkan form reset password */ public function showResetForm(Request $request) { return view('auth.reset-password-otp', [ 'email' => $request->email, 'otp' => $request->otp ]); } /** * Reset password dengan OTP */ public function resetPassword(Request $request) { $request->validate([ 'email' => 'required|email', 'otp' => 'required|string|size:6', 'password' => 'required|string|min:8|confirmed', ]); // Verifikasi OTP sekali lagi $otpRecord = PasswordOtp::where('email', $request->email) ->where('otp', $request->otp) ->first(); if (!$otpRecord || Carbon::now()->gt($otpRecord->expires_at)) { return back()->with('error', 'Kode OTP tidak valid atau sudah kadaluarsa.'); } // Update password user $user = User::where('email', $request->email)->first(); $user->password = Hash::make($request->password); $user->save(); // Hapus data OTP $otpRecord->delete(); return redirect()->route('login') ->with('success', 'Password berhasil direset. Silakan login dengan password baru.'); } /** * Kirim email OTP */ private function sendOtpEmail($toEmail, $toName, $otp) { $subject = "Kode OTP Reset Password - LearnMood"; $message = " Kode OTP Reset Password

LearnMood

Halo {$toName},

Kami menerima permintaan reset password untuk akun Anda. Gunakan kode OTP berikut untuk melanjutkan:

Kode Verifikasi OTP

{$otp}

⏰ Berlaku selama: 5 menit
📧 Email: {$toEmail}

Jika Anda tidak meminta reset password, abaikan email ini.

© " . date('Y') . " LearnMood. All rights reserved.

"; $headers = "MIME-Version: 1.0" . "\r\n"; $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n"; $headers .= 'From: LearnMood <' . env('MAIL_FROM_ADDRESS') . '>' . "\r\n"; $headers .= 'Reply-To: ' . env('MAIL_FROM_ADDRESS') . "\r\n"; $headers .= 'X-Mailer: PHP/' . phpversion(); mail($toEmail, $subject, $message, $headers); } }