all(), [ 'full_name' => 'required|string|max:255', 'mobile' => 'required|string|max:50|unique:sponsors,mobile', 'password' => 'required|string|min:6', 'license_file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240', 'emirates_id_file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240', 'organization_name' => 'required|string|max:255', 'email' => 'required|email|max:255|unique:sponsors,email', 'nationality' => 'required|string|max:100', 'city' => 'required|string|max:100', 'address' => 'required|string|max:255', 'country_code' => 'required|string|max:10', 'license_expiry' => 'required|date_format:Y-m-d', ], [ 'mobile.unique' => 'This mobile number is already registered.', 'email.unique' => 'This email address is already registered.', 'license_file.required' => 'Please upload your organization or trade license.', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => $validator->errors() ], 422); } try { // Auto-generate email if not provided $mobileClean = preg_replace('/[^0-9]/', '', $request->mobile); $email = $request->email ?? "sponsor.{$mobileClean}@migrant.ae"; if (!$request->email && Sponsor::where('email', $email)->exists()) { $email = "sponsor.{$mobileClean}." . rand(10, 99) . "@migrant.ae"; } // Store files $destinationPath = public_path('uploads/licenses'); if (!file_exists($destinationPath)) { mkdir($destinationPath, 0755, true); } $licenseFile = $request->file('license_file'); $licenseFileName = time() . '_license_' . preg_replace('/[^a-zA-Z0-9_.-]/', '', $licenseFile->getClientOriginalName()); $licenseFile->move($destinationPath, $licenseFileName); $licensePath = 'uploads/licenses/' . $licenseFileName; $emiratesIdPath = null; if ($request->hasFile('emirates_id_file')) { $emiratesIdFile = $request->file('emirates_id_file'); $emiratesIdFileName = time() . '_emirates_id_' . preg_replace('/[^a-zA-Z0-9_.-]/', '', $emiratesIdFile->getClientOriginalName()); $emiratesIdFile->move($destinationPath, $emiratesIdFileName); $emiratesIdPath = 'uploads/licenses/' . $emiratesIdFileName; } $apiToken = Str::random(80); $licenseExpiry = $request->license_expiry ?? now()->addYears(3)->toDateString(); $sponsor = DB::transaction(function () use ($request, $email, $licensePath, $emiratesIdPath, $apiToken, $licenseExpiry) { return Sponsor::create([ 'full_name' => $request->full_name, 'organization_name' => $request->organization_name, 'email' => $email, 'mobile' => $request->mobile, 'password' => Hash::make($request->password), 'country_code' => $request->country_code, 'nationality' => $request->nationality, 'city' => $request->city, 'address' => $request->address, 'license_file' => $licensePath, 'emirates_id_file' => $emiratesIdPath, 'license_expiry' => $licenseExpiry, 'status' => 'active', 'is_verified' => false, 'subscription_status' => 'none', 'api_token' => $apiToken, ]); }); return response()->json([ 'success' => true, 'message' => 'Sponsor account registered successfully. Your license is pending admin review.', 'data' => [ 'sponsor' => $sponsor->makeHidden(['password', 'api_token']), 'token' => $apiToken, ] ], 201); } catch (\Exception $e) { logger()->error('Sponsor Registration Failure: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'An error occurred during registration. Please try again.', 'error' => app()->environment('local') ? $e->getMessage() : 'Internal Server Error' ], 500); } } /** * Authenticate a Sponsor and return their bearer token. * * POST /api/sponsors/login */ public function login(Request $request) { $validator = Validator::make($request->all(), [ 'mobile' => 'required|string', 'password' => 'required|string', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation error.', 'errors' => $validator->errors() ], 422); } $sponsor = Sponsor::where('mobile', $request->mobile)->first(); if (!$sponsor || !Hash::check($request->password, $sponsor->password)) { return response()->json([ 'success' => false, 'message' => 'Invalid mobile number or password.' ], 401); } if ($sponsor->status === 'suspended') { return response()->json([ 'success' => false, 'message' => 'Your account has been suspended. Please contact support.' ], 403); } // Rotate token on each login for security $apiToken = Str::random(80); $sponsor->update([ 'api_token' => $apiToken, 'last_login_at' => now(), ]); return response()->json([ 'success' => true, 'message' => 'Login successful.', 'data' => [ 'sponsor' => $sponsor->makeHidden(['password', 'api_token']), 'token' => $apiToken, ] ], 200); } }