GOOD SHELL MAS BOY
Server: Apache/2.4.52 (Ubuntu)
System: Linux vmi1836763.contaboserver.net 5.15.0-130-generic #140-Ubuntu SMP Wed Dec 18 17:59:53 UTC 2024 x86_64
User: www-data (33)
PHP: 8.4.10
Disabled: NONE
Upload Files
File: /var/www/console.fixgini.com/app/Http/Controllers/Authentication/RegisterSeller.php
<?php

namespace App\Http\Controllers\Authentication;

use App\Models\User;
use App\Models\Wallet;
use App\Models\Country;
use App\Mail\WelcomeMail;
use App\Models\SellerNin;
use Illuminate\Http\Request;
use App\Models\OtpVerification;
use App\Services\ActivityLogger;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\ValidationException;

class RegisterSeller extends Controller
{

    public function register(Request $request)
    {
        try {

             if (User::where('email', $request->email)->exists()) {
            return response()->json([
                'status' => 'error',
                'message' => 'Email already exists.'
            ], 409); // Conflict status code
        }

        if (User::where('phone', $request->phone)->exists()) {
            return response()->json([
                'status' => 'error',
                'message' => 'Phone already exists.'
            ], 409);
        }

            $validatedData = $request->validate([
                "name" => "required|string|max:100",
                "lastname" => "required|string|max:100",
                "middlename" => "nullable|string|max:100",
                "dob" => "required|string|max:100",
                "email" => "required|unique:users,email",
                "gender" => "required",
                "ip_address" => "required",
                "phone" => "required|digits:11",
                "password" => "required|min:6",
                "nationality_id" => "required|exists:countries,id",
                "device_name" => "required",
                'role' => 'required',
               
            ]);
            DB::beginTransaction();
            $ip = $validatedData['ip_address'];
            $location = $this->getLocation($ip);

            $user = User::create([
                'name' => ucwords(strtolower($validatedData['name'])),
                'lastname' => ucwords(strtolower($validatedData['lastname'])),
                'email' => $validatedData['email'],
                'latitude' => $location['latitude'] ?? '',
                'longitude' => $location['longitude'] ?? '',
                'state' => $location['state'] ?? '',
                'city' => $location['city'] ?? '',
                'phone' => $validatedData['phone'],
                'nationality_id' => $validatedData['nationality_id'],
                'role' => $validatedData['role'],
                'status' => 'active',
                'is_fingerprint' => false,
                'profile_photo_url' => 'https://console.fixgini.com/icon.png',
                'is_pin' => false,
                'password' => Hash::make($validatedData['password']),
            ]);
            if (!$user) {
                throw new \Exception('User creation failed.');
            }
            $currency = Country::where('id', $validatedData['nationality_id'])->first();
            $wallet = Wallet::create([
                'user_id' => $user->id,
                'currency' => $currency->symbol
            ]);


            if (!$wallet) {
                throw new \Exception('Wallet creation failed.');
            }

             SellerNin::create([
                'user_id' => $user->id,
               'first_name' => ucwords(strtolower($validatedData['name'])),
                'last_name' => ucwords(strtolower($validatedData['lastname'])),
                'middle_name' => isset($validatedData['middlename']) ? ucwords(strtolower($validatedData['middlename'])) : 'NA',
                'dob' => $validatedData['dob'],
                 'gender' => $validatedData['gender'],
                
             ]);

            $device = $validatedData['device_name'];
            // Log the user activity
            $activityLogger = app(ActivityLogger::class);
            $activityLogger->log('Seller Account was created', $user->id, $user->role, $device);

            // Send welcome email
            $this->sendPhoneOtp($user);
            Mail::to($validatedData['email'])->send(new WelcomeMail($user));

            DB::commit();

            return response()->json(['status' => 'success', 'user' => $user, 'message' => 'Registration successful.'], 200);
        } catch (\Exception $e) {
            Log::error($e->getMessage());
            DB::rollBack();
            return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500);
        }
    }
    private function getLocation($ip)
    {
        try {
            $clientIp = $ip;

            // Attempt the first API call to api.ip2location.io
            $apiKey = env('IP2LOCATION_API_KEY');
            $response = Http::get("https://api.ip2location.io/?key={$apiKey}&ip={$clientIp}&format=json");

            if ($response->successful()) {
                $data = $response->json();
                $latitude = $data['latitude'] ?? '';
                $longitude = $data['longitude'] ?? '';

                // If lat/lng exists, perform reverse geocoding
                if ($latitude && $longitude) {
                    return $this->reverseGeocode($latitude, $longitude);
                }
            } else {
                // Check for specific error in the first API response
                $error = $response->json('error') ?? [];
                if (isset($error['error_code']) && $error['error_code'] === 10000) {
                    Log::warning('IP2Location API error: ' . $error['error_message']);
                }
            }

            // Fallback to ipinfo.io API
            $response = Http::get("https://ipinfo.io/{$clientIp}?token=55690b2a8bf492");
            if ($response->successful()) {
                $data = $response->json();

                // Extract location data from ipinfo response
                $location = explode(',', $data['loc'] ?? '');
                $latitude = $location[0] ?? '';
                $longitude = $location[1] ?? '';

                // Include city and state in the response
                return [
                    'city' => $data['city'] ?? '',
                    'state' => $data['region'] ?? '',
                    'country' => $data['country'] ?? '',
                    'latitude' => $latitude ?? '',
                    'longitude' => $longitude ?? '',
                ];
            }
        } catch (\Exception $e) {
            Log::error('Location fetching error: ' . $e->getMessage());
        }

        // Return a default response if all attempts fail
        return [
            'city' => '',
            'state' => '',
            'country' => '',
            'latitude' => '',
            'longitude' => '',
        ];
    }

    private function reverseGeocode($latitude, $longitude)
    {
        try {
            $response = Http::get('https://maps.googleapis.com/maps/api/geocode/json', [
                'latlng' => "{$latitude},{$longitude}",
                'key' => env('GOOGLE_API_KEY'),
            ]);

            if ($response->successful()) {
                $data = $response->json();
                if (isset($data['results']) && count($data['results']) > 0) {
                    $components = $data['results'][0]['address_components'];
                    $locationInfo = [
                        'city' => null,
                        'state' => null,
                        'latitude' => $latitude,
                        'longitude' => $longitude,
                    ];

                    foreach ($components as $component) {
                        if (in_array('locality', $component['types'])) {
                            $locationInfo['city'] = $component['long_name'];
                        }
                        if (in_array('administrative_area_level_1', $component['types'])) {
                            $locationInfo['state'] = $component['long_name'];
                        }
                    }
                    return $locationInfo;
                } else {
                    return [
                        'error' => true,
                        'message' => 'No results found.',
                    ];
                }
            } else {
                return [
                    'error' => true,
                    'message' => 'Failed to retrieve location data.',
                ];
            }
        } catch (\Exception $e) {
            return [
                'error' => true,
                'message' => $e->getMessage(),
            ];
        }
    }

    private function sendPhoneOtp($user)
    {
        try {

            $phone = $user['phone'];
            // Validate phone number
            if (!$phone) {
                return response(['status' => 'failed', 'message' => 'Phone number is required'], 400);
            }

            // Format phone number
            if (substr($phone, 0, 1) == '0') {
                $Fphone = '234' . substr($phone, 1);
            }

            $otp = random_int(100000, 999999);


            // Update or create OTP record
            OtpVerification::updateOrCreate(
                ['phone' => $phone],
                ['otp' => $otp]
            );

            // // Prepare SMS body
            $body = [
                'api_key' => config('services.termii.api'),
                'to' => $Fphone,
                'from' => 'N-Alert',
                'sms' => 'Hi there, your phone number confirmation code is ' . $otp . '. Thank you for choosing FixGini Services',
                'type' => 'plain',
                'channel' => 'dnd',
            ];

            // Uncomment and complete the API call to send the OTP
            $response = Http::post('https://api.ng.termii.com/api/sms/send', $body);
            if ($response->successful()) {
                return response(['status' => 'success', 'message' => 'OTP sent to phone number successfully'], 200);
            } else {
                Log::error($response->json());
                return response(['status' => 'failed', 'message' => 'Unable to send OTP to phone successfully'], 500);
            }
        } catch (\Throwable $th) {
            return response(['status' => 'failed', 'message' => 'Unable to send SMS ', 'data' =>  $th->getMessage()], 500);
        }
    }
    public function phoneOtp(Request $request)
    {
        try {
            $validatedData = $request->validate([
                'phone' => 'required',
            ]);
            $phone = $validatedData['phone'];
            // Validate phone number
            if (!$phone) {
                return response(['status' => 'failed', 'message' => 'Phone number is required'], 400);
            }

            // Format phone number
            if (substr($phone, 0, 1) == '0') {
                $Fphone = '234' . substr($phone, 1);
            }

            $otp = random_int(100000, 999999);


            // Update or create OTP record
            OtpVerification::updateOrCreate(
                ['phone' => $phone],
                ['otp' => $otp]
            );

            // // Prepare SMS body
            $body = [
                'api_key' => config('services.termii.api'),
                'to' => $Fphone,
                'from' => 'N-Alert',
                'sms' => 'Hi there, your phone number confirmation code is ' . $otp . '. Thank you for choosing FixGini Services',
                'type' => 'plain',
                'channel' => 'dnd',
            ];

            // Uncomment and complete the API call to send the OTP
            $response = Http::post('https://api.ng.termii.com/api/sms/send', $body);
            if ($response->successful()) {
                return response(['status' => 'success', 'message' => 'OTP sent to phone number successfully'], 200);
            } else {
                Log::error($response->json());
                return response(['status' => 'failed', 'message' => 'Unable to send OTP to phone successfully'], 500);
            }
        } catch (\Throwable $th) {
            return response(['status' => 'failed', 'message' => 'Unable to send SMS ', 'data' =>  $th->getMessage()], 500);
        }
    }

    // public function verifyPhoneOtp(Request $request)
    // {
    //     try {
    //         $validatedData = $request->validate([
    //             'phone' => 'required|digits:11|exists:otp_verifications,phone',
    //             'otp' => 'required|digits:6|exists:otp_verifications,otp',
    //         ], [
    //             'phone.exists' => 'The phone is not correct.',
    //             'otp.exists' => 'The otp is not correct.',
    //             'phone.required' => 'The phone field is required.',
    //             'otp.required' => 'The otp field is required.',
    //             'phone.digits' => 'The phone must be 11 digits.',
    //             'otp.digits' => 'The otp must be 6 digits.',
    //         ]);

    //         $phone = $validatedData['phone'];
    //         $otp = $validatedData['otp'];

    //         // Fetch the OTP record
    //         $otpRecord = OtpVerification::where([
    //             ['otp', '=', $otp],
    //             ['status', '=', 'unverified'],
    //             ['phone', '=', $phone],
    //         ])->latest()->first();

    //         if (!$otpRecord) {
    //             Log::info('Invalid OTP or OTP record not found for phone: ' . $phone);
    //             return response()->json(['message' => 'Invalid OTP. Please try again', 'status' => 'error'], 401);
    //         }

    //         DB::beginTransaction();
    //         try {
    //             // Update OTP record to verified
    //             $otpRecord->update([
    //                 'status' => 'verified'
    //             ]);
    //             // Find the user by phone
    //             $user = User::where('phone', $phone)->first();
    //             if (!$user) {
    //                 DB::rollBack();
    //                 Log::info('User not found for phone: ' . $phone);
    //                 return response()->json(['message' => 'User not found', 'status' => 'error'], 404);
    //             }

    //             // Update user status to active
    //             $user->update([
    //                 'status' => 'active'
    //             ]);


    //             // Update SellerNin record if exists
    //             $updateseller = SellerNin::where('user_id', $user->id)->first();
    //             if ($updateseller) {
    //                 $updateseller->update([
    //                     'phone_verified_at' => now(),
    //                     'status' => 'verified'
    //                 ]);
    //             }
    //             //send token
    //             $token = $user->createToken('api_token')->plainTextToken;

    //             // Delete the OTP record after success
    //             $otpRecord->delete();
    //             DB::commit();


    //             return response()->json(['message' => 'Phone number verified', 'status' => 'success', 'token' => $token, 'user' => $user], 200);
    //         } catch (\Throwable $th) {
    //             DB::rollBack();
    //             Log::error('Transaction failed: ' . $th->getMessage());
    //             return response()->json(['status' => 'error', 'message' => 'Failed to verify phone number. Please try again.', 'details' => $th->getMessage()], 500);
    //         }
    //     } catch (ValidationException $e) {
    //         Log::error('Validation error: ' . $e->getMessage());
    //         return response()->json(['status' => 'error', 'message' => $e->getMessage()], 422);
    //     } catch (\Throwable $th) {
    //         Log::error('OTP verification error: ' . $th->getMessage());
    //         return response()->json(['status' => 'error', 'message' => 'Network Issue! Please try again later.', 'details' => $th->getMessage()], 500);
    //     }
    // }

    public function verifyPhoneOtp(Request $request)
    {
        try {
            // Validate input
            $validatedData = $request->validate([
                'phone' => 'required|digits:11',
                'otp' => 'required|digits:6',
            ], [
                'phone.required' => 'The phone field is required.',
                'phone.digits' => 'The phone must be exactly 11 digits.',
                'otp.required' => 'The OTP field is required.',
                'otp.digits' => 'The OTP must be exactly 6 digits.',
            ]);

            $phone = $validatedData['phone'];
            $otp = $validatedData['otp'];

            // Find the most recent unverified OTP for this phone
            $otpRecord = OtpVerification::where('phone', $phone)
                ->where('status', 'unverified')
                ->latest()
                ->first();

            if (!$otpRecord) {
                Log::info("No unverified OTP found for phone: $phone");
                return response()->json([
                    'message' => 'No valid OTP found for this phone number. It may have expired or already been used.',
                    'status' => 'error'
                ], 401);
            }

            // Check if OTP matches
            if ($otpRecord->otp !== $otp) {
                Log::info("Invalid OTP attempt for phone: $phone");
                return response()->json([
                    'message' => 'The OTP entered is incorrect. Please try again.',
                    'status' => 'error'
                ], 401);
            }

            DB::beginTransaction();

            // Mark OTP as verified
            $otpRecord->update(['status' => 'verified']);

            // Update user status
            $user = User::where('phone', $phone)->first();
            if (!$user) {
                DB::rollBack();
                Log::info("User not found for phone: $phone");
                return response()->json([
                    'message' => 'No user associated with this phone number.',
                    'status' => 'error'
                ], 404);
            }

            $user->update(['status' => 'active']);

            // Update SellerNin if applicable
            // $updateseller = SellerNin::where('user_id', $user->id)->first();
            // if ($updateseller) {
            //     $updateseller->update([
            //         'phone_verified_at' => now(),
            //         'status' => 'verified'
            //     ]);
            // }

            // Create token
            $token = $user->createToken('api_token')->plainTextToken;

            // Remove OTP record
            $otpRecord->delete();

            DB::commit();

            return response()->json([
                'message' => 'Phone number successfully verified.',
                'status' => 'success',
                'token' => $token,
                'user' => $user
            ], 200);
        } catch (ValidationException $e) {
            return response()->json([
                'status' => 'error',
                'message' => $e->validator->errors()->first()
            ], 422);
        } catch (\Throwable $th) {
            DB::rollBack();
            Log::error('OTP verification failed: ' . $th->getMessage());
            return response()->json([
                'status' => 'error',
                'message' => 'An unexpected error occurred. Please try again later.',
                'details' => $th->getMessage()
            ], 500);
        }
    }
}