encryption = $encryption; } // ══════════════════════════════════════════════ // PASSENGER LOGIN & REGISTRATION // ══════════════════════════════════════════════ public function passengerLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); $phone = $request->input('phone'); $password = $request->input('password'); $fingerprint = $request->input('fingerprint'); $fcmToken = $request->input('fcm_token'); $rawPhone = $phone; $localPhone = '0' . substr($phone, 3); $encRawPhone = $this->encryption->encrypt($rawPhone); $encLocalPhone = $this->encryption->encrypt($localPhone); $passenger = Passenger::active() ->whereIn('phone', [$rawPhone, $localPhone, $encRawPhone, $encLocalPhone]) ->first(); if (!$passenger) { return $this->failure('Invalid credentials'); } $storedPassword = $passenger->password; if (!password_verify($password, $storedPassword) && !hash_equals($storedPassword, hash_hmac('sha256', $password, config('intaleq.jwt_secret')))) { return $this->failure('Invalid credentials'); } $passengerToken = PassengerToken::where('passengerID', $passenger->id)->first(); $encryptedFcm = $this->encryption->encrypt($fcmToken); if ($passengerToken) { $passengerToken->update([ 'token' => $encryptedFcm, 'fingerPrint' => $fingerprint, ]); } else { PassengerToken::create([ 'token' => $encryptedFcm, 'passengerID' => $passenger->id, 'fingerPrint' => $fingerprint, ]); } if (empty($passenger->api_key)) { $this->generateApiKeys($passenger); } $jwt = $this->createJwt($passenger->id, 'passenger', $fingerprint, 3600); // 1 Hour return $this->success([ 'token' => $jwt, 'expires_in' => 3600, 'user_id' => $passenger->id, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret, ]); } public function passengerRegister(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'email' => 'required|email', 'first_name' => 'required|string', 'last_name' => 'required|string', 'fingerprint' => 'nullable|string', 'fcm_token' => 'nullable|string', ]); $phone = $request->input('phone'); $encryptedPhone = $this->encryption->encrypt($phone); if (Passenger::where('phone', $encryptedPhone)->exists()) { return $this->failure('Phone number already registered'); } $passengerId = (string) mt_rand(1000000000, 9999999999) . mt_rand(100000000, 999999999); $passenger = Passenger::create([ 'id' => $passengerId, 'phone' => $encryptedPhone, 'email' => $this->encryption->encrypt($request->input('email')), 'password' => password_hash($request->input('password', '123456'), PASSWORD_BCRYPT), 'first_name' => $this->encryption->encrypt($request->input('first_name')), 'last_name' => $this->encryption->encrypt($request->input('last_name')), 'gender' => $this->encryption->encrypt($request->input('gender', 'male')), 'birthdate' => $this->encryption->encrypt($request->input('birthdate', '2000-01-01')), 'site' => $request->input('site', 'Syria'), ]); if ($request->has('fcm_token')) { PassengerToken::create([ 'token' => $this->encryption->encrypt($request->input('fcm_token')), 'passengerID' => $passengerId, 'fingerPrint' => $request->input('fingerprint', 'unknown'), ]); } $this->generateApiKeys($passenger); $jwt = $this->createJwt($passengerId, 'passenger', $request->input('fingerprint', 'unknown'), 3600); return $this->success([ 'token' => $jwt, 'expires_in' => 3600, 'user_id' => $passengerId, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret, ], 201); } public function driverRegister(Request $request): JsonResponse { // 1. Validate Required Driver Fields $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'first_name' => 'required|string', 'last_name' => 'required|string', // Car Fields 'vin' => 'required|string', 'car_plate' => 'required|string', 'make' => 'required|string', 'model' => 'required|string', 'year' => 'required|string', 'expiration_date' => 'required|string', 'color' => 'required|string', 'owner' => 'required|string', 'color_hex' => 'required|string', 'fuel' => 'required|string', // Documents 'driver_license_front' => 'required|url', 'driver_license_back' => 'required|url', 'car_license_front' => 'required|url', 'car_license_back' => 'required|url', ]); $data = $request->all(); // 2. Format Phone (Syrian Logic) $phone = $data['phone']; $phone = preg_replace('/[ \-\(\)\+]/', '', $phone); $phone = trim($phone); if (strpos($phone, '00963') === 0) $phone = substr($phone, 2); elseif (strpos($phone, '0963') === 0) $phone = substr($phone, 1); if (strpos($phone, '96309') === 0) $phone = '9639' . substr($phone, 5); elseif (strpos($phone, '9630') === 0) $phone = '9639' . substr($phone, 4); elseif (strpos($phone, '09') === 0) $phone = '963' . substr($phone, 1); elseif (strpos($phone, '9') === 0 && strlen($phone) == 9) $phone = '963' . $phone; elseif (strpos($phone, '0') === 0 && strlen($phone) == 10) $phone = '963' . substr($phone, 1); if (strpos($phone, '963') === 0 && strlen($phone) > 3) { if (strpos($phone, '9639') !== 0) { $phone = '9639' . substr($phone, 3); } } $data['phone'] = $phone; // 3. Format Birthdate if (!empty($data['birthdate'])) { $data['birthdate'] = trim($data['birthdate']) . '-01-01'; } else { $data['birthdate'] = '1970-01-01'; } // 4. Generate ID & Email if (empty($data['id'])) { $data['id'] = 'DRV' . date('YmdHis') . random_int(1000, 9999); } if (empty($data['email'])) { $data['email'] = $data['phone'] . '@intaleqapp.com'; } // 5. Encrypt Driver Fields $toEncryptDriver = [ 'phone', 'email', 'first_name', 'last_name', 'name_arabic', 'gender', 'national_number', 'address', 'site', 'fullNameMaritial', 'birthdate' ]; foreach ($toEncryptDriver as $f) { if (!empty($data[$f]) && $data[$f] !== 'Not specified') { $data[$f] = $this->encryption->encrypt($data[$f]); } else { $data[$f] = null; } } // 6. Encrypt Car Fields $car = [ 'vin' => $this->encryption->encrypt($request->input('vin')), 'car_plate' => $this->encryption->encrypt($request->input('car_plate')), 'make' => $request->input('make'), 'model' => $request->input('model'), 'year' => $request->input('year'), 'expiration_date' => $request->input('expiration_date'), 'color' => $request->input('color'), 'owner' => $this->encryption->encrypt($request->input('owner')), 'color_hex' => $request->input('color_hex'), 'fuel' => $request->input('fuel'), 'vehicle_category_id' => $request->input('vehicle_category_id', 1), 'fuel_type_id' => $request->input('fuel_type_id', 1), ]; // 7. Hash Password (HMAC + password_hash) $pepper = config('intaleq.hmac_secret', env('SECRET_KEY_HMAC')); $baseParts = [ $data['id'], $phone, // Original formatted phone ]; if (!empty($request->input('national_number')) && $request->input('national_number') !== 'Not specified') { $baseParts[] = $request->input('national_number'); } elseif (!empty($request->input('birthdate'))) { $year = substr($request->input('birthdate'), 0, 4); if (preg_match('/^\d{4}$/', $year)) { $baseParts[] = $year; } } $baseString = implode('|', $baseParts); $rawSecret = hash_hmac('sha256', $baseString, $pepper, true); $pwdHashed = password_hash($rawSecret, PASSWORD_DEFAULT); // 8. Check Duplicates $dup = DB::connection('primary')->table('driver') ->where('phone', $data['phone']) ->orWhere('email', $data['email']) ->exists(); if ($dup) { return response()->json(['status' => 'Failure', 'data' => 'Phone or email already registered.']); } DB::connection('primary')->beginTransaction(); try { // 9. Insert Driver DB::connection('primary')->table('driver')->insert([ 'id' => $data['id'], 'phone' => $data['phone'], 'email' => $data['email'], 'password' => $pwdHashed, 'gender' => $data['gender'] ?? 'Male', 'license_type' => $request->input('license_type', 'yet'), 'national_number' => $data['national_number'], 'name_arabic' => $data['name_arabic'], 'issue_date' => $request->input('issue_date', '2020-01-01'), 'expiry_date' => $request->input('expiry_date', 'yet'), 'license_categories' => $request->input('license_categories', 'B'), 'address' => $data['address'], 'licenseIssueDate' => $request->input('licenseIssueDate', '2020-01-01'), 'status' => $request->input('status', 'yet'), 'birthdate' => $data['birthdate'], 'site' => $data['site'] ?? 'demascus', 'first_name' => $data['first_name'], 'last_name' => $data['last_name'], 'accountBank' => 'yet', 'bankCode' => 'yet', 'employmentType' => $request->input('employmentType', 'yet'), 'maritalStatus' => $request->input('maritalStatus', 'yet'), 'fullNameMaritial' => $data['fullNameMaritial'] ?? 'yet', 'expirationDate' => $request->input('expirationDate', 'yet'), 'created_at' => now(), 'updated_at' => now(), ]); // 10. Insert Car Registration $isDefault = DB::connection('primary')->table('CarRegistration') ->where('driverID', $data['id'])->exists() ? 0 : 1; $carRegId = DB::connection('primary')->table('CarRegistration')->insertGetId([ 'driverID' => $data['id'], 'vin' => $car['vin'], 'car_plate' => $car['car_plate'], 'make' => $car['make'], 'model' => $car['model'], 'year' => $car['year'], 'expiration_date' => $car['expiration_date'], 'color' => $car['color'], 'owner' => $car['owner'], 'color_hex' => $car['color_hex'], 'fuel' => $car['fuel'], 'vehicle_category_id' => $car['vehicle_category_id'], 'fuel_type_id' => $car['fuel_type_id'], 'isDefault' => $isDefault, 'status' => 'yet', 'created_at' => now(), ]); // 11. Insert Documents $docKeys = [ 'driver_license_front', 'driver_license_back', 'car_license_front', 'car_license_back' ]; $docUrls = []; foreach ($docKeys as $k) { $url = $request->input($k); $docUrls[$k] = $url; $name = basename(parse_url($url, PHP_URL_PATH) ?? ''); if ($name === '') { $name = $k . '_' . time() . '.jpg'; } DB::connection('primary')->table('driver_documents')->insert([ 'driverID' => $data['id'], 'doc_type' => $k, 'image_name' => $name, 'link' => $url, 'upload_date' => now(), ]); } DB::connection('primary')->commit(); // 12. FCM Notification to Admin (Fire and Forget) try { $fcm = resolve(\App\Services\FcmService::class); $driverFullName = $request->input('first_name') . ' ' . $request->input('last_name'); $fcm->sendToTopic( 'service', 'تسجيل سائق جديد', "سائق جديد ($driverFullName) سجل برقم ID: {$data['id']} وهو بانتظار المراجعة والتفعيل.", ['category' => 'new_driver_registration'] ); } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error("FCM Admin notification failed: " . $e->getMessage()); } return response()->json([ 'status' => 'success', 'driverID' => $data['id'], 'carRegID' => $carRegId, 'documents' => $docUrls ]); } catch (\Exception $e) { DB::connection('primary')->rollBack(); return response()->json(['status' => 'Failure', 'message' => 'Registration failed: ' . $e->getMessage()]); } } // ══════════════════════════════════════════════ // DRIVER LOGIN // ══════════════════════════════════════════════ public function driverLogin(Request $request): JsonResponse { $request->validate([ 'email' => 'required|string', 'password' => 'required|string', ]); $email = $request->input('email'); $searchEmail = (str_contains($email, '+') || str_contains($email, '/')) ? $email : $this->encryption->encrypt($email); $row = DB::connection('primary') ->table('driver as d') ->leftJoin('phone_verification', 'phone_verification.phone_number', '=', 'd.phone') ->leftJoin('invites', 'invites.inviterDriverPhone', '=', 'd.phone') ->leftJoin('shamCash', 'shamCash.driver_id', '=', 'd.id') ->leftJoin('CarRegistration', 'CarRegistration.driverID', '=', 'd.id') ->leftJoin('captain_bank', 'captain_bank.captain_id', '=', 'd.id') ->select([ 'd.id', 'd.phone', 'd.email', 'd.gender', 'd.status', 'd.first_name', 'd.last_name', 'd.password', 'd.name_arabic', 'phone_verification.verified as is_verified', 'invites.isInstall', 'shamCash.is_claimed', 'CarRegistration.make', 'CarRegistration.model', 'CarRegistration.year', 'captain_bank.bankCode', 'captain_bank.accountBank', ]) ->where('d.email', $searchEmail) ->first(); if (!$row) { return response()->json(['status' => 'failure', 'message' => 'Invalid credentials']); } $driver = (array) $row; // Verify password if (!password_verify($request->input('password'), $driver['password'])) { return response()->json(['status' => 'failure', 'message' => 'Invalid credentials']); } // Decrypt necessary fields $fieldsToDecrypt = ['email', 'phone', 'first_name', 'last_name', 'gender', 'name_arabic']; foreach ($fieldsToDecrypt as $field) { if (!empty($driver[$field])) { $dec = $this->encryption->decrypt($driver[$field]); if ($dec) $driver[$field] = $dec; } } unset($driver['password']); // don't send password // Default values for null fields $driver['is_verified'] = $driver['is_verified'] ?? 0; $driver['isInstall'] = $driver['isInstall'] ?? 0; $driver['is_claimed'] = $driver['is_claimed'] ?? 0; $driver['bankCode'] = $driver['bankCode'] ?? ''; $driver['accountBank'] = $driver['accountBank'] ?? ''; $driver['make'] = $driver['make'] ?? ''; $driver['model'] = $driver['model'] ?? ''; $driver['year'] = $driver['year'] ?? ''; return response()->json([ 'status' => 'success', 'data' => [$driver] ]); } public function driverLoginGoogle(Request $request): JsonResponse { $request->validate(['id' => 'required|string']); $id = $request->input('id'); $row = DB::connection('primary') ->table('driver as d') ->leftJoin('phone_verification', 'phone_verification.phone_number', '=', 'd.phone') ->leftJoin('invites', 'invites.inviterDriverPhone', '=', 'd.phone') ->leftJoin('driver_gifts', 'driver_gifts.driver_id', '=', 'd.id') ->leftJoin('CarRegistration', 'CarRegistration.driverID', '=', 'd.id') ->select([ 'd.id', 'd.phone', 'd.email', 'd.gender', 'd.status', 'd.first_name', 'd.last_name', 'd.name_arabic', 'd.birthdate', 'd.site', 'd.employmentType', 'd.maritalStatus', 'd.accountBank', 'd.bankCode', 'phone_verification.verified as is_verified', 'invites.isInstall', 'invites.isGiftToken', 'driver_gifts.is_claimed', 'CarRegistration.make', 'CarRegistration.model', 'CarRegistration.year', ]) ->where('d.id', $id) ->first(); if (!$row) { return response()->json(['status' => 'Failure', 'data' => 'User does not exist.']); } $driver = (array) $row; // Decrypt necessary fields $fieldsToDecrypt = ['email', 'phone', 'first_name', 'last_name', 'gender', 'name_arabic']; foreach ($fieldsToDecrypt as $field) { if (!empty($driver[$field])) { $dec = $this->encryption->decrypt($driver[$field]); if ($dec) $driver[$field] = $dec; } } // Default values for null fields $driver['is_verified'] = $driver['is_verified'] ?? 0; $driver['isInstall'] = $driver['isInstall'] ?? 0; $driver['is_claimed'] = $driver['is_claimed'] ?? 0; $driver['bankCode'] = $driver['bankCode'] ?? ''; $driver['accountBank'] = $driver['accountBank'] ?? ''; $driver['make'] = $driver['make'] ?? ''; $driver['model'] = $driver['model'] ?? ''; $driver['year'] = $driver['year'] ?? ''; return response()->json([ 'status' => 'success', 'count' => 1, 'data' => [$driver] ]); } // ══════════════════════════════════════════════ // HANDSHAKE (V1 Compatibility) // ══════════════════════════════════════════════ public function passengerJwtHandshake(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $passenger = Passenger::find($request->input('id')); if (!$passenger) return $this->failure('User not found'); // Security Check: Verify fingerprint matches stored token $storedToken = DB::connection('primary')->table('tokens') ->where('passengerID', $passenger->id) ->first(); if ($storedToken && !hash_equals((string)$storedToken->fingerPrint, (string)$request->input('fingerPrint'))) { return $this->failure('Security mismatch: Invalid device fingerprint', 403); } if (empty($passenger->api_key)) { $this->generateApiKeys($passenger); } $jwt = $this->createJwt($passenger->id, 'passenger', $request->input('fingerPrint'), 3600); return response()->json([ 'status' => 'success', 'jwt' => $jwt, 'expires_in' => 3600, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret ]); } public function driverJwtHandshake(Request $request): JsonResponse { // Flutter sends the email in the 'password' field, plus 'id' and 'fingerPrint' $request->validate([ 'id' => 'required|string', 'fingerPrint' => 'required|string', 'password' => 'required|string', 'fcm_token' => 'required|string' ]); $driver = Driver::where('id', $request->input('id'))->first(); if (!$driver) return $this->failure('User not found'); // The Flutter app sends the app-level secret (passnpassenger) in the 'password' field. // Since the Flutter app modifies this string locally (e.g., split(Env.addd)[0]), // it may not match the raw env('passwordnewpassenger') on the server exactly. // We will rely on the fingerprint check below for security, as done in passengerJwtHandshake. $appSecret = config('intaleq.wallet_app_password', ''); if ($appSecret !== '') { if ($request->input('password') !== $appSecret && $request->input('password') !== $this->encryption->decrypt($driver->email)) { \Log::warning('App verification mismatch, proceeding to fingerprint check', [ 'driver_id' => $driver->id, ]); } } // Security Check: Verify fingerprint matches stored token $storedToken = DB::connection('primary')->table('driverToken') ->where('captain_id', $driver->id) ->first(); // If a fingerprint exists, verify it. Otherwise, save the new one (acts as first time) if ($storedToken) { if (!empty($storedToken->fingerPrint) && !hash_equals((string)$storedToken->fingerPrint, (string)$request->input('fingerPrint'))) { return $this->failure('Security mismatch: Invalid device fingerprint', 403); } // Update fingerprint and token DB::connection('primary')->table('driverToken')->where('captain_id', $driver->id) ->update([ 'fingerPrint' => $request->input('fingerPrint'), 'token' => $request->input('fcm_token'), ]); } else { DB::connection('primary')->table('driverToken')->insert([ 'captain_id' => $driver->id, 'fingerPrint' => $request->input('fingerPrint'), 'token' => $request->input('fcm_token'), ]); } if (empty($driver->api_key)) { $this->generateApiKeys($driver); } $jwt = $this->createJwt($driver->id, 'driver', $request->input('fingerPrint'), 14400); return response()->json([ 'status' => 'success', 'jwt' => $jwt, 'expires_in' => 14400, 'api_key' => $driver->api_key, 'api_secret' => $driver->api_secret ]); } // ══════════════════════════════════════════════ // WALLET LOGIN // ══════════════════════════════════════════════ public function passengerWalletLogin(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $jwt = $this->createWalletJwt($request->input('id'), $request->input('fingerPrint'), $request->input('aud', 'TripzWallet'), 60); $hmac = hash_hmac('sha256', $request->input('id'), config('intaleq.wallet_hmac_secret')); return $this->success(['jwt' => $jwt, 'hmac' => $hmac, 'expires_in' => 60]); } public function driverWalletLogin(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $jwt = $this->createWalletJwt($request->input('id'), $request->input('fingerPrint'), $request->input('aud', 'TripzWallet'), 60, config('intaleq.wallet_jwt_secret')); $hmac = hash_hmac('sha256', $request->input('id'), config('intaleq.wallet_hmac_secret')); return $this->success(['jwt' => $jwt, 'hmac' => $hmac, 'expires_in' => 60]); } // ══════════════════════════════════════════════ // ADMIN LOGIN // ══════════════════════════════════════════════ public function adminLogin(Request $request): JsonResponse { $request->validate(['device_number' => 'required|string', 'password' => 'required|string']); $admin = DB::connection('primary')->table('adminUser')->where('device_number', $request->input('device_number'))->first(); if (!$admin || !password_verify($request->input('password'), $admin->password ?? '')) { return $this->failure('Invalid credentials'); } $jwt = $this->createJwt((string)$admin->id, 'admin', $request->input('device_number'), 900); return $this->success(['token' => $jwt, 'expires_in' => 900, 'user_id' => $admin->id]); } // ══════════════════════════════════════════════ // GOOGLE LOGIN // ══════════════════════════════════════════════ public function passengerLoginGoogle(Request $request): JsonResponse { $request->validate(['email' => 'required|string', 'id' => 'required|string']); $email = $request->input('email'); $searchEmail = (str_contains($email, '+') || str_contains($email, '/')) ? $email : $this->encryption->encrypt($email); $row = DB::connection('primary') ->table('passengers as p') ->leftJoin('phone_verification_passenger', 'phone_verification_passenger.phone_number', '=', 'p.phone') ->leftJoin('invitesToPassengers', 'invitesToPassengers.inviterPassengerPhone', '=', 'p.phone') ->leftJoin('promos', 'promos.passengerID', '=', 'p.id') ->select([ 'p.id', 'p.phone', 'p.email', 'p.gender', 'p.status', 'p.birthdate', 'p.site', 'p.first_name', 'p.last_name', 'p.sosPhone', 'p.education', 'p.employmentType', 'p.maritalStatus', 'p.created_at', 'p.updated_at', 'phone_verification_passenger.verified', 'invitesToPassengers.isInstall', 'invitesToPassengers.inviteCode', 'invitesToPassengers.isGiftToken', 'promos.promo_code as promo', 'promos.amount as discount', 'promos.validity_end_date as validity', 'p.api_key', 'p.api_secret', ]) ->selectSub(function ($query) use ($request) { $query->from('packageInfo') ->select('version') ->where('platform', $request->input('platform', 'ios')) ->limit(1); }, 'package') ->where('p.email', $searchEmail) ->where('p.id', $request->input('id')) ->first(); if (!$row) return response()->json(['status' => 'Failure', 'data' => 'User does not exist.']); $data = (array) $row; $data['package'] = $data['package'] ?? '1.1.33'; // Default to avoid Null error in Flutter // Ensure API keys exist if (empty($data['api_key'])) { $passenger = Passenger::find($data['id']); if ($passenger) { $this->generateApiKeys($passenger); $data['api_key'] = $passenger->api_key; $data['api_secret'] = $passenger->api_secret; } } foreach ($data as $key => $value) { if (is_string($value) && !in_array($key, ['id', 'status', 'created_at', 'updated_at', 'verified', 'isInstall', 'isGiftToken', 'api_key', 'api_secret', 'package'])) { $dec = $this->encryption->decrypt($value); if ($dec) $data[$key] = $dec; } } // Fetch Notification Token & Fingerprint $tokenRow = DB::connection('primary')->table('tokens')->where('passengerID', $data['id'])->first(); if ($tokenRow) { $data['fcm_token'] = $this->encryption->decrypt($tokenRow->token); $data['fingerprint'] = $tokenRow->fingerPrint; } else { $data['fcm_token'] = null; $data['fingerprint'] = null; } // Generate JWT using the header fingerprint, or fallback to the stored one $clientFp = $request->header('X-Device-FP'); $jwtFp = !empty($clientFp) ? $clientFp : ($data['fingerprint'] ?? 'unknown'); $jwt = $this->createJwt($data['id'], 'passenger', $jwtFp, 3600); return response()->json([ 'status' => 'success', 'count' => 1, 'data' => [$data], 'jwt' => $jwt, 'expires_in' => 3600 ]); } // ══════════════════════════════════════════════ // HELPERS // ══════════════════════════════════════════════ private function createJwt(string $userId, string $userType, string $fingerprint, int $expiry): string { $payload = [ 'user_id' => $userId, 'user_type' => $userType, 'fingerprint' => $fingerprint, 'iat' => time(), 'exp' => time() + $expiry, 'aud' => 'mobile-app', 'iss' => 'Tripz', 'jti' => bin2hex(random_bytes(16)), ]; return JWT::encode($payload, config('intaleq.jwt_secret'), 'HS256'); } private function createWalletJwt(string $userId, string $fingerprint, string $audience, int $expiry, ?string $secret = null): string { $payload = [ 'user_id' => $userId, 'fingerPrint' => hash('sha256', $fingerprint . config('intaleq.fp_pepper', '')), 'exp' => time() + $expiry, 'iat' => time(), 'iss' => 'Tripz-Wallet', 'aud' => $audience, 'jti' => bin2hex(random_bytes(16)), ]; return JWT::encode($payload, $secret ?? config('intaleq.jwt_secret'), 'HS256'); } private function generateApiKeys($model) { $model->api_key = bin2hex(random_bytes(16)); $model->api_secret = bin2hex(random_bytes(32)); DB::connection('primary')->table($model->getTable())->where('id', $model->id)->update([ 'api_key' => $model->api_key, 'api_secret' => $model->api_secret ]); } }