clientId); $url .= '&response_type=code'; $url .= '&redirect_uri=' . urlencode($this->redirectUri); return response()->json(['url' => $url]); } // Handle the redirect with authorization code and exchange for access token public function getRedirectCode(Request $request) { // Get the authorization code from the request $authorizationCode = $request->input('code'); if (!$authorizationCode) { return response()->json(['error' => 'Authorization code is missing'], 400); } //return $this->getCalendlyUserAndAvailability(); // Call method to fetch access token and cache it $this->getAccessTokenFromCode($authorizationCode); return response()->json(['message' => 'Admin has been authenticated.'], 200); } // Handle the redirect with authorization code and exchange for access token public function getAvailabeSlotDates(Request $request) { return $this->getCalendlyUserAndAvailability(); // Call method to fetch access token and cache it } // Fetch or refresh access token if needed public function getAccessToken() { // Check if the access token exists in cache if (Cache::has('calendly_access_token')) { return Cache::get('calendly_access_token'); } // If no token is available, return error or trigger refresh return response()->json(['error' => 'No valid access token. Please authenticate again.'], 401); } // Exchange authorization code for access token and store it in cache private function getAccessTokenFromCode($authorizationCode) { $tokenUrl = 'https://auth.calendly.com/oauth/token'; // Use GuzzleHttp client to make the POST request $client = new Client(); try { $response = $client->post($tokenUrl, [ 'form_params' => [ 'grant_type' => 'authorization_code', 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'redirect_uri' => $this->redirectUri, 'code' => $authorizationCode, ], 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', ], ]); // Decode the JSON response $data = json_decode($response->getBody()->getContents(), true); // Store access token and refresh token in cache with an expiration //Cache::put('calendly_access_token', $data['access_token'], now()->addSeconds($data['expires_in'])); //Cache::put('calendly_refresh_token', $data['refresh_token'], now()->addDays(30)); // Refresh tokens don't expire until used return response()->json([ 'access_token' => $data['access_token'], 'refresh_token' => $data['refresh_token'], 'token_type' => $data['token_type'], 'expires_in' => $data['expires_in'] ]); } catch (\Exception $e) { // Handle errors return response()->json(['error' => 'Failed to fetch access token: ' . $e->getMessage()], 500); } } // Use refresh token to get a new access token when expired private function refreshAccessToken() { if (!Cache::has('calendly_refresh_token')) { return response()->json(['error' => 'Refresh token not available.'], 401); } $refreshToken = Cache::get('calendly_refresh_token'); $tokenUrl = 'https://auth.calendly.com/oauth/token'; $client = new Client(); try { $response = $client->post($tokenUrl, [ 'form_params' => [ 'grant_type' => 'refresh_token', 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'refresh_token' => $refreshToken, ], 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', ], ]); // Decode the response $data = json_decode($response->getBody()->getContents(), true); // Store the new access token and refresh token in cache Cache::put('calendly_access_token', $data['access_token'], now()->addSeconds($data['expires_in'])); Cache::put('calendly_refresh_token', $data['refresh_token'], now()->addDays(30)); // New refresh token return $data['access_token']; } catch (\Exception $e) { return response()->json(['error' => 'Failed to refresh access token: ' . $e->getMessage()], 500); } } function getCalendlyUserAndAvailability() { // Get the access token from cache $accessToken = Cache::get('calendly_access_token'); // If the token is not in cache, fetch a new one if (!$accessToken) { $accessToken = $this->fetchCalendlyAccessToken(); } if (!$accessToken) { return response()->json(['error' => 'Token Expired!'], 500); } // 1. Call the /users/me API to get user information $client = new Client(); try { $response = $client->request('GET', 'https://api.calendly.com/users/me', [ 'headers' => [ 'Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/json', ] ]); $data = json_decode($response->getBody(), true); $userUri = $data['resource']['uri']; } catch (\Exception $e) { return response()->json(['error' => 'Failed to fetch user details: ' . $e->getMessage()], 500); } // 2. Use the user URI to fetch availability schedules try { $availabilityResponse = $client->request('GET', 'https://api.calendly.com/user_availability_schedules', [ 'headers' => [ 'Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/json', ], 'query' => [ 'user' => $userUri ] ]); $availabilityData = json_decode($availabilityResponse->getBody(), true); // Get the rules from the availability data $rules = $availabilityData['collection'][0]['rules']; // Map day of the week to the date in the current week $weekDates = $this->getCurrentWeekDates(); // Get this week's dates // Add the corresponding date to each rule $updatedRules = array_map(function ($rule) use ($weekDates) { $wday = $rule['wday']; // Check if we have a corresponding date for this weekday if (isset($weekDates[$wday])) { $rule['date'] = $weekDates[$wday]; // Add date to the rule } else { $rule['date'] = null; // No availability on that day } return $rule; }, $rules); // Add the updated rules with dates back to the response $availabilityData['collection'][0]['rules'] = $updatedRules; // Get the rules from the availability data $rules = $availabilityData['collection'][0]['rules']; // Map day of the week to the date in the current week $weekDates = $this->getCurrentWeekDates(); // Get this week's dates // Add the corresponding date to each rule $updatedRules = array_map(function ($rule) use ($weekDates) { $wday = $rule['wday']; // Check if we have a corresponding date for this weekday if (isset($weekDates[$wday])) { $rule['date'] = $weekDates[$wday]; // Add date to the rule } else { $rule['date'] = null; // No availability on that day } return $rule; }, $rules); $filteredDates = array_values(array_filter(array_map(function ($rule) { return !empty($rule['intervals']) ? $rule['date'] : null; }, $updatedRules))); // Add the updated rules with dates back to the response $availabilityData['collection'][0]['rules'] = $updatedRules; return response()->json($availabilityData, 200); } catch (\Exception $e) { return response()->json(['error' => 'Failed to fetch availability: ' . $e->getMessage()], 500); } } private function getCurrentWeekDates() { $weekDates = []; // Start from Sunday as 0, loop through the week for ($day = 0; $day < 7; $day++) { $carbonDate = Carbon::now()->startOfWeek()->addDays($day); // Start on Sunday $weekday = strtolower($carbonDate->format('l')); // Get the day name (sunday, monday, etc.) $weekDates[$weekday] = $carbonDate->toDateString(); // Store the date for that day } return $weekDates; // Return array of week dates } // Helper function to fetch access token function fetchCalendlyAccessToken() { $client = new Client(); try { $response = $client->post('https://auth.calendly.com/oauth/token', [ 'form_params' => [ 'grant_type' => 'authorization_code', 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'redirect_uri' => $this->redirectUri, 'code' => request()->input('code'), // Get authorization code from request ], 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', ] ]); $data = json_decode($response->getBody(), true); $accessToken = $data['access_token']; // Store access token in cache for 2 hours Cache::put('calendly_access_token', $accessToken, 120 * 60); return $accessToken; } catch (\Exception $e) { return null; } } public function getAvailableTimes(Request $request) { try { // Validate start_time and end_time input /* $validatedData = $request->validate([ 'start_time' => 'required|date_format:Y-m-d\TH:i:s\Z', 'end_time' => 'required|date_format:Y-m-d\TH:i:s\Z', ]); */ // Get the access token from cache $accessToken = Cache::get('calendly_access_token'); // If the token is not in cache, fetch a new one if (!$accessToken) { $accessToken = $this->fetchCalendlyAccessToken(); } if (!$accessToken) { return response()->json(['error' => 'Unable to retrieve access token'], 500); } // 1. Call the /users/me API to get user information $client = new Client(); $response = $client->request('GET', 'https://api.calendly.com/users/me', [ 'headers' => [ 'Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/json', ] ]); $data = json_decode($response->getBody(), true); $userUri = $data['resource']['uri']; // 1. Call the /users/me API to get user information $client = new Client(); $responseEvent = $client->request('GET', 'https://api.calendly.com/event_types?user=' . $userUri, [ 'headers' => [ 'Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/json', ] ]); $dataEvent = json_decode($responseEvent->getBody(), true); $even_type_url = $dataEvent['collection'][0]['uri']; $userUri = $data['resource']['uri']; $client = new Client(); // Prepare API endpoint with the required parameters $eventTypeUrl = 'https://api.calendly.com/event_type_available_times'; $queryParams = [ 'event_type' => $even_type_url, //'https://api.calendly.com/event_types/60992c14-2f0b-42c2-af7b-95062d065600', // Use your event_type URL 'start_time' => $request->input('start_time'), 'end_time' => $request->input('end_time') ]; $str = "event_type=" . urlencode($queryParams['event_type']) . "&" . "start_time=" . urlencode($queryParams['start_time']) . "&" . "end_time=" . urlencode($queryParams['end_time']); $eventTypeUrl = $eventTypeUrl . "?" . ($str); // Send the request to Calendly $response = $client->request('GET', $eventTypeUrl, [ 'headers' => [ 'Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/json', ], // 'query' => $queryParams ]); $data = json_decode($response->getBody(), true); $processedTimes = array_map(function ($item) { $dateTime = new DateTime($item['start_time']); $readableTime = $dateTime->format('Y-m-d H:i'); // Format: YYYY-MM-DD h:mm AM/PM return [ 'status' => $item['status'], 'start_time' => $readableTime, 'invitees_remaining' => $item['invitees_remaining'], 'scheduling_url' => $item['scheduling_url'] ]; }, $data['collection']); // Extract scheduling URLs from available time slots $availableTimes = array_map(function ($slot) { return $slot['scheduling_url']; }, $data['collection']); return response()->json([ 'available_times' => $processedTimes ], 200); } catch (\Exception $e) { return response()->json(['error' => 'Failed to fetch available times: ' . $e->getMessage()], 500); } } }