$clientId, 'redirect_uri' => $this->redirectUri(), 'response_type' => 'code', 'approval_prompt' => 'auto', 'scope' => config('services.strava.scope', 'read,activity:read'), 'state' => $state, ], '', '&', PHP_QUERY_RFC3986); } public function exchangeCode(string $code): array { return $this->tokenRequest() ->post('/oauth/token', [ 'client_id' => config('services.strava.client_id'), 'client_secret' => config('services.strava.client_secret'), 'code' => $code, 'grant_type' => 'authorization_code', ]) ->throw() ->json(); } public function refresh(StravaConnection $connection): StravaConnection { $payload = $this->tokenRequest() ->post('/oauth/token', [ 'client_id' => config('services.strava.client_id'), 'client_secret' => config('services.strava.client_secret'), 'grant_type' => 'refresh_token', 'refresh_token' => $connection->refresh_token, ]) ->throw() ->json(); $connection->update([ 'access_token' => $payload['access_token'], 'refresh_token' => $payload['refresh_token'], 'expires_at' => Carbon::createFromTimestamp((int) $payload['expires_at']), ]); return $connection->refresh(); } public function deauthorize(StravaConnection $connection): void { $accessToken = $this->accessToken($connection); Http::baseUrl('https://www.strava.com') ->timeout(10) ->connectTimeout(3) ->retry([100, 500, 1000]) ->post('/oauth/deauthorize?'.http_build_query([ 'access_token' => $accessToken, ], '', '&', PHP_QUERY_RFC3986)) ->throw(); } public function activities(StravaConnection $connection, int $page, int $perPage): array { return Http::baseUrl('https://www.strava.com/api/v3') ->withToken($this->accessToken($connection)) ->timeout(10) ->connectTimeout(3) ->retry([100, 500, 1000]) ->get('/athlete/activities', [ 'page' => $page, 'per_page' => $perPage, ]) ->throw() ->json(); } public function activity(StravaConnection $connection, int|string $activityId): array { return Http::baseUrl('https://www.strava.com/api/v3') ->withToken($this->accessToken($connection)) ->timeout(10) ->connectTimeout(3) ->retry([100, 500, 1000]) ->get("/activities/{$activityId}", [ 'include_all_efforts' => false, ]) ->throw() ->json(); } private function accessToken(StravaConnection $connection): string { if (! $connection->expires_at || $connection->expires_at->lte(now()->addMinutes(5))) { $connection = $this->refresh($connection); } return $connection->access_token; } private function tokenRequest(): PendingRequest { return Http::baseUrl('https://www.strava.com') ->asForm() ->timeout(10) ->connectTimeout(3) ->retry([100, 500, 1000]); } private function redirectUri(): string { return rtrim((string) config('app.url'), '/').'/strava/callback'; } }