117 lines
3.2 KiB
Dart
117 lines
3.2 KiB
Dart
import 'dart:convert';
|
|
import 'dart:math';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
/// Service for sending OTP emails via SMTP API
|
|
class EmailJSService {
|
|
// Gmail SMTP Configuration
|
|
static const String _senderEmail = 'erdiseptawahyupratamaggwp@gmail.com';
|
|
static const String _appPassword = 'bcwgpewcqxcyhszp';
|
|
static const String _apiUrl = 'https://freeemailapi.vercel.app/sendEmail/';
|
|
|
|
// Store OTP codes temporarily
|
|
static final Map<String, _OTPData> _otpStore = {};
|
|
static String? lastGeneratedOTP;
|
|
|
|
/// Generate a random 5-digit OTP
|
|
static String generateOTP() {
|
|
final random = Random();
|
|
return (10000 + random.nextInt(90000)).toString();
|
|
}
|
|
|
|
/// Send OTP to email
|
|
static Future<bool> sendOTP(String email) async {
|
|
try {
|
|
final otp = generateOTP();
|
|
final expiresAt = DateTime.now().add(const Duration(minutes: 5));
|
|
|
|
_otpStore[email] = _OTPData(otp: otp, expiresAt: expiresAt);
|
|
lastGeneratedOTP = otp;
|
|
|
|
debugPrint('📤 Sending OTP to $email...');
|
|
|
|
final response = await http.post(
|
|
Uri.parse(_apiUrl),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({
|
|
'fromEmail': _senderEmail,
|
|
'passkey': _appPassword,
|
|
'toEmail': email,
|
|
'title': 'Amori Assistant',
|
|
'subject': 'Kode Verifikasi OTP',
|
|
'body':
|
|
'Kode verifikasi Anda adalah: $otp\n\n'
|
|
'Kode ini berlaku selama 5 menit.\n'
|
|
'Jika Anda tidak meminta kode ini, abaikan email ini.',
|
|
}),
|
|
);
|
|
|
|
final data = jsonDecode(response.body);
|
|
final message = data['message']?.toString() ?? '';
|
|
|
|
debugPrint('📥 Email API Response: $message');
|
|
|
|
if (message == 'emailSendSuccess') {
|
|
debugPrint('✅ OTP sent successfully to $email');
|
|
return true;
|
|
} else {
|
|
debugPrint('❌ Failed to send OTP: $message');
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
debugPrint('❌ Error sending OTP: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Verify OTP code
|
|
static bool verifyOTP(String email, String code) {
|
|
final otpData = _otpStore[email];
|
|
|
|
if (otpData == null) {
|
|
debugPrint('❌ No OTP found for $email');
|
|
return false;
|
|
}
|
|
|
|
if (DateTime.now().isAfter(otpData.expiresAt)) {
|
|
debugPrint('❌ OTP expired for $email');
|
|
_otpStore.remove(email);
|
|
return false;
|
|
}
|
|
|
|
if (otpData.otp != code) {
|
|
debugPrint(
|
|
'❌ Invalid OTP for $email (expected: ${otpData.otp}, got: $code)',
|
|
);
|
|
return false;
|
|
}
|
|
|
|
debugPrint('✅ OTP verified for $email');
|
|
_otpStore.remove(email);
|
|
return true;
|
|
}
|
|
|
|
/// Check if OTP exists and is valid (not expired)
|
|
static bool hasValidOTP(String email) {
|
|
final otpData = _otpStore[email];
|
|
if (otpData == null) return false;
|
|
return DateTime.now().isBefore(otpData.expiresAt);
|
|
}
|
|
|
|
/// Clear OTP for email
|
|
static void clearOTP(String email) {
|
|
_otpStore.remove(email);
|
|
}
|
|
|
|
/// Get last generated OTP (for dev mode display)
|
|
static String? getLastOTP() => lastGeneratedOTP;
|
|
}
|
|
|
|
class _OTPData {
|
|
final String otp;
|
|
final DateTime expiresAt;
|
|
|
|
_OTPData({required this.otp, required this.expiresAt});
|
|
}
|