true, 'languages' => true, 'thumbs' => [ 'quality' => 85, 'format' => 'webp', 'presets' => [ 'product-card' => [ 'width' => 600, 'height' => 600, 'crop' => true, 'format' => 'webp' ], 'product-detail' => [ 'width' => 1200, 'format' => 'webp' ], 'default' => [ 'width' => 1024, 'format' => 'webp' ], ], 'srcsets' => [ 'default' => [400, 600, 800, 1024, 1440, 2048], 'webp' => [ '400w' => ['width' => 400, 'format' => 'webp'], '600w' => ['width' => 600, 'format' => 'webp'], '800w' => ['width' => 800, 'format' => 'webp'], '1024w' => ['width' => 1024, 'format' => 'webp'], '1440w' => ['width' => 1440, 'format' => 'webp'], ], ], ], 'routes' => [ // SNIPCART ROUTES - Désactivées, voir assets/snipcart-archive/README.md pour restauration /* [ 'pattern' => '(:any)/validate.json', 'method' => 'GET', 'action' => function ($slug) { $page = page($slug); if (!$page || $page->intendedTemplate() !== 'product') { header('Content-Type: application/json'); http_response_code(404); echo json_encode(['error' => 'Product not found']); return; } // Récupérer le stock actuel $stock = (int) $page->stock()->value(); // Préparer la réponse JSON pour Snipcart $response = [ 'id' => $page->slug(), 'price' => (float) $page->price()->value(), 'url' => $page->url() . '/validate.json', 'name' => $page->title()->value(), 'description' => $page->description()->value(), 'image' => $page->images()->first() ? $page->images()->first()->url() : '', 'inventory' => $stock, 'stock' => $stock ]; // Ajouter les options si disponibles if ($page->hasOptions()->toBool() && $page->optionValues()->isNotEmpty()) { $values = $page->optionValues()->split(','); $trimmedValues = array_map('trim', $values); $snipcartOptions = implode('|', $trimmedValues); $response['customFields'] = [ [ 'name' => $page->optionLabel()->value(), 'options' => $snipcartOptions, 'required' => true ] ]; } header('Content-Type: application/json'); echo json_encode($response); } ], [ 'pattern' => 'snipcart-webhook', 'method' => 'POST', 'action' => function () { // Webhook handler pour Snipcart // Vérifie la signature et décrémente le stock $requestBody = file_get_contents('php://input'); $event = json_decode($requestBody, true); // Vérifier la signature Snipcart (à implémenter avec la clé secrète) // $signature = $_SERVER['HTTP_X_SNIPCART_REQUESTTOKEN'] ?? ''; if (!$event || !isset($event['eventName'])) { return Response::json(['error' => 'Invalid request'], 400); } // Gérer l'événement order.completed if ($event['eventName'] === 'order.completed') { $order = $event['content'] ?? null; if ($order && isset($order['items'])) { // Impersonate pour avoir les permissions d'écriture kirby()->impersonate('kirby'); foreach ($order['items'] as $item) { $productId = $item['id'] ?? null; $quantity = $item['quantity'] ?? 0; if ($productId && $quantity > 0) { // Trouver le produit par son slug $products = site()->index()->filterBy('intendedTemplate', 'product'); foreach ($products as $product) { if ($product->slug() === $productId) { // Décrémenter le stock $currentStock = (int) $product->stock()->value(); $newStock = max(0, $currentStock - $quantity); // Mettre à jour le stock try { $product->update([ 'stock' => $newStock ]); } catch (Exception $e) { // Log l'erreur mais continue le traitement error_log('Webhook stock update error: ' . $e->getMessage()); } break; } } } } } } return Response::json(['status' => 'success'], 200); } ] */ ] ];