phone; $restaurantId = $this->getRestaurantIdFromHeader($request); if ($restaurantId instanceof JsonResponse) { return $restaurantId; } // Check if customer already exists $customer = Customer::where('restaurant_id', $restaurantId) ->where('phone', $phone) ->first(); // If customer not exists, create one if (! $customer) { $customer = Customer::create([ 'name' => 'Customer '.substr($phone, -4), 'phone' => $phone, 'password' => Hash::make($phone), // temporary password 'restaurant_id' => $restaurantId, ]); } // Generate OTP $otp = '1234'; // rand(100000, 999999); $expiryTime = now()->addMinutes(5); // Save or update OTP record OtpVerification::updateOrCreate( [ 'restaurant_id' => $restaurantId, 'phone' => $phone, ], [ 'otp' => $otp, 'expires_at' => $expiryTime, 'updated_at' => now(), ] ); // TODO: Send OTP via SMS Gateway (mock for now) // Muthofun::send($phone, "Your OTP is: $otp"); return $this->ResponseSuccess([ 'phone' => $phone, 'otp' => $otp, // ⚠️ remove in production ], 'OTP sent successfully.'); } /** * Step 2: Verify OTP and Login */ public function customerOtpVerify(Request $request) { $restaurantId = $this->getRestaurantIdFromHeader($request); if ($restaurantId instanceof JsonResponse) { return $restaurantId; } $request->validate([ 'phone' => 'required|numeric|exists:customers,phone', 'otp' => 'required', ]); $phone = $request->phone; $otp = $request->otp; // Fetch OTP record $otpRecord = OtpVerification::where('restaurant_id', $restaurantId) ->where('phone', $phone) ->first(); if (! $otpRecord) { return $this->ResponseError([], 'Invalid phone or OTP expired.', 400); } // Check expiry if (now()->greaterThan($otpRecord->expires_at)) { $otpRecord->update(['otp' => null]); return $this->ResponseError([], 'OTP expired. Please request a new one.', 400); } // Check OTP validity if ($otpRecord->otp != $otp) { return $this->ResponseError([], 'Invalid OTP.', 400); } // OTP valid → Find customer $customer = Customer::where('restaurant_id', $restaurantId) ->where('phone', $phone) ->first(); if (! $customer) { return $this->ResponseError([], 'Customer not found.', 404); } // Clear OTP after success $otpRecord->update(['otp' => null]); // Create API token $tokenResult = $customer->createToken('Customer Access Token'); return $this->ResponseSuccess([ 'customer' => $customer, 'access_token' => $tokenResult->accessToken, 'expires_at' => $tokenResult->token->expires_at, ], 'Login successful.'); } public function customerOtpSent(Request $request) { $restaurantId = $this->getRestaurantIdFromHeader($request); if ($restaurantId instanceof JsonResponse) { return $restaurantId; } $request->validate([ 'phone' => 'required|numeric|exists:customers,phone', ]); $phone = $request->phone; // TODO $expiryTime = now()->addMinutes(15); // OTP expiry = 2 minutes $otp = rand(100000, 999999); // Generate random 6-digit OTP // Check if an OTP record already exists for this phone $otpRecord = OtpVerification::where('restaurant_id', $restaurantId) ->where('phone', $phone) ->first(); if ($otpRecord) { // Check if existing OTP is still valid (not expired) if (now()->lessThan($otpRecord->expires_at)) { $remainingSeconds = now()->diffInSeconds($otpRecord->expires_at); return $this->ResponseError([ 'remaining_seconds' => $remainingSeconds, ], "An OTP has already been sent. Please wait {$remainingSeconds} seconds before requesting a new one.", 429); } // OTP expired → generate and resend a new one $otpRecord->update([ 'otp' => $otp, 'expires_at' => $expiryTime, ]); } else { // No record → create a new one OtpVerification::create([ 'phone' => $phone, 'otp' => $otp, 'expires_at' => $expiryTime, ]); } // TODO: Replace with your SMS sending logic // Example: Muthofun::send($phone, "Your OTP is: $otp"); return $this->ResponseSuccess([ 'phone' => $phone, 'otp' => $otp, // ⚠️ Remove in production 'expires_at' => $expiryTime->toDateTimeString(), ], 'OTP sent successfully.'); } public function customerProfile(Request $request) { try { $customer = Customer::where('id', Auth::id())->first(); if ($customer) { $permissions = $customer->role?->permissions->pluck('name')->toArray() ?? []; // Convert permissions to an array $customer = [ 'id' => $customer->id, 'name' => $customer->first_name.' '.$customer->last_name, 'email' => $customer->email, 'phone' => $customer->phone, 'image' => $customer->avatar, 'role' => $customer->role?->name ?? 'no-role-assign', 'status' => $customer->status, 'permissions' => $permissions, ]; return $this->responseSuccess($customer, 'Profile fetched successfully.'); } else { return $this->responseError([], 'Profile not found.'); } } catch (Exception $e) { return $this->responseError([], 'An error occurred while fetching the profile.'); } } public function customerUpdate(ProfileUpdateRequest $request) { $customer = Auth::guard('customer')->user(); // Get validated data $data = $request->validated(); // Avatar upload (first time + update) if ($request->hasFile('avatar')) { $data['avatar'] = fileUploader( 'customers/', 'png', $request->file('avatar'), $customer->avatar // old image (null for first upload) ); } // Fill customer data $customer->fill($data); // Reset email verification if email changed if ($customer->isDirty('email')) { $customer->email_verified_at = null; } $customer->save(); return $this->responseSuccess( $customer->fresh(), 'Profile Update Successfully Done.' ); } public function changePassword(Request $request) { $validated = $request->validateWithBag('updatePassword', [ 'current_password' => ['required', 'current_password'], 'password' => ['required', 'confirmed'], ]); $request->customer()->update([ 'password' => Hash::make($validated['password']), ]); return $this->ResponseSuccess([], 'Password Changes Successfully.'); } public function accountDeleted(Request $request) { $request->validateWithBag('updatePassword', [ 'current_password' => ['required', 'current_password'], ]); $request->customer()->update([ 'deleted_at' => Carbon::now(), ]); return $this->ResponseSuccess([], 'Account Delete successfully.'); } public function customerLogout(Request $request) { try { $result = $request->customer()->token()->revoke(); if ($result) { return $this->ResponseSuccess([], 'Logout Success'); } } catch (Exception $e) { return $this->ResponseSuccess([], 'Logout Failed'); } } private function getRestaurantIdFromHeader(Request $request) { $domain = $request->header('X-Domain'); // Custom header key (Change if needed) if (! $domain) { return $this->responseError([], 'Domain header is missing.', 400); } $restaurantResponse = RestaurantHelper::getRestaurantIdByDomain($domain); if (! $restaurantResponse['status']) { return $this->responseError([], $restaurantResponse['error'], 404); } return $restaurantResponse['restaurant_id']; } }