api changes for register empyer, worker, sponsor
This commit is contained in:
parent
955fb2c67e
commit
121337eea7
@ -191,6 +191,8 @@ public function register(Request $request)
|
|||||||
'email' => 'required|string|email|max:255|unique:users,email|unique:sponsors,email',
|
'email' => 'required|string|email|max:255|unique:users,email|unique:sponsors,email',
|
||||||
'phone' => 'required|string|max:50|unique:employer_profiles,phone|unique:sponsors,mobile',
|
'phone' => 'required|string|max:50|unique:employer_profiles,phone|unique:sponsors,mobile',
|
||||||
'fcm_token' => 'nullable|string|max:255',
|
'fcm_token' => 'nullable|string|max:255',
|
||||||
|
'emirates_id' => 'nullable|array',
|
||||||
|
'address' => 'nullable|string|max:255',
|
||||||
], [
|
], [
|
||||||
'email.unique' => 'This email address is already registered.',
|
'email.unique' => 'This email address is already registered.',
|
||||||
'phone.unique' => 'This mobile number is already registered.',
|
'phone.unique' => 'This mobile number is already registered.',
|
||||||
@ -215,6 +217,29 @@ public function register(Request $request)
|
|||||||
'expires_at' => now()->addMinutes(10)->timestamp
|
'expires_at' => now()->addMinutes(10)->timestamp
|
||||||
], 600);
|
], 600);
|
||||||
|
|
||||||
|
$emiratesIdInput = $request->input('emirates_id');
|
||||||
|
$emiratesIdNumber = null;
|
||||||
|
$emiratesIdExpiry = null;
|
||||||
|
$emiratesIdName = null;
|
||||||
|
$emiratesIdDob = null;
|
||||||
|
$emiratesIdNationality = null;
|
||||||
|
$emiratesIdIssueDate = null;
|
||||||
|
$emiratesIdEmployer = null;
|
||||||
|
$emiratesIdIssuePlace = null;
|
||||||
|
$emiratesIdOccupation = null;
|
||||||
|
|
||||||
|
if (is_array($emiratesIdInput)) {
|
||||||
|
$emiratesIdNumber = $emiratesIdInput['emirates_id_number'] ?? $emiratesIdInput['id_number'] ?? $emiratesIdInput['id number'] ?? $emiratesIdInput['card_number'] ?? null;
|
||||||
|
$emiratesIdExpiry = $emiratesIdInput['expiry_date'] ?? $emiratesIdInput['expiry date'] ?? $emiratesIdInput['emirates_id_expiry'] ?? null;
|
||||||
|
$emiratesIdName = $emiratesIdInput['name'] ?? null;
|
||||||
|
$emiratesIdDob = $emiratesIdInput['date_of_birth'] ?? $emiratesIdInput['date of birth'] ?? $emiratesIdInput['dob'] ?? null;
|
||||||
|
$emiratesIdNationality = $emiratesIdInput['nationality'] ?? null;
|
||||||
|
$emiratesIdIssueDate = $emiratesIdInput['issue_date'] ?? $emiratesIdInput['issue date'] ?? null;
|
||||||
|
$emiratesIdEmployer = $emiratesIdInput['employer'] ?? null;
|
||||||
|
$emiratesIdIssuePlace = $emiratesIdInput['issue_place'] ?? $emiratesIdInput['issue place'] ?? null;
|
||||||
|
$emiratesIdOccupation = $emiratesIdInput['occupation'] ?? $emiratesIdInput['occuption'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
// Create inactive User
|
// Create inactive User
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
@ -235,6 +260,16 @@ public function register(Request $request)
|
|||||||
'verification_status' => 'pending',
|
'verification_status' => 'pending',
|
||||||
'language' => 'English',
|
'language' => 'English',
|
||||||
'notifications' => true,
|
'notifications' => true,
|
||||||
|
'emirates_id_number' => $emiratesIdNumber,
|
||||||
|
'emirates_id_expiry' => $emiratesIdExpiry,
|
||||||
|
'emirates_id_name' => $emiratesIdName,
|
||||||
|
'emirates_id_dob' => $emiratesIdDob,
|
||||||
|
'nationality' => $emiratesIdNationality,
|
||||||
|
'emirates_id_issue_date' => $emiratesIdIssueDate,
|
||||||
|
'emirates_id_employer' => $emiratesIdEmployer,
|
||||||
|
'emirates_id_issue_place' => $emiratesIdIssuePlace,
|
||||||
|
'emirates_id_occupation' => $emiratesIdOccupation,
|
||||||
|
'address' => $request->address,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Create unverified Sponsor
|
// Create unverified Sponsor
|
||||||
@ -253,6 +288,8 @@ public function register(Request $request)
|
|||||||
'otp_verified_at' => null,
|
'otp_verified_at' => null,
|
||||||
'status' => 'inactive',
|
'status' => 'inactive',
|
||||||
'fcm_token' => $request->fcm_token,
|
'fcm_token' => $request->fcm_token,
|
||||||
|
'emirates_id' => $emiratesIdNumber,
|
||||||
|
'address' => $request->address,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($request->filled('fcm_token')) {
|
if ($request->filled('fcm_token')) {
|
||||||
@ -723,144 +760,4 @@ public function resetPassword(Request $request)
|
|||||||
], 500);
|
], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /api/employers/register/emirates-front
|
|
||||||
*/
|
|
||||||
public function uploadEmiratesIdFront(Request $request)
|
|
||||||
{
|
|
||||||
$validator = Validator::make($request->all(), [
|
|
||||||
'email' => 'required|string|email|max:255',
|
|
||||||
'emirates_id_front' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
||||||
], [
|
|
||||||
'email.required' => 'The email address is required.',
|
|
||||||
'emirates_id_front.required' => 'Please upload the front side of your Emirates ID.',
|
|
||||||
'emirates_id_front.mimes' => 'The Emirates ID front must be an image or a PDF.',
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Validation error.',
|
|
||||||
'errors' => $validator->errors()
|
|
||||||
], 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$user = User::where('email', $request->email)->first();
|
|
||||||
$sponsor = \App\Models\Sponsor::where('email', $request->email)->first();
|
|
||||||
|
|
||||||
if (!$user) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'User account not found.'
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$frontFile = $request->file('emirates_id_front');
|
|
||||||
$frontOcr = \App\Services\OcrDocumentService::extractEmiratesIdFrontData($frontFile);
|
|
||||||
$extractedIdNumber = $frontOcr['emirates_id_number'];
|
|
||||||
|
|
||||||
// Update or create EmployerProfile
|
|
||||||
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
|
||||||
if (!$profile) {
|
|
||||||
$profile = EmployerProfile::create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'company_name' => $user->name . ' Household',
|
|
||||||
'phone' => $sponsor ? $sponsor->mobile : '+971 50 123 4567',
|
|
||||||
'country' => 'United Arab Emirates',
|
|
||||||
'verification_status' => 'pending',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$profile->update([
|
|
||||||
'emirates_id_number' => $extractedIdNumber,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Emirates ID front uploaded and processed successfully.',
|
|
||||||
'ocr_extracted_data' => $frontOcr,
|
|
||||||
'email' => $request->email,
|
|
||||||
], 200);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
logger()->error('API Emirates ID Front Upload Failure: ' . $e->getMessage());
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'An error occurred while uploading the Emirates ID front.',
|
|
||||||
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
||||||
], 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /api/employers/register/emirates-back
|
|
||||||
*/
|
|
||||||
public function uploadEmiratesIdBack(Request $request)
|
|
||||||
{
|
|
||||||
$validator = Validator::make($request->all(), [
|
|
||||||
'email' => 'required|string|email|max:255',
|
|
||||||
'emirates_id_back' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
||||||
], [
|
|
||||||
'email.required' => 'The email address is required.',
|
|
||||||
'emirates_id_back.required' => 'Please upload the back side of your Emirates ID.',
|
|
||||||
'emirates_id_back.mimes' => 'The Emirates ID back must be an image or a PDF.',
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Validation error.',
|
|
||||||
'errors' => $validator->errors()
|
|
||||||
], 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$user = User::where('email', $request->email)->first();
|
|
||||||
$sponsor = \App\Models\Sponsor::where('email', $request->email)->first();
|
|
||||||
|
|
||||||
if (!$user) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'User account not found.'
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$backFile = $request->file('emirates_id_back');
|
|
||||||
$backOcr = \App\Services\OcrDocumentService::extractEmiratesIdBackData($backFile);
|
|
||||||
$extractedExpiry = $backOcr['expiry_date'];
|
|
||||||
|
|
||||||
// Update or create EmployerProfile
|
|
||||||
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
|
||||||
if (!$profile) {
|
|
||||||
$profile = EmployerProfile::create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'company_name' => $user->name . ' Household',
|
|
||||||
'phone' => $sponsor ? $sponsor->mobile : '+971 50 123 4567',
|
|
||||||
'country' => 'United Arab Emirates',
|
|
||||||
'verification_status' => 'pending',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$profile->update([
|
|
||||||
'emirates_id_expiry' => $extractedExpiry,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Emirates ID back uploaded and processed successfully.',
|
|
||||||
'ocr_extracted_data' => $backOcr,
|
|
||||||
'email' => $request->email,
|
|
||||||
], 200);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
logger()->error('API Emirates ID Back Upload Failure: ' . $e->getMessage());
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'An error occurred while uploading the Emirates ID back.',
|
|
||||||
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
||||||
], 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
46
app/Http/Controllers/Api/GoogleVisionOcrController.php
Normal file
46
app/Http/Controllers/Api/GoogleVisionOcrController.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Services\GoogleVisionOcrService;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
class GoogleVisionOcrController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Upload passport image and extract fields using Google Cloud Vision API.
|
||||||
|
*/
|
||||||
|
public function extractPassport(Request $request)
|
||||||
|
{
|
||||||
|
$validator = Validator::make($request->all(), [
|
||||||
|
'passport_file' => 'required|file|image|max:10240', // Max 10MB
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Validation error.',
|
||||||
|
'errors' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $request->file('passport_file');
|
||||||
|
|
||||||
|
$result = GoogleVisionOcrService::extractPassportData($file);
|
||||||
|
|
||||||
|
if (!$result['success']) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $result['message'] ?? 'Could not extract data from passport image.',
|
||||||
|
'data' => $result['data'] ?? null,
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $result['data'],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,8 +26,8 @@ public function register(Request $request)
|
|||||||
'full_name' => 'required|string|max:255',
|
'full_name' => 'required|string|max:255',
|
||||||
'mobile' => 'required|string|max:50|unique:sponsors,mobile|unique:employer_profiles,phone',
|
'mobile' => 'required|string|max:50|unique:sponsors,mobile|unique:employer_profiles,phone',
|
||||||
'password' => 'required|string|min:6',
|
'password' => 'required|string|min:6',
|
||||||
'license_file' => 'nullable|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
'license' => 'nullable|array',
|
||||||
'emirates_id_file' => 'nullable|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
'emirates_id' => 'nullable|array',
|
||||||
'organization_name' => 'required|string|max:255',
|
'organization_name' => 'required|string|max:255',
|
||||||
'email' => 'required|email|max:255|unique:sponsors,email|unique:users,email',
|
'email' => 'required|email|max:255|unique:sponsors,email|unique:users,email',
|
||||||
'nationality' => 'required|string|max:100',
|
'nationality' => 'required|string|max:100',
|
||||||
@ -58,22 +58,44 @@ public function register(Request $request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
$licenseExpiry = $request->license_expiry;
|
$licenseExpiry = $request->license_expiry;
|
||||||
if ($request->hasFile('license_file')) {
|
$licenseInput = $request->input('license');
|
||||||
$licenseFile = $request->file('license_file');
|
if (is_array($licenseInput)) {
|
||||||
$ocrLicense = \App\Services\OcrDocumentService::extractSponsorLicenseData($licenseFile);
|
$licenseExpiry = $licenseExpiry ?? ($licenseInput['expiry_date'] ?? $licenseInput['license_expiry'] ?? null);
|
||||||
$licenseExpiry = $licenseExpiry ?? $ocrLicense['expiry_date'];
|
}
|
||||||
|
|
||||||
|
$emiratesIdInput = $request->input('emirates_id');
|
||||||
|
$emiratesId = null;
|
||||||
|
$emiratesIdExpiry = null;
|
||||||
|
$emiratesIdName = null;
|
||||||
|
$emiratesIdDob = null;
|
||||||
|
$emiratesIdNationality = null;
|
||||||
|
$emiratesIdIssueDate = null;
|
||||||
|
$emiratesIdEmployer = null;
|
||||||
|
$emiratesIdIssuePlace = null;
|
||||||
|
$emiratesIdOccupation = null;
|
||||||
|
|
||||||
|
if (is_array($emiratesIdInput)) {
|
||||||
|
$emiratesId = $emiratesIdInput['emirates_id_number'] ?? $emiratesIdInput['id_number'] ?? $emiratesIdInput['id number'] ?? $emiratesIdInput['card_number'] ?? null;
|
||||||
|
$emiratesIdExpiry = $emiratesIdInput['expiry_date'] ?? $emiratesIdInput['expiry date'] ?? $emiratesIdInput['emirates_id_expiry'] ?? null;
|
||||||
|
$emiratesIdName = $emiratesIdInput['name'] ?? null;
|
||||||
|
$emiratesIdDob = $emiratesIdInput['date_of_birth'] ?? $emiratesIdInput['date of birth'] ?? $emiratesIdInput['dob'] ?? null;
|
||||||
|
$emiratesIdNationality = $emiratesIdInput['nationality'] ?? null;
|
||||||
|
$emiratesIdIssueDate = $emiratesIdInput['issue_date'] ?? $emiratesIdInput['issue date'] ?? null;
|
||||||
|
$emiratesIdEmployer = $emiratesIdInput['employer'] ?? null;
|
||||||
|
$emiratesIdIssuePlace = $emiratesIdInput['issue_place'] ?? $emiratesIdInput['issue place'] ?? null;
|
||||||
|
$emiratesIdOccupation = $emiratesIdInput['occupation'] ?? $emiratesIdInput['occuption'] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$emiratesIdPath = null;
|
$emiratesIdPath = null;
|
||||||
if ($request->hasFile('emirates_id_file')) {
|
|
||||||
$emiratesIdFile = $request->file('emirates_id_file');
|
|
||||||
\App\Services\OcrDocumentService::extractEmiratesIdFrontData($emiratesIdFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
$licensePath = null;
|
$licensePath = null;
|
||||||
$apiToken = Str::random(80);
|
$apiToken = Str::random(80);
|
||||||
|
|
||||||
$sponsor = DB::transaction(function () use ($request, $email, $licensePath, $emiratesIdPath, $apiToken, $licenseExpiry) {
|
$sponsor = DB::transaction(function () use (
|
||||||
|
$request, $email, $licensePath, $emiratesIdPath, $emiratesId, $apiToken, $licenseExpiry,
|
||||||
|
$emiratesIdExpiry, $emiratesIdName, $emiratesIdDob, $emiratesIdIssueDate, $emiratesIdEmployer,
|
||||||
|
$emiratesIdIssuePlace, $emiratesIdOccupation
|
||||||
|
) {
|
||||||
return Sponsor::create([
|
return Sponsor::create([
|
||||||
'full_name' => $request->full_name,
|
'full_name' => $request->full_name,
|
||||||
'organization_name' => $request->organization_name,
|
'organization_name' => $request->organization_name,
|
||||||
@ -86,12 +108,20 @@ public function register(Request $request)
|
|||||||
'address' => $request->address,
|
'address' => $request->address,
|
||||||
'license_file' => $licensePath,
|
'license_file' => $licensePath,
|
||||||
'emirates_id_file' => $emiratesIdPath,
|
'emirates_id_file' => $emiratesIdPath,
|
||||||
|
'emirates_id' => $emiratesId,
|
||||||
'license_expiry' => $licenseExpiry,
|
'license_expiry' => $licenseExpiry,
|
||||||
'status' => 'active',
|
'status' => 'active',
|
||||||
'is_verified' => false,
|
'is_verified' => false,
|
||||||
'subscription_status' => 'none',
|
'subscription_status' => 'none',
|
||||||
'api_token' => $apiToken,
|
'api_token' => $apiToken,
|
||||||
'fcm_token' => $request->fcm_token,
|
'fcm_token' => $request->fcm_token,
|
||||||
|
'emirates_id_name' => $emiratesIdName,
|
||||||
|
'emirates_id_dob' => $emiratesIdDob,
|
||||||
|
'emirates_id_issue_date' => $emiratesIdIssueDate,
|
||||||
|
'emirates_id_expiry' => $emiratesIdExpiry,
|
||||||
|
'emirates_id_employer' => $emiratesIdEmployer,
|
||||||
|
'emirates_id_issue_place' => $emiratesIdIssuePlace,
|
||||||
|
'emirates_id_occupation' => $emiratesIdOccupation,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -189,119 +219,7 @@ public function login(Request $request)
|
|||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload Sponsor's Emirates ID Front.
|
|
||||||
* POST /api/sponsors/register/emirates-front
|
|
||||||
*/
|
|
||||||
public function uploadEmiratesIdFront(Request $request)
|
|
||||||
{
|
|
||||||
$validator = Validator::make($request->all(), [
|
|
||||||
'email' => 'required|string|email|max:255',
|
|
||||||
'emirates_id_front' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
||||||
], [
|
|
||||||
'email.required' => 'The email address is required.',
|
|
||||||
'emirates_id_front.required' => 'Please upload the front side of your Emirates ID.',
|
|
||||||
'emirates_id_front.mimes' => 'The Emirates ID front must be an image or a PDF.',
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Validation error.',
|
|
||||||
'errors' => $validator->errors()
|
|
||||||
], 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$sponsor = Sponsor::where('email', $request->email)->first();
|
|
||||||
|
|
||||||
if (!$sponsor) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Sponsor account not found.'
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$frontFile = $request->file('emirates_id_front');
|
|
||||||
$frontOcr = \App\Services\OcrDocumentService::extractEmiratesIdFrontData($frontFile);
|
|
||||||
|
|
||||||
$sponsor->update([
|
|
||||||
'emirates_id_file' => null,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Emirates ID front uploaded and processed successfully.',
|
|
||||||
'ocr_extracted_data' => $frontOcr,
|
|
||||||
'email' => $request->email,
|
|
||||||
], 200);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
logger()->error('API Sponsor Emirates ID Front Upload Failure: ' . $e->getMessage());
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'An error occurred while uploading the Emirates ID front.',
|
|
||||||
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
||||||
], 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload Sponsor's Emirates ID Back.
|
|
||||||
* POST /api/sponsors/register/emirates-back
|
|
||||||
*/
|
|
||||||
public function uploadEmiratesIdBack(Request $request)
|
|
||||||
{
|
|
||||||
$validator = Validator::make($request->all(), [
|
|
||||||
'email' => 'required|string|email|max:255',
|
|
||||||
'emirates_id_back' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
|
||||||
], [
|
|
||||||
'email.required' => 'The email address is required.',
|
|
||||||
'emirates_id_back.required' => 'Please upload the back side of your Emirates ID.',
|
|
||||||
'emirates_id_back.mimes' => 'The Emirates ID back must be an image or a PDF.',
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Validation error.',
|
|
||||||
'errors' => $validator->errors()
|
|
||||||
], 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$sponsor = Sponsor::where('email', $request->email)->first();
|
|
||||||
|
|
||||||
if (!$sponsor) {
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Sponsor account not found.'
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$backFile = $request->file('emirates_id_back');
|
|
||||||
$backOcr = \App\Services\OcrDocumentService::extractEmiratesIdBackData($backFile);
|
|
||||||
|
|
||||||
$sponsor->update([
|
|
||||||
'emirates_id_file' => null,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Emirates ID back uploaded and processed successfully.',
|
|
||||||
'ocr_extracted_data' => $backOcr,
|
|
||||||
'email' => $request->email,
|
|
||||||
], 200);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
logger()->error('API Sponsor Emirates ID Back Upload Failure: ' . $e->getMessage());
|
|
||||||
return response()->json([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'An error occurred while uploading the Emirates ID back.',
|
|
||||||
'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error'
|
|
||||||
], 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload Sponsor's License document.
|
* Upload Sponsor's License document.
|
||||||
|
|||||||
@ -22,6 +22,13 @@ class EmployerProfile extends Model
|
|||||||
'accommodation',
|
'accommodation',
|
||||||
'district',
|
'district',
|
||||||
'emirates_id_number',
|
'emirates_id_number',
|
||||||
'emirates_id_expiry'
|
'emirates_id_expiry',
|
||||||
|
'emirates_id_name',
|
||||||
|
'emirates_id_dob',
|
||||||
|
'emirates_id_issue_date',
|
||||||
|
'emirates_id_employer',
|
||||||
|
'emirates_id_issue_place',
|
||||||
|
'emirates_id_occupation',
|
||||||
|
'address'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,8 +33,16 @@ class Sponsor extends Authenticatable
|
|||||||
'api_token',
|
'api_token',
|
||||||
'license_file',
|
'license_file',
|
||||||
'emirates_id_file',
|
'emirates_id_file',
|
||||||
|
'emirates_id',
|
||||||
'license_expiry',
|
'license_expiry',
|
||||||
'fcm_token',
|
'fcm_token',
|
||||||
|
'emirates_id_name',
|
||||||
|
'emirates_id_dob',
|
||||||
|
'emirates_id_issue_date',
|
||||||
|
'emirates_id_expiry',
|
||||||
|
'emirates_id_employer',
|
||||||
|
'emirates_id_issue_place',
|
||||||
|
'emirates_id_occupation',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
|
|||||||
323
app/Services/GoogleVisionOcrService.php
Normal file
323
app/Services/GoogleVisionOcrService.php
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class GoogleVisionOcrService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Call Google Cloud Vision API to detect text in the uploaded image.
|
||||||
|
*/
|
||||||
|
public static function extractPassportData(UploadedFile $file): array
|
||||||
|
{
|
||||||
|
$apiKey = config('services.google.vision_api_key');
|
||||||
|
if (empty($apiKey)) {
|
||||||
|
Log::warning('Google Vision API key is not configured.');
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Google Vision API key is not configured.',
|
||||||
|
'data' => self::emptyData(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$base64Image = base64_encode($file->get());
|
||||||
|
|
||||||
|
$url = "https://vision.googleapis.com/v1/images:annotate?key={$apiKey}";
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'requests' => [
|
||||||
|
[
|
||||||
|
'image' => [
|
||||||
|
'content' => $base64Image,
|
||||||
|
],
|
||||||
|
'features' => [
|
||||||
|
[
|
||||||
|
'type' => 'TEXT_DETECTION',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = Http::withoutVerifying()->timeout(30)->post($url, $payload);
|
||||||
|
|
||||||
|
if (!$response->successful()) {
|
||||||
|
Log::error('Google Vision API error response: ' . $response->body());
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Google Vision API request failed.',
|
||||||
|
'data' => self::emptyData(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = $response->json();
|
||||||
|
$fullText = $json['responses'][0]['fullTextAnnotation']['text'] ?? '';
|
||||||
|
|
||||||
|
if (empty($fullText)) {
|
||||||
|
Log::warning('Google Vision API did not detect any text.');
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'data' => self::emptyData(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$parsedData = self::parsePassportText($fullText);
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'data' => $parsedData,
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Google Vision OCR exception: ' . $e->getMessage());
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'An error occurred during OCR processing.',
|
||||||
|
'data' => self::emptyData(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function emptyData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'date_of_birth' => null,
|
||||||
|
'date_of_expiry' => null,
|
||||||
|
'date_of_issue' => null,
|
||||||
|
'given_names' => null,
|
||||||
|
'issuing_country' => null,
|
||||||
|
'nationality' => null,
|
||||||
|
'passport_number' => null,
|
||||||
|
'place_of_birth' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse raw OCR text to extract passport fields.
|
||||||
|
*/
|
||||||
|
public static function parsePassportText(string $text): array
|
||||||
|
{
|
||||||
|
$data = self::emptyData();
|
||||||
|
|
||||||
|
// Split text into lines
|
||||||
|
$lines = explode("\n", $text);
|
||||||
|
$cleanLines = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if ($line !== '') {
|
||||||
|
$cleanLines[] = $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse MRZ first
|
||||||
|
$mrzLines = self::findMrzLines($cleanLines);
|
||||||
|
if ($mrzLines) {
|
||||||
|
$mrzData = self::parseMrz($mrzLines[0], $mrzLines[1]);
|
||||||
|
foreach ($mrzData as $key => $val) {
|
||||||
|
if ($val !== null) {
|
||||||
|
$data[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Passport Number fallback ─────────────────────────────────────────
|
||||||
|
if (empty($data['passport_number'])) {
|
||||||
|
foreach ($cleanLines as $line) {
|
||||||
|
if (preg_match('/(?:passport|pass\s*no|doc\s*no|document|number|no\.?)\s*[:\-\s]\s*([A-Z0-9]{7,9})/i', $line, $matches)) {
|
||||||
|
$data['passport_number'] = strtoupper($matches[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($data['passport_number'])) {
|
||||||
|
foreach ($cleanLines as $line) {
|
||||||
|
if (preg_match('/\b([A-Z][0-9]{7,8}|[A-Z0-9]{8,9})\b/', $line, $matches)) {
|
||||||
|
$data['passport_number'] = strtoupper($matches[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Given Names fallback ─────────────────────────────────────────────
|
||||||
|
if (empty($data['given_names'])) {
|
||||||
|
foreach ($cleanLines as $line) {
|
||||||
|
if (preg_match('/(?:given\s*names?|first\s*names?|name|nom)\s*[:\-\s]\s*([A-Z\s]{2,})/i', $line, $matches)) {
|
||||||
|
$data['given_names'] = trim(strtoupper($matches[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Place of Birth fallback ──────────────────────────────────────────
|
||||||
|
if (empty($data['place_of_birth'])) {
|
||||||
|
foreach ($cleanLines as $line) {
|
||||||
|
if (preg_match('/(?:place\s*of\s*birth|lieu\s*de\s*naissance|birthplace)\s*[:\-\s]\s*([A-Z\s]{2,})/i', $line, $matches)) {
|
||||||
|
$data['place_of_birth'] = trim(strtoupper($matches[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Dates fallback ───────────────────────────────────────────────────
|
||||||
|
if (empty($data['date_of_birth'])) {
|
||||||
|
$data['date_of_birth'] = self::findDateByKeyword($cleanLines, ['birth', 'dob', 'naissance']);
|
||||||
|
}
|
||||||
|
if (empty($data['date_of_expiry'])) {
|
||||||
|
$data['date_of_expiry'] = self::findDateByKeyword($cleanLines, ['expiry', 'expiration', 'exp', 'valable']);
|
||||||
|
}
|
||||||
|
if (empty($data['date_of_issue'])) {
|
||||||
|
$data['date_of_issue'] = self::findDateByKeyword($cleanLines, ['issue', 'delivrance', 'issued']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Nationality fallback ─────────────────────────────────────────────
|
||||||
|
if (empty($data['nationality'])) {
|
||||||
|
foreach ($cleanLines as $line) {
|
||||||
|
if (preg_match('/(?:nationality|nationalite)\s*[:\-\s]\s*([A-Z\s]{3,})/i', $line, $matches)) {
|
||||||
|
$data['nationality'] = trim(strtoupper($matches[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Issuing Country fallback ─────────────────────────────────────────
|
||||||
|
if (empty($data['issuing_country'])) {
|
||||||
|
foreach ($cleanLines as $line) {
|
||||||
|
if (preg_match('/(?:issuing\s*state|issuing\s*country|country)\s*[:\-\s]\s*([A-Z\s]{3,})/i', $line, $matches)) {
|
||||||
|
$data['issuing_country'] = trim(strtoupper($matches[1]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look for MRZ lines in clean lines.
|
||||||
|
*/
|
||||||
|
private static function findMrzLines(array $lines): ?array
|
||||||
|
{
|
||||||
|
$candidateLines = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$cleaned = str_replace(' ', '', $line);
|
||||||
|
$cleaned = str_replace('«', '<', $cleaned);
|
||||||
|
if (strlen($cleaned) >= 35 && strlen($cleaned) <= 50) {
|
||||||
|
$candidateLines[] = [
|
||||||
|
'original' => $line,
|
||||||
|
'cleaned' => $cleaned
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($candidateLines); $i++) {
|
||||||
|
$c1 = $candidateLines[$i]['cleaned'];
|
||||||
|
if (strpos($c1, 'P') === 0) {
|
||||||
|
for ($j = 0; $j < count($candidateLines); $j++) {
|
||||||
|
if ($i === $j) continue;
|
||||||
|
$c2 = $candidateLines[$j]['cleaned'];
|
||||||
|
if (preg_match('/^[A-Z0-9<]{35,50}$/', $c2)) {
|
||||||
|
if (preg_match('/\d{6}/', $c2)) {
|
||||||
|
return [$candidateLines[$i]['cleaned'], $candidateLines[$j]['cleaned']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse standard 44-character passport MRZ lines.
|
||||||
|
*/
|
||||||
|
private static function parseMrz(string $line1, string $line2): array
|
||||||
|
{
|
||||||
|
$data = self::emptyData();
|
||||||
|
|
||||||
|
$line1 = str_pad(substr($line1, 0, 44), 44, '<');
|
||||||
|
$line2 = str_pad(substr($line2, 0, 44), 44, '<');
|
||||||
|
|
||||||
|
$data['issuing_country'] = str_replace('<', '', substr($line1, 2, 3));
|
||||||
|
|
||||||
|
$namePart = substr($line1, 5);
|
||||||
|
$parts = explode('<<', $namePart);
|
||||||
|
$surname = isset($parts[0]) ? str_replace('<', ' ', $parts[0]) : '';
|
||||||
|
$givenNames = isset($parts[1]) ? str_replace('<', ' ', $parts[1]) : '';
|
||||||
|
|
||||||
|
$data['given_names'] = trim(strtoupper($givenNames));
|
||||||
|
if (empty($data['given_names'])) {
|
||||||
|
$data['given_names'] = trim(strtoupper($surname));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['passport_number'] = str_replace('<', '', substr($line2, 0, 9));
|
||||||
|
$data['nationality'] = str_replace('<', '', substr($line2, 10, 3));
|
||||||
|
|
||||||
|
$rawDob = substr($line2, 13, 6);
|
||||||
|
if (ctype_digit($rawDob)) {
|
||||||
|
$data['date_of_birth'] = self::formatMrzDate($rawDob, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawExpiry = substr($line2, 21, 6);
|
||||||
|
if (ctype_digit($rawExpiry)) {
|
||||||
|
$data['date_of_expiry'] = self::formatMrzDate($rawExpiry, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function formatMrzDate(string $yymmdd, bool $isBirth): string
|
||||||
|
{
|
||||||
|
$yy = intval(substr($yymmdd, 0, 2));
|
||||||
|
$mm = substr($yymmdd, 2, 2);
|
||||||
|
$dd = substr($yymmdd, 4, 2);
|
||||||
|
|
||||||
|
$currentYear = intval(date('Y'));
|
||||||
|
$currentYearShort = $currentYear % 100;
|
||||||
|
|
||||||
|
if ($isBirth) {
|
||||||
|
$year = ($yy > $currentYearShort) ? (1900 + $yy) : (2000 + $yy);
|
||||||
|
} else {
|
||||||
|
$year = ($yy > ($currentYearShort + 20)) ? (1900 + $yy) : (2000 + $yy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{$dd}/{$mm}/{$year}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function findDateByKeyword(array $lines, array $keywords): ?string
|
||||||
|
{
|
||||||
|
foreach ($lines as $i => $line) {
|
||||||
|
$matched = false;
|
||||||
|
foreach ($keywords as $kw) {
|
||||||
|
if (stripos($line, $kw) !== false) {
|
||||||
|
$matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($matched) {
|
||||||
|
$textToSearch = $line;
|
||||||
|
if (isset($lines[$i + 1])) {
|
||||||
|
$textToSearch .= ' ' . $lines[$i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{4})/', $textToSearch, $matches)) {
|
||||||
|
$day = str_pad($matches[1], 2, '0', STR_PAD_LEFT);
|
||||||
|
$month = str_pad($matches[2], 2, '0', STR_PAD_LEFT);
|
||||||
|
$year = $matches[3];
|
||||||
|
return "{$day}/{$month}/{$year}";
|
||||||
|
}
|
||||||
|
if (preg_match('/(\d{4})[\/\-\.](\d{1,2})[\/\-\.](\d{1,2})/', $textToSearch, $matches)) {
|
||||||
|
$year = $matches[1];
|
||||||
|
$month = str_pad($matches[2], 2, '0', STR_PAD_LEFT);
|
||||||
|
$day = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
|
||||||
|
return "{$day}/{$month}/{$year}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,4 +43,8 @@
|
|||||||
'sponsor_license_url' => env('SPONSOR_LICENSE_API_URL'),
|
'sponsor_license_url' => env('SPONSOR_LICENSE_API_URL'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'google' => [
|
||||||
|
'vision_api_key' => env('GOOGLE_VISION_API_KEY'),
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('sponsors', function (Blueprint $table) {
|
||||||
|
if (!Schema::hasColumn('sponsors', 'emirates_id')) {
|
||||||
|
$table->string('emirates_id')->nullable()->after('emirates_id_file');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('sponsors', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('emirates_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('employer_profiles', function (Blueprint $table) {
|
||||||
|
$table->string('emirates_id_name')->nullable();
|
||||||
|
$table->string('emirates_id_dob')->nullable();
|
||||||
|
$table->string('emirates_id_issue_date')->nullable();
|
||||||
|
$table->string('emirates_id_employer')->nullable();
|
||||||
|
$table->string('emirates_id_issue_place')->nullable();
|
||||||
|
$table->string('emirates_id_occupation')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('employer_profiles', function (Blueprint $table) {
|
||||||
|
$table->dropColumn([
|
||||||
|
'emirates_id_name',
|
||||||
|
'emirates_id_dob',
|
||||||
|
'emirates_id_issue_date',
|
||||||
|
'emirates_id_employer',
|
||||||
|
'emirates_id_issue_place',
|
||||||
|
'emirates_id_occupation',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('sponsors', function (Blueprint $table) {
|
||||||
|
$table->string('emirates_id_name')->nullable();
|
||||||
|
$table->string('emirates_id_dob')->nullable();
|
||||||
|
$table->string('emirates_id_issue_date')->nullable();
|
||||||
|
$table->string('emirates_id_expiry')->nullable();
|
||||||
|
$table->string('emirates_id_employer')->nullable();
|
||||||
|
$table->string('emirates_id_issue_place')->nullable();
|
||||||
|
$table->string('emirates_id_occupation')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('sponsors', function (Blueprint $table) {
|
||||||
|
$table->dropColumn([
|
||||||
|
'emirates_id_name',
|
||||||
|
'emirates_id_dob',
|
||||||
|
'emirates_id_issue_date',
|
||||||
|
'emirates_id_expiry',
|
||||||
|
'emirates_id_employer',
|
||||||
|
'emirates_id_issue_place',
|
||||||
|
'emirates_id_occupation',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('employer_profiles', function (Blueprint $table) {
|
||||||
|
$table->string('address')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('employer_profiles', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('address');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"requestBody": {
|
"requestBody": {
|
||||||
"required": true,
|
"required": true,
|
||||||
"content": {
|
"content": {
|
||||||
"multipart/form-data": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"mobile": {
|
"mobile": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "+971501112233",
|
"example": "+971501112233",
|
||||||
"description": "Mobile phone number \u2014 must be unique. (REQUIRED)"
|
"description": "Mobile phone number — must be unique. (REQUIRED)"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -59,15 +59,65 @@
|
|||||||
"example": "securepass123",
|
"example": "securepass123",
|
||||||
"description": "Login password (min 6 characters). (REQUIRED)"
|
"description": "Login password (min 6 characters). (REQUIRED)"
|
||||||
},
|
},
|
||||||
"license_file": {
|
"license": {
|
||||||
"type": "string",
|
"type": "object",
|
||||||
"format": "binary",
|
"description": "Optional JSON object containing extracted organization/trade license details.",
|
||||||
"description": "Organization or trade license document (jpg/png/pdf, max 10MB). (REQUIRED)"
|
"properties": {
|
||||||
|
"license_number": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "123456"
|
||||||
|
},
|
||||||
|
"organization_name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Al-Rashidi Charitable Foundation"
|
||||||
|
},
|
||||||
|
"expiry_date": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "2028-06-12"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"emirates_id_file": {
|
"emirates_id": {
|
||||||
"type": "string",
|
"type": "object",
|
||||||
"format": "binary",
|
"description": "Optional JSON object containing extracted Emirates ID details.",
|
||||||
"description": "Emirates ID document file (jpg/png/pdf, max 10MB). (REQUIRED)"
|
"properties": {
|
||||||
|
"emirates_id_number": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "784-1988-5310327-2"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "KRISHNA PRASAD"
|
||||||
|
},
|
||||||
|
"nationality": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "NPL"
|
||||||
|
},
|
||||||
|
"date_of_birth": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "1988-03-22"
|
||||||
|
},
|
||||||
|
"expiry_date": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "2028-04-11"
|
||||||
|
},
|
||||||
|
"issue_date": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "2023-04-11"
|
||||||
|
},
|
||||||
|
"employer": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Federal Authority"
|
||||||
|
},
|
||||||
|
"issue_place": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Abu Dhabi"
|
||||||
|
},
|
||||||
|
"occupation": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Manager"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"organization_name": {
|
"organization_name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -155,194 +205,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/sponsors/register/emirates-front": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Sponsor/Auth"
|
|
||||||
],
|
|
||||||
"summary": "Upload Sponsor Emirates ID Front",
|
|
||||||
"description": "Uploads and processes the front side of a Sponsor's Emirates ID using OCR.",
|
|
||||||
"security": [],
|
|
||||||
"requestBody": {
|
|
||||||
"required": true,
|
|
||||||
"content": {
|
|
||||||
"multipart/form-data": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"email",
|
|
||||||
"emirates_id_front"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "email",
|
|
||||||
"example": "info@alrashidi.ae",
|
|
||||||
"description": "Email address of the registered sponsor. (REQUIRED)"
|
|
||||||
},
|
|
||||||
"emirates_id_front": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "binary",
|
|
||||||
"description": "Emirates ID front image/PDF. (REQUIRED)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Emirates ID front processed successfully.",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "Emirates ID front uploaded and processed successfully."
|
|
||||||
},
|
|
||||||
"ocr_extracted_data": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true
|
|
||||||
},
|
|
||||||
"emirates_id_number": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "784-1988-5310327-2"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "KRISHNA PRASAD"
|
|
||||||
},
|
|
||||||
"nationality": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "NPL"
|
|
||||||
},
|
|
||||||
"date_of_birth": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "1988-03-22"
|
|
||||||
},
|
|
||||||
"ocr_accuracy": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 98.5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "info@alrashidi.ae"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Sponsor account not found."
|
|
||||||
},
|
|
||||||
"422": {
|
|
||||||
"description": "Validation error."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/sponsors/register/emirates-back": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Sponsor/Auth"
|
|
||||||
],
|
|
||||||
"summary": "Upload Sponsor Emirates ID Back",
|
|
||||||
"description": "Uploads and processes the back side of a Sponsor's Emirates ID using OCR.",
|
|
||||||
"security": [],
|
|
||||||
"requestBody": {
|
|
||||||
"required": true,
|
|
||||||
"content": {
|
|
||||||
"multipart/form-data": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"email",
|
|
||||||
"emirates_id_back"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "email",
|
|
||||||
"example": "info@alrashidi.ae",
|
|
||||||
"description": "Email address of the registered sponsor. (REQUIRED)"
|
|
||||||
},
|
|
||||||
"emirates_id_back": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "binary",
|
|
||||||
"description": "Emirates ID back image/PDF. (REQUIRED)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Emirates ID back processed successfully.",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "Emirates ID back uploaded and processed successfully."
|
|
||||||
},
|
|
||||||
"ocr_extracted_data": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true
|
|
||||||
},
|
|
||||||
"expiry_date": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2028-04-11"
|
|
||||||
},
|
|
||||||
"issue_date": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2023-04-11"
|
|
||||||
},
|
|
||||||
"ocr_accuracy": {
|
|
||||||
"type": "number",
|
|
||||||
"example": 98.5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "info@alrashidi.ae"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Sponsor account not found."
|
|
||||||
},
|
|
||||||
"422": {
|
|
||||||
"description": "Validation error."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/sponsors/register/license": {
|
"/sponsors/register/license": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -1140,6 +1002,97 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/workers/ocr/passport-vision": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Worker/Auth"
|
||||||
|
],
|
||||||
|
"summary": "Extract Passport Data using Google Cloud Vision API",
|
||||||
|
"description": "Upload a passport image file to extract OCR data using the Google Cloud Vision API.",
|
||||||
|
"security": [],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"passport_file"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"passport_file": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary",
|
||||||
|
"description": "Passport image file (jpg/png/jpeg, max 10MB)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Passport data successfully extracted.",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"success": {
|
||||||
|
"type": "boolean",
|
||||||
|
"example": true
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"passport_number": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Z43R34255"
|
||||||
|
},
|
||||||
|
"given_names": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "AHMAD"
|
||||||
|
},
|
||||||
|
"date_of_birth": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "03/07/1978"
|
||||||
|
},
|
||||||
|
"nationality": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "ARE"
|
||||||
|
},
|
||||||
|
"issuing_country": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "ARE"
|
||||||
|
},
|
||||||
|
"place_of_birth": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "DUBAI"
|
||||||
|
},
|
||||||
|
"date_of_issue": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "10/02/2015"
|
||||||
|
},
|
||||||
|
"date_of_expiry": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "10/02/2020"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation error (missing passport_file)."
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "OCR processing error or failed Google Vision API request."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/workers/config": {
|
"/workers/config": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -2857,10 +2810,57 @@
|
|||||||
"example": "+971509990001",
|
"example": "+971509990001",
|
||||||
"description": "Sponsor's mobile phone number"
|
"description": "Sponsor's mobile phone number"
|
||||||
},
|
},
|
||||||
|
"emirates_id": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Optional JSON object containing extracted Emirates ID details.",
|
||||||
|
"properties": {
|
||||||
|
"emirates_id_number": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "784-1988-5310327-2"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Ahmad Bin Ahmed"
|
||||||
|
},
|
||||||
|
"date_of_birth": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "1990-01-01"
|
||||||
|
},
|
||||||
|
"nationality": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Emirati"
|
||||||
|
},
|
||||||
|
"issue_date": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "2023-04-11"
|
||||||
|
},
|
||||||
|
"expiry_date": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "2028-04-11"
|
||||||
|
},
|
||||||
|
"employer": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Federal Authority"
|
||||||
|
},
|
||||||
|
"issue_place": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Abu Dhabi"
|
||||||
|
},
|
||||||
|
"occupation": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Manager"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fcm_token": {
|
"fcm_token": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "fcm_token_example",
|
"example": "fcm_token_example",
|
||||||
"description": "Firebase Cloud Messaging token for push notifications."
|
"description": "Firebase Cloud Messaging token for push notifications."
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Villa 45, Street 12",
|
||||||
|
"description": "Physical address/location details."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2919,148 +2919,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/employers/register/emirates-front": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Employer/Auth"
|
|
||||||
],
|
|
||||||
"summary": "Upload Emirates ID Front Side Scan & OCR Extract",
|
|
||||||
"description": "Accepts an email and an Emirates ID front scan file, performs automatic OCR extraction of the Emirates ID number, updates the employer's profile fields, and returns the extracted front data.",
|
|
||||||
"security": [],
|
|
||||||
"requestBody": {
|
|
||||||
"required": true,
|
|
||||||
"content": {
|
|
||||||
"multipart/form-data": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"email",
|
|
||||||
"emirates_id_front"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "ahmad@example.com",
|
|
||||||
"description": "Employer's email address."
|
|
||||||
},
|
|
||||||
"emirates_id_front": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "binary",
|
|
||||||
"description": "Physical scan or image file of the Emirates ID front side (Max 10MB)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Emirates ID front uploaded and processed successfully.",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "Emirates ID front uploaded and processed successfully."
|
|
||||||
},
|
|
||||||
"ocr_extracted_data": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"emirates_id_number": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "784-1988-5310327-2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "ahmad@example.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/employers/register/emirates-back": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Employer/Auth"
|
|
||||||
],
|
|
||||||
"summary": "Upload Emirates ID Back Side Scan & OCR Extract",
|
|
||||||
"description": "Accepts an email and an Emirates ID back scan file, performs automatic OCR extraction of the card expiry date, updates the employer's profile fields, and returns the extracted back data.",
|
|
||||||
"security": [],
|
|
||||||
"requestBody": {
|
|
||||||
"required": true,
|
|
||||||
"content": {
|
|
||||||
"multipart/form-data": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"email",
|
|
||||||
"emirates_id_back"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "ahmad@example.com",
|
|
||||||
"description": "Employer's email address."
|
|
||||||
},
|
|
||||||
"emirates_id_back": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "binary",
|
|
||||||
"description": "Physical scan or image file of the Emirates ID back side (Max 10MB)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Emirates ID back uploaded and processed successfully.",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"success": {
|
|
||||||
"type": "boolean",
|
|
||||||
"example": true
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "Emirates ID back uploaded and processed successfully."
|
|
||||||
},
|
|
||||||
"ocr_extracted_data": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"expiry_date": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "2028-04-11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "ahmad@example.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/employers/payment": {
|
"/employers/payment": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
use App\Http\Controllers\Api\EmployerReviewController;
|
use App\Http\Controllers\Api\EmployerReviewController;
|
||||||
use App\Http\Controllers\Api\SponsorAuthController;
|
use App\Http\Controllers\Api\SponsorAuthController;
|
||||||
use App\Http\Controllers\Api\SponsorController;
|
use App\Http\Controllers\Api\SponsorController;
|
||||||
|
use App\Http\Controllers\Api\GoogleVisionOcrController;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -42,12 +43,11 @@
|
|||||||
Route::post('/workers/setup-profile', [WorkerAuthController::class, 'setupProfile']);
|
Route::post('/workers/setup-profile', [WorkerAuthController::class, 'setupProfile']);
|
||||||
Route::post('/workers/register', [WorkerAuthController::class, 'register']);
|
Route::post('/workers/register', [WorkerAuthController::class, 'register']);
|
||||||
Route::post('/workers/login', [WorkerAuthController::class, 'login']);
|
Route::post('/workers/login', [WorkerAuthController::class, 'login']);
|
||||||
|
Route::post('/workers/ocr/passport-vision', [GoogleVisionOcrController::class, 'extractPassport']);
|
||||||
|
|
||||||
// Unprotected Employer Mobile Auth Endpoints
|
// Unprotected Employer Mobile Auth Endpoints
|
||||||
Route::post('/employers/register', [EmployerAuthController::class, 'register']);
|
Route::post('/employers/register', [EmployerAuthController::class, 'register']);
|
||||||
Route::post('/employers/verify', [EmployerAuthController::class, 'verify']);
|
Route::post('/employers/verify', [EmployerAuthController::class, 'verify']);
|
||||||
Route::post('/employers/register/emirates-front', [EmployerAuthController::class, 'uploadEmiratesIdFront']);
|
|
||||||
Route::post('/employers/register/emirates-back', [EmployerAuthController::class, 'uploadEmiratesIdBack']);
|
|
||||||
Route::post('/employers/payment', [EmployerAuthController::class, 'payment']);
|
Route::post('/employers/payment', [EmployerAuthController::class, 'payment']);
|
||||||
Route::post('/employers/password', [EmployerAuthController::class, 'password']);
|
Route::post('/employers/password', [EmployerAuthController::class, 'password']);
|
||||||
Route::get('/employers/plans', [EmployerAuthController::class, 'plans']);
|
Route::get('/employers/plans', [EmployerAuthController::class, 'plans']);
|
||||||
@ -57,8 +57,6 @@
|
|||||||
|
|
||||||
// Sponsor Mobile Auth Endpoints (unprotected)
|
// Sponsor Mobile Auth Endpoints (unprotected)
|
||||||
Route::post('/sponsors/register', [SponsorAuthController::class, 'register']);
|
Route::post('/sponsors/register', [SponsorAuthController::class, 'register']);
|
||||||
Route::post('/sponsors/register/emirates-front', [SponsorAuthController::class, 'uploadEmiratesIdFront']);
|
|
||||||
Route::post('/sponsors/register/emirates-back', [SponsorAuthController::class, 'uploadEmiratesIdBack']);
|
|
||||||
Route::post('/sponsors/register/license', [SponsorAuthController::class, 'uploadLicense']);
|
Route::post('/sponsors/register/license', [SponsorAuthController::class, 'uploadLicense']);
|
||||||
Route::post('/sponsors/login', [EmployerAuthController::class, 'login']);
|
Route::post('/sponsors/login', [EmployerAuthController::class, 'login']);
|
||||||
|
|
||||||
|
|||||||
@ -89,9 +89,6 @@ public function test_employer_can_login()
|
|||||||
*/
|
*/
|
||||||
public function test_employer_can_register()
|
public function test_employer_can_register()
|
||||||
{
|
{
|
||||||
\Illuminate\Support\Facades\Storage::fake('local');
|
|
||||||
$fakeIdFile = \Illuminate\Http\UploadedFile::fake()->create('emirates_id.jpg', 800, 'image/jpeg');
|
|
||||||
|
|
||||||
// Step 1: Register
|
// Step 1: Register
|
||||||
$response = $this->postJson('/api/employers/register', [
|
$response = $this->postJson('/api/employers/register', [
|
||||||
'company_name' => 'New Company Corp',
|
'company_name' => 'New Company Corp',
|
||||||
@ -100,6 +97,18 @@ public function test_employer_can_register()
|
|||||||
'phone' => '+971500000003',
|
'phone' => '+971500000003',
|
||||||
'password' => 'Password@123',
|
'password' => 'Password@123',
|
||||||
'password_confirmation' => 'Password@123',
|
'password_confirmation' => 'Password@123',
|
||||||
|
'address' => 'Test Address 123',
|
||||||
|
'emirates_id' => [
|
||||||
|
'emirates_id_number' => '784-1988-5310327-2',
|
||||||
|
'expiry_date' => '2028-04-11',
|
||||||
|
'name' => 'Alice Employer',
|
||||||
|
'date_of_birth' => '1990-01-01',
|
||||||
|
'nationality' => 'Emirati',
|
||||||
|
'issue_date' => '2023-04-11',
|
||||||
|
'employer' => 'Federal Authority',
|
||||||
|
'issue_place' => 'Abu Dhabi',
|
||||||
|
'occuption' => 'Manager'
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201)
|
$response->assertStatus(201)
|
||||||
@ -116,105 +125,23 @@ public function test_employer_can_register()
|
|||||||
|
|
||||||
$this->assertDatabaseHas('employer_profiles', [
|
$this->assertDatabaseHas('employer_profiles', [
|
||||||
'company_name' => 'Alice Employer Household',
|
'company_name' => 'Alice Employer Household',
|
||||||
|
'emirates_id_number' => '784-1988-5310327-2',
|
||||||
|
'emirates_id_expiry' => '2028-04-11',
|
||||||
|
'emirates_id_name' => 'Alice Employer',
|
||||||
|
'emirates_id_dob' => '1990-01-01',
|
||||||
|
'nationality' => 'Emirati',
|
||||||
|
'emirates_id_issue_date' => '2023-04-11',
|
||||||
|
'emirates_id_employer' => 'Federal Authority',
|
||||||
|
'emirates_id_issue_place' => 'Abu Dhabi',
|
||||||
|
'emirates_id_occupation' => 'Manager',
|
||||||
|
'address' => 'Test Address 123',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Step 2: Upload Emirates ID Front
|
$this->assertDatabaseHas('sponsors', [
|
||||||
$uploadFrontResponse = $this->postJson('/api/employers/register/emirates-front', [
|
|
||||||
'email' => 'alice@example.com',
|
'email' => 'alice@example.com',
|
||||||
'emirates_id_front' => $fakeIdFile,
|
'emirates_id' => '784-1988-5310327-2',
|
||||||
|
'address' => 'Test Address 123',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$uploadFrontResponse->assertStatus(200)
|
|
||||||
->assertJsonStructure([
|
|
||||||
'success',
|
|
||||||
'message',
|
|
||||||
'ocr_extracted_data' => [
|
|
||||||
'emirates_id_number',
|
|
||||||
],
|
|
||||||
'email'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Step 3: Upload Emirates ID Back
|
|
||||||
$uploadBackResponse = $this->postJson('/api/employers/register/emirates-back', [
|
|
||||||
'email' => 'alice@example.com',
|
|
||||||
'emirates_id_back' => $fakeIdFile,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$uploadBackResponse->assertStatus(200)
|
|
||||||
->assertJsonStructure([
|
|
||||||
'success',
|
|
||||||
'message',
|
|
||||||
'ocr_extracted_data' => [
|
|
||||||
'expiry_date',
|
|
||||||
],
|
|
||||||
'email'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$user = User::where('email', 'alice@example.com')->first();
|
|
||||||
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
|
||||||
|
|
||||||
$this->assertNull($profile->emirates_id_front);
|
|
||||||
$this->assertNotNull($profile->emirates_id_number);
|
|
||||||
$this->assertNotNull($profile->emirates_id_expiry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test employer can upload front and back side Emirates ID separately via distinct API endpoints.
|
|
||||||
*/
|
|
||||||
public function test_employer_can_upload_separate_emirates_id_front_and_back()
|
|
||||||
{
|
|
||||||
\Illuminate\Support\Facades\Storage::fake('local');
|
|
||||||
$frontFile = \Illuminate\Http\UploadedFile::fake()->create('front.jpg', 500, 'image/jpeg');
|
|
||||||
$backFile = \Illuminate\Http\UploadedFile::fake()->create('back.jpg', 500, 'image/jpeg');
|
|
||||||
|
|
||||||
// Create an employer user to upload for
|
|
||||||
$user = User::create([
|
|
||||||
'name' => 'Charlie Employer',
|
|
||||||
'email' => 'charlie@example.com',
|
|
||||||
'password' => bcrypt('password'),
|
|
||||||
'role' => 'employer',
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Upload Front
|
|
||||||
$responseFront = $this->postJson('/api/employers/register/emirates-front', [
|
|
||||||
'email' => 'charlie@example.com',
|
|
||||||
'emirates_id_front' => $frontFile,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$responseFront->assertStatus(200)
|
|
||||||
->assertJsonStructure([
|
|
||||||
'success',
|
|
||||||
'message',
|
|
||||||
'ocr_extracted_data' => [
|
|
||||||
'emirates_id_number',
|
|
||||||
],
|
|
||||||
'email'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals('784-1988-5310327-2', $responseFront->json('ocr_extracted_data.emirates_id_number'));
|
|
||||||
|
|
||||||
// Upload Back
|
|
||||||
$responseBack = $this->postJson('/api/employers/register/emirates-back', [
|
|
||||||
'email' => 'charlie@example.com',
|
|
||||||
'emirates_id_back' => $backFile,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$responseBack->assertStatus(200)
|
|
||||||
->assertJsonStructure([
|
|
||||||
'success',
|
|
||||||
'message',
|
|
||||||
'ocr_extracted_data' => [
|
|
||||||
'expiry_date',
|
|
||||||
],
|
|
||||||
'email'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertEquals('2028-04-11', $responseBack->json('ocr_extracted_data.expiry_date'));
|
|
||||||
|
|
||||||
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
|
||||||
$this->assertNotNull($profile);
|
|
||||||
$this->assertEquals('784-1988-5310327-2', $profile->emirates_id_number);
|
|
||||||
$this->assertEquals('2028-04-11', $profile->emirates_id_expiry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -51,7 +51,11 @@ public function test_employer_registration_and_login_stores_fcm_token_and_sends_
|
|||||||
'name' => 'Employer FCM Test',
|
'name' => 'Employer FCM Test',
|
||||||
'email' => 'employer.fcm@example.com',
|
'email' => 'employer.fcm@example.com',
|
||||||
'phone' => '+971500000001',
|
'phone' => '+971500000001',
|
||||||
'fcm_token' => 'employer_register_fcm_token'
|
'fcm_token' => 'employer_register_fcm_token',
|
||||||
|
'emirates_id' => [
|
||||||
|
'emirates_id_number' => '784-1988-5310327-2',
|
||||||
|
'expiry_date' => '2028-04-11',
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
@ -59,6 +63,10 @@ public function test_employer_registration_and_login_stores_fcm_token_and_sends_
|
|||||||
$user = User::where('email', 'employer.fcm@example.com')->first();
|
$user = User::where('email', 'employer.fcm@example.com')->first();
|
||||||
$this->assertEquals('employer_register_fcm_token', $user->fcm_token);
|
$this->assertEquals('employer_register_fcm_token', $user->fcm_token);
|
||||||
|
|
||||||
|
$profile = EmployerProfile::where('user_id', $user->id)->first();
|
||||||
|
$this->assertEquals('784-1988-5310327-2', $profile->emirates_id_number);
|
||||||
|
$this->assertEquals('2028-04-11', $profile->emirates_id_expiry);
|
||||||
|
|
||||||
$sponsor = Sponsor::where('email', 'employer.fcm@example.com')->first();
|
$sponsor = Sponsor::where('email', 'employer.fcm@example.com')->first();
|
||||||
$this->assertEquals('employer_register_fcm_token', $sponsor->fcm_token);
|
$this->assertEquals('employer_register_fcm_token', $sponsor->fcm_token);
|
||||||
|
|
||||||
@ -163,15 +171,18 @@ public function test_worker_registration_setup_profile_and_login_stores_fcm_toke
|
|||||||
|
|
||||||
public function test_sponsor_registration_and_login_stores_fcm_token_and_sends_notification()
|
public function test_sponsor_registration_and_login_stores_fcm_token_and_sends_notification()
|
||||||
{
|
{
|
||||||
$licenseFile = UploadedFile::fake()->create('license.pdf', 500, 'application/pdf');
|
|
||||||
$emiratesIdFile = UploadedFile::fake()->create('emirates_id.pdf', 500, 'application/pdf');
|
|
||||||
|
|
||||||
$response = $this->postJson('/api/sponsors/register', [
|
$response = $this->postJson('/api/sponsors/register', [
|
||||||
'full_name' => 'Sponsor FCM Org',
|
'full_name' => 'Sponsor FCM Org',
|
||||||
'mobile' => '+971500000004',
|
'mobile' => '+971500000004',
|
||||||
'password' => 'sponsorpassword123',
|
'password' => 'sponsorpassword123',
|
||||||
'license_file' => $licenseFile,
|
'license' => [
|
||||||
'emirates_id_file' => $emiratesIdFile,
|
'license_number' => '123456',
|
||||||
|
'organization_name' => 'Sponsor FCM Co',
|
||||||
|
'expiry_date' => '2027-01-01',
|
||||||
|
],
|
||||||
|
'emirates_id' => [
|
||||||
|
'emirates_id_number' => '784-1988-5310327-2',
|
||||||
|
],
|
||||||
'organization_name' => 'Sponsor FCM Co',
|
'organization_name' => 'Sponsor FCM Co',
|
||||||
'email' => 'sponsor.fcm@example.com',
|
'email' => 'sponsor.fcm@example.com',
|
||||||
'nationality' => 'Emirati',
|
'nationality' => 'Emirati',
|
||||||
|
|||||||
105
tests/Feature/GoogleVisionOcrApiTest.php
Normal file
105
tests/Feature/GoogleVisionOcrApiTest.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class GoogleVisionOcrApiTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Test validation requires passport_file.
|
||||||
|
*/
|
||||||
|
public function test_extract_passport_validation_fails_without_file()
|
||||||
|
{
|
||||||
|
$response = $this->postJson('/api/workers/ocr/passport-vision');
|
||||||
|
|
||||||
|
$response->assertStatus(422)
|
||||||
|
->assertJson([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Validation error.',
|
||||||
|
])
|
||||||
|
->assertJsonValidationErrors(['passport_file']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test successful OCR extraction using mocked Google Vision API.
|
||||||
|
*/
|
||||||
|
public function test_extract_passport_success_with_mrz()
|
||||||
|
{
|
||||||
|
// Mock Google Vision API Response
|
||||||
|
Http::fake([
|
||||||
|
'https://vision.googleapis.com/*' => Http::response([
|
||||||
|
'responses' => [
|
||||||
|
[
|
||||||
|
'fullTextAnnotation' => [
|
||||||
|
'text' => "PASSPORT\nType: P Country: ARE\nPassport No: Z43R34255\nSurname: AL-HASHIMI\nGiven Names: AHMAD\nNationality: ARE\nDate of Birth: 03/07/1978\nSex: M\nPlace of Birth: DUBAI\nDate of Issue: 10/02/2015\nDate of Expiry: 10/02/2020\nMRZ:\nP<AREAL<HASHIMI<<AHMAD<<<<<<<<<<<<<<<<<<<<<<\nZ43R342558ARE7807033M2002100<<<<<<<<<<<<<<02"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], 200)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$file = UploadedFile::fake()->image('passport.jpg');
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/workers/ocr/passport-vision', [
|
||||||
|
'passport_file' => $file
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(200)
|
||||||
|
->assertJson([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'date_of_birth' => '03/07/1978',
|
||||||
|
'date_of_expiry' => '10/02/2020',
|
||||||
|
'date_of_issue' => '10/02/2015',
|
||||||
|
'given_names' => 'AHMAD',
|
||||||
|
'issuing_country' => 'ARE',
|
||||||
|
'nationality' => 'ARE',
|
||||||
|
'passport_number' => 'Z43R34255',
|
||||||
|
'place_of_birth' => 'DUBAI',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test OCR extraction fallback via regex when MRZ is absent.
|
||||||
|
*/
|
||||||
|
public function test_extract_passport_success_via_regex_fallback()
|
||||||
|
{
|
||||||
|
// Mock Google Vision API Response without MRZ lines
|
||||||
|
Http::fake([
|
||||||
|
'https://vision.googleapis.com/*' => Http::response([
|
||||||
|
'responses' => [
|
||||||
|
[
|
||||||
|
'fullTextAnnotation' => [
|
||||||
|
'text' => "PASSPORT\nPassport Number: Z43R34255\nGiven Names: AHMAD\nNationality: ARE\nIssuing Country: ARE\nDate of Birth: 03/07/1978\nDate of Issue: 10/02/2015\nDate of Expiry: 10/02/2020\nPlace of Birth: DUBAI"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], 200)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$file = UploadedFile::fake()->image('passport.jpg');
|
||||||
|
|
||||||
|
$response = $this->postJson('/api/workers/ocr/passport-vision', [
|
||||||
|
'passport_file' => $file
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(200)
|
||||||
|
->assertJson([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'date_of_birth' => '03/07/1978',
|
||||||
|
'date_of_expiry' => '10/02/2020',
|
||||||
|
'date_of_issue' => '10/02/2015',
|
||||||
|
'given_names' => 'AHMAD',
|
||||||
|
'issuing_country' => 'ARE',
|
||||||
|
'nationality' => 'ARE',
|
||||||
|
'passport_number' => 'Z43R34255',
|
||||||
|
'place_of_birth' => 'DUBAI',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,15 +19,26 @@ class SponsorAuthApiTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function test_sponsor_can_register_with_license()
|
public function test_sponsor_can_register_with_license()
|
||||||
{
|
{
|
||||||
$licenseFile = UploadedFile::fake()->create('license.pdf', 500, 'application/pdf');
|
|
||||||
$emiratesIdFile = UploadedFile::fake()->create('emirates_id.pdf', 500, 'application/pdf');
|
|
||||||
|
|
||||||
$response = $this->postJson('/api/sponsors/register', [
|
$response = $this->postJson('/api/sponsors/register', [
|
||||||
'full_name' => 'Test Sponsor Organization',
|
'full_name' => 'Test Sponsor Organization',
|
||||||
'mobile' => '+971509990001',
|
'mobile' => '+971509990001',
|
||||||
'password' => 'securepassword',
|
'password' => 'securepassword',
|
||||||
'license_file' => $licenseFile,
|
'license' => [
|
||||||
'emirates_id_file' => $emiratesIdFile,
|
'license_number' => '123456',
|
||||||
|
'organization_name' => 'Charity Co',
|
||||||
|
'expiry_date' => '2028-06-12',
|
||||||
|
],
|
||||||
|
'emirates_id' => [
|
||||||
|
'emirates_id_number' => '784-1988-5310327-2',
|
||||||
|
'name' => 'Ahmad Bin Ahmed',
|
||||||
|
'date_of_birth' => '1990-01-01',
|
||||||
|
'nationality' => 'Emirati',
|
||||||
|
'issue_date' => '2023-04-11',
|
||||||
|
'expiry_date' => '2028-04-11',
|
||||||
|
'employer' => 'Federal Authority',
|
||||||
|
'issue_place' => 'Abu Dhabi',
|
||||||
|
'occupation' => 'Manager'
|
||||||
|
],
|
||||||
'organization_name' => 'Charity Co',
|
'organization_name' => 'Charity Co',
|
||||||
'email' => 'test.sponsor@example.com',
|
'email' => 'test.sponsor@example.com',
|
||||||
'nationality' => 'Emirati',
|
'nationality' => 'Emirati',
|
||||||
@ -51,6 +62,7 @@ public function test_sponsor_can_register_with_license()
|
|||||||
'city',
|
'city',
|
||||||
'license_file',
|
'license_file',
|
||||||
'emirates_id_file',
|
'emirates_id_file',
|
||||||
|
'emirates_id',
|
||||||
'license_expiry',
|
'license_expiry',
|
||||||
],
|
],
|
||||||
'token',
|
'token',
|
||||||
@ -61,9 +73,17 @@ public function test_sponsor_can_register_with_license()
|
|||||||
'mobile' => '+971509990001',
|
'mobile' => '+971509990001',
|
||||||
'full_name' => 'Test Sponsor Organization',
|
'full_name' => 'Test Sponsor Organization',
|
||||||
'license_expiry' => '2028-06-12 00:00:00',
|
'license_expiry' => '2028-06-12 00:00:00',
|
||||||
|
'emirates_id_name' => 'Ahmad Bin Ahmed',
|
||||||
|
'emirates_id_dob' => '1990-01-01',
|
||||||
|
'emirates_id_issue_date' => '2023-04-11',
|
||||||
|
'emirates_id_expiry' => '2028-04-11',
|
||||||
|
'emirates_id_employer' => 'Federal Authority',
|
||||||
|
'emirates_id_issue_place' => 'Abu Dhabi',
|
||||||
|
'emirates_id_occupation' => 'Manager',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$sponsor = Sponsor::where('mobile', '+971509990001')->first();
|
$sponsor = Sponsor::where('mobile', '+971509990001')->first();
|
||||||
|
$this->assertEquals('784-1988-5310327-2', $sponsor->emirates_id);
|
||||||
$this->assertNull($sponsor->emirates_id_file);
|
$this->assertNull($sponsor->emirates_id_file);
|
||||||
$this->assertNull($sponsor->license_file);
|
$this->assertNull($sponsor->license_file);
|
||||||
}
|
}
|
||||||
@ -500,11 +520,11 @@ public function test_sponsor_can_list_own_pending_charity_event()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test uploading Emirates ID front/back and license files separately.
|
* Test registering Sponsor with explicit emirates_id, and uploading license separately.
|
||||||
*/
|
*/
|
||||||
public function test_sponsor_can_upload_separate_documents()
|
public function test_sponsor_registration_with_emirates_id_and_separate_license()
|
||||||
{
|
{
|
||||||
// 1. Register Sponsor without documents
|
// 1. Register Sponsor with emirates_id in payload
|
||||||
$response = $this->postJson('/api/sponsors/register', [
|
$response = $this->postJson('/api/sponsors/register', [
|
||||||
'full_name' => 'Test Sponsor Sep',
|
'full_name' => 'Test Sponsor Sep',
|
||||||
'mobile' => '+971509990901',
|
'mobile' => '+971509990901',
|
||||||
@ -515,55 +535,19 @@ public function test_sponsor_can_upload_separate_documents()
|
|||||||
'city' => 'Abu Dhabi',
|
'city' => 'Abu Dhabi',
|
||||||
'address' => 'Villa 45, Street 12',
|
'address' => 'Villa 45, Street 12',
|
||||||
'country_code' => '+971',
|
'country_code' => '+971',
|
||||||
|
'emirates_id' => [
|
||||||
|
'emirates_id_number' => '784-1988-5310327-2',
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
|
|
||||||
$sponsor = Sponsor::where('email', 'test.sponsor.sep@example.com')->first();
|
$sponsor = Sponsor::where('email', 'test.sponsor.sep@example.com')->first();
|
||||||
$this->assertNotNull($sponsor);
|
$this->assertNotNull($sponsor);
|
||||||
|
$this->assertEquals('784-1988-5310327-2', $sponsor->emirates_id);
|
||||||
$this->assertNull($sponsor->license_expiry);
|
$this->assertNull($sponsor->license_expiry);
|
||||||
|
|
||||||
// 2. Upload Emirates ID Front
|
// 2. Upload License File
|
||||||
$frontFile = UploadedFile::fake()->create('emirates_id_front.jpg', 500, 'image/jpeg');
|
|
||||||
$responseFront = $this->postJson('/api/sponsors/register/emirates-front', [
|
|
||||||
'email' => 'test.sponsor.sep@example.com',
|
|
||||||
'emirates_id_front' => $frontFile,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$responseFront->assertStatus(200)
|
|
||||||
->assertJson([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Emirates ID front uploaded and processed successfully.',
|
|
||||||
])
|
|
||||||
->assertJsonStructure([
|
|
||||||
'ocr_extracted_data' => [
|
|
||||||
'emirates_id_number',
|
|
||||||
'name',
|
|
||||||
'nationality',
|
|
||||||
'date_of_birth',
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 3. Upload Emirates ID Back
|
|
||||||
$backFile = UploadedFile::fake()->create('emirates_id_back.jpg', 500, 'image/jpeg');
|
|
||||||
$responseBack = $this->postJson('/api/sponsors/register/emirates-back', [
|
|
||||||
'email' => 'test.sponsor.sep@example.com',
|
|
||||||
'emirates_id_back' => $backFile,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$responseBack->assertStatus(200)
|
|
||||||
->assertJson([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Emirates ID back uploaded and processed successfully.',
|
|
||||||
])
|
|
||||||
->assertJsonStructure([
|
|
||||||
'ocr_extracted_data' => [
|
|
||||||
'expiry_date',
|
|
||||||
'issue_date',
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 4. Upload License File
|
|
||||||
$licenseFile = UploadedFile::fake()->create('license.pdf', 500, 'application/pdf');
|
$licenseFile = UploadedFile::fake()->create('license.pdf', 500, 'application/pdf');
|
||||||
$responseLicense = $this->postJson('/api/sponsors/register/license', [
|
$responseLicense = $this->postJson('/api/sponsors/register/license', [
|
||||||
'email' => 'test.sponsor.sep@example.com',
|
'email' => 'test.sponsor.sep@example.com',
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default defineConfig({
|
|||||||
port: 5173,
|
port: 5173,
|
||||||
cors: true,
|
cors: true,
|
||||||
hmr: {
|
hmr: {
|
||||||
host: '192.168.0.172',
|
host: '192.168.0.245',
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
ignored: ['**/storage/framework/views/**'],
|
ignored: ['**/storage/framework/views/**'],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user