Initial V2 commit
This commit is contained in:
153
app/Http/Controllers/OtpController.php
Normal file
153
app/Http/Controllers/OtpController.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?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
|
||||
* Replaces: auth/otpmessage.php, verifyOtpMessage.php, sendVerifyEmail.php, etc.
|
||||
*/
|
||||
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],
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user