Files
Malek TellissiandClaude Sonnet 4.6 7e9c43f2bf
Deploy / deploy (push) Successful in 14s
Improve Shopify sync error logging
Log clear hint for 403 (IP whitelist), 401, 429, 5xx.
Commands exit with failure and print error when API returns no data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-11 02:54:44 +02:00

86 lines
2.7 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class ShopifyService
{
private string $baseUrl;
private string $token;
public function __construct()
{
$domain = config('services.shopify.domain');
$version = config('services.shopify.api_version');
$this->baseUrl = "https://{$domain}/admin/api/{$version}";
$this->token = config('services.shopify.access_token');
}
/**
* Returns all product variants from Shopify.
* Each entry: ['id', 'sku', 'inventory_quantity', 'product_title']
*/
public function getAllVariants(): array
{
$variants = [];
$url = "{$this->baseUrl}/products.json?limit=250&fields=id,title,variants";
while ($url) {
$response = Http::withHeader('X-Shopify-Access-Token', $this->token)->get($url);
if ($response->failed()) {
$status = $response->status();
$hint = match (true) {
$status === 401 => 'Access Token ungültig oder fehlt',
$status === 403 => 'IP-Whitelist: VPS-IP ist in Shopify nicht freigeschalten',
$status === 429 => 'Rate Limit überschritten',
$status >= 500 => 'Shopify-seitiger Fehler',
default => 'Unbekannter Fehler',
};
Log::error("Shopify Sync fehlgeschlagen: {$hint}", [
'status' => $status,
'url' => $url,
'body' => $response->body(),
]);
break;
}
foreach ($response->json('products', []) as $product) {
foreach ($product['variants'] as $variant) {
$variants[] = [
'id' => (string) $variant['id'],
'sku' => $variant['sku'] ?? '',
'inventory_quantity' => (int) ($variant['inventory_quantity'] ?? 0),
'product_title' => $product['title'],
];
}
}
// Follow Shopify's Link header pagination
$url = $this->nextPageUrl($response->header('Link'));
if ($url) {
usleep(500_000); // 0.5s — stay well within 2 req/s limit
}
}
return $variants;
}
private function nextPageUrl(?string $linkHeader): ?string
{
if (!$linkHeader) {
return null;
}
// Link: <https://...?page_info=abc>; rel="next"
if (preg_match('/<([^>]+)>;\s*rel="next"/', $linkHeader, $m)) {
return $m[1];
}
return null;
}
}