credentialId() !== '' && $this->credentialSecret() !== '' && $this->tokenUrl() !== '' && $this->apiBaseUrl() !== ''; } public function getMarketplace(): string { return trim((string) config('services.amazon.marketplace', 'www.amazon.it')) ?: 'www.amazon.it'; } public function getAssociateTag(): ?string { $tag = trim((string) config('services.amazon.referral_tag', '')); return $tag !== '' ? $tag : null; } /** @return array */ public function searchItems(string $keywords, array $parameters = []): array { $keywords = trim($keywords); if ($keywords === '') { throw new RuntimeException('Keywords Amazon vuote.'); } return $this->requestOperation('searchItems', array_filter(array_replace([ 'keywords' => $keywords, 'marketplace' => $this->getMarketplace(), 'partnerTag' => $this->getAssociateTag(), ], $parameters), static fn(mixed $value): bool => $value !== null && $value !== '')); } /** @param array $identifiers * @return array */ public function getItems(array $identifiers, array $parameters = []): array { $identifiers = array_values(array_filter(array_map( static fn(mixed $value): string => trim((string) $value), $identifiers ), static fn(string $value): bool => $value !== '')); if ($identifiers === []) { throw new RuntimeException('Specificare almeno un ASIN Amazon.'); } return $this->requestOperation('getItems', array_filter(array_replace([ 'itemIds' => $identifiers, 'marketplace' => $this->getMarketplace(), 'partnerTag' => $this->getAssociateTag(), ], $parameters), static fn(mixed $value): bool => $value !== null && $value !== '')); } private function credentialId(): string { return trim((string) config('services.amazon.creators_credential_id', '')); } private function credentialSecret(): string { return trim((string) config('services.amazon.creators_credential_secret', '')); } private function tokenUrl(): string { return trim((string) config('services.amazon.creators_token_url', '')); } private function apiBaseUrl(): string { return rtrim(trim((string) config('services.amazon.creators_api_base_url', '')), '/'); } /** @return array */ private function requestOperation(string $operation, array $payload): array { if (! $this->isConfigured()) { throw new RuntimeException('Amazon Creators API non configurata: valorizza le variabili AMAZON_CREATORS_* in ambiente.'); } $path = trim((string) Arr::get(config('services.amazon.creators_paths', []), $operation, ''), '/'); if ($path === '') { throw new RuntimeException('Percorso Creators API mancante per l\'operazione ' . $operation . '.'); } $response = Http::timeout(max(5, (int) config('services.amazon.creators_timeout', 20))) ->acceptJson() ->asJson() ->withToken($this->getAccessToken()) ->post($this->apiBaseUrl() . '/' . $path, $payload); if (! $response->successful()) { throw new RuntimeException('Amazon Creators API ' . $operation . ' ha risposto con HTTP ' . $response->status() . ': ' . substr(trim($response->body()), 0, 400)); } $json = $response->json(); if (! is_array($json)) { throw new RuntimeException('Risposta Creators API non valida per ' . $operation . '.'); } return $json; } private function getAccessToken(): string { $cacheKey = 'amazon-creators-api-token:' . sha1($this->credentialId() . '|' . $this->tokenUrl()); $cached = Cache::get($cacheKey); if (is_string($cached) && trim($cached) !== '') { return $cached; } $response = Http::timeout(max(5, (int) config('services.amazon.creators_timeout', 20))) ->asForm() ->post($this->tokenUrl(), [ 'grant_type' => 'client_credentials', 'client_id' => $this->credentialId(), 'client_secret' => $this->credentialSecret(), 'version' => trim((string) config('services.amazon.creators_credential_version', 'v3.2')) ?: 'v3.2', ]); if (! $response->successful()) { throw new RuntimeException('Token Creators API non ottenuto: HTTP ' . $response->status() . ' ' . substr(trim($response->body()), 0, 300)); } $json = $response->json(); if (! is_array($json)) { throw new RuntimeException('Token Creators API non valido.'); } $token = trim((string) ($json['access_token'] ?? $json['token'] ?? '')); if ($token === '') { throw new RuntimeException('Risposta token Creators API senza access_token.'); } $expiresIn = max(60, (int) ($json['expires_in'] ?? 3600)); Cache::put($cacheKey, $token, now()->addSeconds(max(60, $expiresIn - 60))); return $token; } }