netgescon-day0/app/Services/Catalog/AmazonCreatorsApiService.php

154 lines
5.5 KiB
PHP

<?php
namespace App\Services\Catalog;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use RuntimeException;
class AmazonCreatorsApiService
{
public function isConfigured(): bool
{
return (bool) config('services.amazon.creators_enabled', false)
&& $this->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<string, mixed> */
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<int, string> $identifiers
* @return array<string, mixed>
*/
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<string, mixed> */
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;
}
}