$clientId, 'redirect_uri' => $redirect, 'response_type' => 'code', 'scope' => 'offline_access Calendars.ReadWrite', 'response_mode' => 'query', ]); return "https://login.microsoftonline.com/{$tenant}/oauth2/v2.0/authorize?{$params}"; } /** * @return array|null */ public function exchangeCode(string $code): ?array { $tenant = config('meet.calendar.microsoft.tenant', 'common'); $response = Http::asForm()->post("https://login.microsoftonline.com/{$tenant}/oauth2/v2.0/token", [ 'code' => $code, 'client_id' => config('meet.calendar.microsoft.client_id'), 'client_secret' => config('meet.calendar.microsoft.client_secret'), 'redirect_uri' => config('meet.calendar.microsoft.redirect'), 'grant_type' => 'authorization_code', ]); return $response->successful() ? $response->json() : null; } public function createEvent(CalendarConnection $connection, Room $room, User $host): ?string { if (! $connection->access_token) { return null; } $response = Http::withToken($connection->access_token)->post( 'https://graph.microsoft.com/v1.0/me/events', $this->eventPayload($room, $host), ); return $response->successful() ? $response->json('id') : null; } public function updateEvent(CalendarConnection $connection, Room $room, User $host): bool { if (! $connection->access_token || ! $room->calendar_event_id) { return false; } return Http::withToken($connection->access_token)->patch( 'https://graph.microsoft.com/v1.0/me/events/'.$room->calendar_event_id, $this->eventPayload($room, $host), )->successful(); } public function deleteEvent(CalendarConnection $connection, Room $room): bool { if (! $connection->access_token || ! $room->calendar_event_id) { return false; } return Http::withToken($connection->access_token)->delete( 'https://graph.microsoft.com/v1.0/me/events/'.$room->calendar_event_id, )->successful(); } /** @return array */ protected function eventPayload(Room $room, User $host): array { $start = ($room->scheduled_at ?? now())->toIso8601String(); $end = ($room->scheduled_at ?? now())->addMinutes($room->duration_minutes ?? 60)->toIso8601String(); return [ 'subject' => $room->title, 'body' => [ 'contentType' => 'Text', 'content' => trim(($room->description ?? '')."\n\nJoin: ".$room->joinUrl()), ], 'start' => ['dateTime' => $start, 'timeZone' => $room->timezone], 'end' => ['dateTime' => $end, 'timeZone' => $room->timezone], 'location' => ['displayName' => $room->joinUrl()], 'organizer' => ['emailAddress' => ['address' => $host->email, 'name' => $host->name]], ]; } }