$clientId, 'redirect_uri' => $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 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(); } 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]); } }