baseUrl = rtrim((string) config('wb_api.base_url'), '/'); $this->apiKey = (string) config('wb_api.key'); $this->limit = max(1, min((int) config('wb_api.limit', 100), 500)); $this->timeout = (int) config('wb_api.timeout', 30); $this->requestSleepMs = (int) config('wb_api.request_sleep_ms', 1000); $this->tooManyAttemptsSleepSeconds = (int) config('wb_api.too_many_attempts_sleep_seconds', 30); $this->max429Retries = (int) config('wb_api.max_429_retries', 20); if ($this->baseUrl === '') { throw new RuntimeException('WB_API_BASE_URL is empty. Check your .env file.'); } if ($this->apiKey === '') { throw new RuntimeException('WB_API_KEY is empty. Check your .env file.'); } } public function limit(): int { return $this->limit; } public function fetchPage(string $endpoint, array $params, int $page): array { $url = $this->baseUrl . '/api/' . ltrim($endpoint, '/'); $query = array_filter(array_merge($params, [ 'key' => $this->apiKey, 'limit' => $this->limit, 'page' => $page, ]), static fn ($value) => $value !== null && $value !== ''); $attempt429 = 0; while (true) { if ($this->requestSleepMs > 0) { usleep($this->requestSleepMs * 1000); } $response = Http::timeout($this->timeout) ->acceptJson() ->get($url, $query); if ($response->status() === 429) { $attempt429++; if ($attempt429 > $this->max429Retries) { throw new RuntimeException("Too many 429 responses for {$endpoint} page {$page}"); } $retryAfter = $response->header('Retry-After'); $sleepSeconds = is_numeric($retryAfter) ? max(1, (int) $retryAfter) : $this->tooManyAttemptsSleepSeconds; echo "{$endpoint} page={$page} got 429, sleeping {$sleepSeconds}s, retry {$attempt429}/{$this->max429Retries}\n"; sleep($sleepSeconds); continue; } if (!$response->successful()) { throw new RuntimeException(sprintf( 'API request failed: endpoint=%s page=%d http=%d body=%s', $endpoint, $page, $response->status(), mb_substr($response->body(), 0, 1000) )); } $json = $response->json(); if (!is_array($json)) { throw new RuntimeException("Unexpected non-array API response for {$endpoint}, page {$page}"); } $items = $json['data'] ?? $json; if (!is_array($items)) { throw new RuntimeException("Unexpected API response shape for {$endpoint}, page {$page}"); } if ($items !== [] && array_keys($items) !== range(0, count($items) - 1)) { $items = [$items]; } return array_values(array_filter($items, 'is_array')); } } }