161 lines
5.4 KiB
PHP
161 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* متحكم رموز التحقق (OTP Controller)
|
|
*
|
|
* الغرض من الملف:
|
|
* إدارة إرسال والتحقق من رموز الـ OTP (التي تصل عبر SMS أو البريد الإلكتروني) لضمان ملكية المستخدم للحساب.
|
|
*
|
|
* كيفية العمل:
|
|
* 1. يستقبل رقم الهاتف أو البريد الإلكتروني.
|
|
* 2. يولد رمزاً عشوائياً ويرسله عبر الخدمة المناسبة.
|
|
* 3. يخزن الرمز مؤقتاً للتحقق منه لاحقاً عند إدخاله من قبل المستخدم.
|
|
*/
|
|
class OtpController extends Controller
|
|
{
|
|
/** POST /v2/otp/send */
|
|
public function send(Request $request): JsonResponse
|
|
{
|
|
$request->validate(['phone' => 'required|string']);
|
|
|
|
$phone = $request->input('phone');
|
|
|
|
// Rate limit: 3 OTP per phone per 5 minutes
|
|
$key = "otp_limit:{$phone}";
|
|
if (Cache::get($key, 0) >= 3) {
|
|
return response()->json(['status' => 'failure', 'message' => 'Too many OTP requests'], 429);
|
|
}
|
|
Cache::increment($key);
|
|
Cache::put($key, Cache::get($key), 300);
|
|
|
|
// Generate 6-digit OTP
|
|
$otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
|
|
$expiration = now()->addMinutes(5);
|
|
|
|
// Store OTP
|
|
DB::connection('primary')->table('phone_verification')->updateOrInsert(
|
|
['phone_number' => $phone],
|
|
[
|
|
'token_code' => password_hash($otp, PASSWORD_BCRYPT),
|
|
'expiration_time' => $expiration,
|
|
'is_verified' => 0,
|
|
'created_at' => now(),
|
|
]
|
|
);
|
|
|
|
// TODO: Send SMS via external provider
|
|
// For now, return success (SMS sending is provider-specific)
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'OTP sent',
|
|
'expires_at' => $expiration->toIso8601String(),
|
|
]);
|
|
}
|
|
|
|
/** POST /v2/otp/verify */
|
|
public function verify(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'phone' => 'required|string',
|
|
'otp' => 'required|string|size:6',
|
|
]);
|
|
|
|
$phone = $request->input('phone');
|
|
$otp = $request->input('otp');
|
|
|
|
$record = DB::connection('primary')->table('phone_verification')
|
|
->where('phone_number', $phone)
|
|
->where('is_verified', 0)
|
|
->where('expiration_time', '>', now())
|
|
->first();
|
|
|
|
if (!$record) {
|
|
return response()->json(['status' => 'failure', 'message' => 'OTP expired or not found'], 400);
|
|
}
|
|
|
|
// Verify OTP hash
|
|
if (!password_verify($otp, $record->token_code)) {
|
|
return response()->json(['status' => 'failure', 'message' => 'Invalid OTP'], 400);
|
|
}
|
|
|
|
// Mark as verified
|
|
DB::connection('primary')->table('phone_verification')
|
|
->where('phone_number', $phone)
|
|
->update(['is_verified' => 1]);
|
|
|
|
return response()->json(['status' => 'success', 'message' => 'Phone verified']);
|
|
}
|
|
|
|
/** POST /v2/otp/email/send */
|
|
public function sendEmail(Request $request): JsonResponse
|
|
{
|
|
$request->validate(['email' => 'required|email']);
|
|
|
|
$email = $request->input('email');
|
|
$token = Str::random(32);
|
|
|
|
DB::connection('primary')->table('email_verifications')->updateOrInsert(
|
|
['email' => $email],
|
|
[
|
|
'token' => password_hash($token, PASSWORD_BCRYPT),
|
|
'verified' => 0,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]
|
|
);
|
|
|
|
// TODO: Send email with token link
|
|
|
|
return response()->json(['status' => 'success', 'message' => 'Verification email sent']);
|
|
}
|
|
|
|
/** POST /v2/otp/email/verify */
|
|
public function verifyEmail(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'email' => 'required|email',
|
|
'token' => 'required|string',
|
|
]);
|
|
|
|
$record = DB::connection('primary')->table('email_verifications')
|
|
->where('email', $request->input('email'))
|
|
->where('verified', 0)
|
|
->first();
|
|
|
|
if (!$record || !password_verify($request->input('token'), $record->token)) {
|
|
return response()->json(['status' => 'failure', 'message' => 'Invalid verification'], 400);
|
|
}
|
|
|
|
DB::connection('primary')->table('email_verifications')
|
|
->where('email', $request->input('email'))
|
|
->update(['verified' => 1, 'updated_at' => now()]);
|
|
|
|
return response()->json(['status' => 'success', 'message' => 'Email verified']);
|
|
}
|
|
|
|
/** GET /v2/otp/check-phone?phone=XXX */
|
|
public function checkPhone(Request $request): JsonResponse
|
|
{
|
|
$request->validate(['phone' => 'required|string']);
|
|
|
|
$verified = DB::connection('primary')->table('phone_verification')
|
|
->where('phone_number', $request->input('phone'))
|
|
->where('is_verified', 1)
|
|
->exists();
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'data' => ['verified' => $verified],
|
|
]);
|
|
}
|
|
}
|