Files
Siro/backend/core/Services/OtpService.php
T

79 lines
2.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// ============================================================
// core/Services/OtpService.php
// تخزين OTP في Redis بدلاً من MySQL (أسرع وأخف)
// ============================================================
class OtpService
{
private ?Redis $redis;
private const OTP_TTL = 300; // 5 دقائق
private const MAX_ATTEMPTS = 3;
private const LOCKOUT_TTL = 1800; // 30 دقيقة إذا تجاوز المحاولات
public function __construct(?Redis $redis)
{
$this->redis = $redis;
}
// ── توليد وحفظ OTP ─────────────────────────────────────
// $digits: عدد الأرقام — الافتراضي 6، يمكن تمرير 3 لـ transit (100–999)
public function generate(string $phone, int $digits = 3): string
{
$min = (int)str_pad('1', $digits, '0'); // digits=3 → 100 | digits=6 → 100000
$max = (int)str_pad('9', $digits, '9'); // digits=3 → 999 | digits=6 → 999999
$otp = str_pad((string)random_int($min, $max), $digits, '0', STR_PAD_LEFT);
if ($this->redis) {
$key = "otp:{$phone}";
$this->redis->setex($key, self::OTP_TTL, password_hash($otp, PASSWORD_BCRYPT));
$this->redis->del("otp:attempts:{$phone}");
}
return $otp;
}
// ── التحقق من OTP ───────────────────────────────────────
public function verify(string $phone, string $inputOtp): bool
{
if (!$this->redis) return false;
// فحص الـ lockout
if ($this->redis->exists("otp:locked:{$phone}")) {
return false;
}
$key = "otp:{$phone}";
$stored = $this->redis->get($key);
if (!$stored) {
return false; // انتهت صلاحية الـ OTP
}
$attemptsKey = "otp:attempts:{$phone}";
if (!password_verify($inputOtp, $stored)) {
$attempts = $this->redis->incr($attemptsKey);
$this->redis->expire($attemptsKey, self::OTP_TTL);
if ($attempts >= self::MAX_ATTEMPTS) {
// قفل لمدة 30 دقيقة
$this->redis->setex("otp:locked:{$phone}", self::LOCKOUT_TTL, '1');
$this->redis->del($key);
}
return false;
}
// نجح التحقق — احذف الـ OTP
$this->redis->del($key);
$this->redis->del($attemptsKey);
return true;
}
// ── فحص هل الرقم مقفل ──────────────────────────────────
public function isLocked(string $phone): bool
{
return $this->redis && (bool)$this->redis->exists("otp:locked:{$phone}");
}
}