Lunar, l'e-commerce Laravel qui n'a plus rien à envier à Shopify
Quand on lance un projet e-commerce, le réflexe reste souvent le même : Shopify pour aller vite, WooCommerce parce que c'est gratuit et qu'on connaît WordPress, ou une stack e-commerce headless coûteuse pour les plus ambitieux. Peu de développeurs Laravel pensent spontanément à construire leur boutique directement dans leur framework favori.
Avant d'aller plus loin, voici justement à quoi ressemble une boutique Laravel construite avec Lunar une fois en action :
C'est précisément le problème que Lunar résout.
Lunar est un package Laravel qui apporte tout ce qu'on attend d'une plateforme e-commerce moderne (catalogue, panier, tarification, commandes, taxes, remises, paiements, recherche, back-office) sans jamais quitter l'écosystème Laravel.
Pas d'API externe à appeler, pas de plateforme tierce à louer, pas de commission prélevée sur chaque vente. Le code vous appartient, la base de données vous appartient, et le storefront reste entièrement le vôtre.
Pour illustrer concrètement ce que ça donne, on va s'appuyer à la fois sur la documentation officielle et sur une vraie application en cours de construction, celle de la série que je publie actuellement sur laraveljutsu.com.
Elle n'est pas terminée (loin de là), mais elle est déjà largement assez avancée pour montrer pourquoi ce choix technique tient la route.
Un package, pas une application à part
La première chose qui frappe avec Lunar, c'est qu'il ne cherche pas à imposer sa propre architecture. Un Product reste un modèle Eloquent comme un autre :
1class Product extends \Lunar\Models\Product2{3 protected function smallImageUrl(): Attribute4 {5 return Attribute::get(fn () => $this->getFirstMediaUrl('images', 'small'));6 }7}
Et pour que Lunar utilise cette version étendue plutôt que la sienne, il suffit de le déclarer dans le service provider, sans toucher à une seule ligne du package :
1ModelManifest::replace(ProductContract::class, Product::class);
Même logique côté utilisateurs. N'importe quel modèle User devient un client Lunar à part entière en ajoutant simplement une interface et un trait :
1class User extends Authenticatable implements LunarUserInterface2{3 use HasFactory, LunarUser, Notifiable, TwoFactorAuthenticatable;4}
Pas de modèle imposé, pas de fork à maintenir. On étend par contrat, comme n'importe quel bon package Laravel devrait le faire. Ce n'est d'ailleurs pas propre aux produits ou aux utilisateurs : commandes, paniers, remises, tous les modèles centraux de Lunar suivent le même principe de remplacement par contrat.
Le catalogue, avec de vraies variantes
Les plateformes SaaS gèrent bien les variantes de produits (taille, couleur, etc.), mais elles le font dans une boîte noire. Avec Lunar, ce mécanisme est directement accessible en PHP.
Pour générer toutes les combinaisons possibles d'options d'un produit, on peut par exemple s'appuyer sur Lunar\Utils\Arr::permutate(), qui calcule le produit cartésien de plusieurs jeux de valeurs :
1$combinations = Arr::permutate($setTuples); // Taille x Couleur, toutes les combinaisons 2 3foreach ($combinations as $combo) { 4 $variant = ProductVariant::create([ 5 'product_id' => $product->id, 6 'tax_class_id' => $taxClass->id, 7 'sku' => implode('-', $combo), 8 ]); 9 10 $variant->values()->attach($combo);11}
C'est exactement le mécanisme utilisé en interne par l'admin de Lunar pour générer les variantes. Rien n'est caché, tout est réutilisable. Le catalogue ne s'arrête d'ailleurs pas aux variantes : collections, marques, canaux de vente et système d'attributs flexible font partie du même socle, prêts à être combinés sans dépendance externe.
Un pricing qui s'adapte tout seul au client
C'est sans doute l'exemple le plus parlant. Beaucoup de projets e-commerce maison finissent avec des if en cascade pour gérer des grilles tarifaires par type de client (grossiste, particulier, revendeur). Lunar résout ça nativement avec le concept de groupe client, documenté et utilisable directement via une façade :
1$pricing = \Lunar\Facades\Pricing::customerGroup($group)->for($variant)->get();
Sur la fiche produit de l'application, cette même mécanique se traduit ainsi :
1$product->variants->each(function ($variant) {2 try {3 $variant->resolved_price = $variant->pricing()->get()->matched;4 } catch (\Throwable) {5 $variant->resolved_price = null;6 }7});
pricing()->get()->matched va chercher, tout seul, le bon tarif en fonction du groupe client du visiteur connecté, de la devise active et des remises en cours.
Le même appel expose aussi priceBreaks, les paliers de prix par quantité, et customerGroupPrices, l'ensemble des tarifs spécifiques à chaque groupe : de quoi construire un vrai tarif dégressif B2B sans écrire le moindre calcul soi-même.
Cette cohérence se retrouve jusque dans le panier, où le prix de chaque ligne provient directement du calcul déjà effectué par Lunar, taxes comprises.
Remises, taxes et multi-devises déjà prêts à l'emploi
Un système de remises "achetez 2, le 3e offert" se déclare en quelques lignes, sans construire soi-même un moteur de règles :
1$discount = Discount::create([ 2 'name' => 'Buy 2 Get 1 Free', 3 'handle' => 'buy_2_get_1', 4 'type' => 'Lunar\DiscountTypes\BuyXGetY', 5 'data' => [ 6 'min_qty' => 2, 7 'reward_qty' => 1, 8 'max_reward_qty' => 5, 9 ],10 'starts_at' => now(),11]);
Ces conditions et récompenses acceptent aussi bien des produits que des variantes ou des collections entières, ce qui couvre la plupart des opérations commerciales sans code sur mesure.
Même logique côté fiscalité et devises. Créer une zone de taxation avec son propre taux ne demande aucun service tiers :
1$taxZone = TaxZone::create([ 2 'name' => 'Germany', 3 'zone_type' => 'country', 4 'price_display' => 'tax_inclusive', 5 'default' => true, 6]); 7 8$taxZone->taxRates()->create(['name' => 'MwSt', 'priority' => 1]) 9 ->taxRateAmounts()->create([10 'tax_class_id' => $defaultTaxClass->id,11 'percentage' => 19.000,12 ]);
Vendre en plusieurs devises ou sur plusieurs canaux (site B2C, marketplace, point de vente) fait partie du même socle, chaque canal pouvant avoir ses propres règles d'activation et de disponibilité.
Panier et checkout, sans réinventer la roue
Ajouter un produit au panier ? Une ligne suffit :
1public function store(Request $request, ProductVariant $variant)2{3 $cart = CartSession::current();4 $cart->add($variant, $request->quantity);5 6 return redirect()->back();7}
Modifier une quantité ou retirer une ligne suit exactement la même logique, avec $cart->updateLine() et $cart->remove(). Côté frontend, ces routes sont exposées via Wayfinder, ce qui permet de les appeler depuis Vue avec des fonctions typées plutôt que des URLs en dur.
Même simplicité pour le checkout. Enregistrer les adresses de facturation et de livraison d'une commande tient en deux appels :
1$cart->setBillingAddress($request->validated('billing'));2 3$cart->setShippingAddress(4 $request->boolean('ship_to_billing')5 ? $request->validated('billing')6 : $request->validated('shipping')7);
Lunar gère la persistance, la relation avec la commande, et expose ensuite $cart->isShippable() pour adapter la validation selon que la commande nécessite ou non une livraison physique. Ce genre de détail, anodin en apparence, représente des heures de travail en moins sur un projet e-commerce classique.
Paiement et recherche, déjà branchés
Lunar fournit des drivers de paiement de première partie pour Stripe, PayPal et Opayo, avec une interface extensible pour en ajouter d'autres.
Côté sécurité, aucune donnée de carte sensible ne transite par votre base de données : seuls les quatre derniers chiffres, le type de carte et les références de transaction sont conservés, le reste étant délégué au prestataire de paiement. La conformité PCI n'est donc plus un sujet à gérer soi-même.
Côté recherche, un simple composer require lunarphp/search suffit à brancher Laravel Scout avec une indexation préconfigurée, que ce soit avec la base de données pour démarrer, ou avec Meilisearch, Typesense et Algolia pour un moteur de production.
Un back-office livré clé en main
C'est peut-être l'argument le plus sous-estimé de Lunar. Son panel d'administration est construit sur Filament, et il s'active avec une seule ligne dans un service provider :
1public function register(): void2{3 LunarPanel::register();4}
Cette unique ligne donne accès à un back-office complet : gestion des produits et de leurs variantes, commandes avec leur cycle de vie, clients et groupes clients, remises, taxes, devises, canaux de vente.
Sur le projet de la série laraveljutsu.com, aucune ressource Filament personnalisée n'a été écrite pour obtenir tout ça : tout vient directement du package.
Et le jour où un besoin spécifique apparaît, chaque resource reste extensible plutôt que remplaçable, par exemple pour ajouter une colonne personnalisée à la fiche produit :
1class MyProductResourceExtension extends ResourceExtension 2{ 3 public function extendTable(Table $table): Table 4 { 5 return $table->columns([ 6 ...$table->getColumns(), 7 TextColumn::make('product_code'), 8 ]); 9 }10}
Pourquoi pas WooCommerce, pourquoi pas Shopify
WooCommerce reste séduisant par son coût d'entrée nul, mais il traîne avec lui toute la dette technique de WordPress : plugins tiers de qualité inégale, mises à jour parfois hasardeuses, et une architecture pensée pour du contenu avant d'être pensée pour du commerce. Dès que les besoins deviennent un peu spécifiques, on se retrouve à empiler des extensions plutôt qu'à écrire du code propre.
Shopify, de son côté, fait très bien son travail tant qu'on reste dans les cases prévues par la plateforme. Mais la personnalisation du storefront a un plafond, l'hébergement des données vous échappe totalement, et chaque vente rogne une commission. Pour un développeur Laravel habitué à contrôler chaque couche de son application, cette dépendance à une plateforme fermée finit par peser.
Avec Lunar, on garde tout ce qu'on aime de Laravel (Eloquent, Inertia, les tests Pest, les queues, les policies) tout en récupérant la logique métier e-commerce déjà écrite et éprouvée : catalogue, pricing, remises, taxes, paiements, recherche et back-office. Aucune commission, aucun vendor lock-in, et une personnalisation du storefront qui n'a d'autre limite que celle de Laravel lui-même.
Conclusion
Lunar ne cherche pas à remplacer Shopify ou WooCommerce sur leur propre terrain, celui du SaaS clé en main pour non-développeurs.
Il vise un public différent : les équipes Laravel qui veulent une boutique aussi flexible que leur application, sans sacrifier la maîtrise de leur code ni payer une commission sur chaque commande.
Et à en juger par la rapidité avec laquelle un catalogue, un pricing dynamique, des remises, un panier et un back-office prennent forme, ce public a de bonnes raisons de s'y intéresser.
Source : https://lunarphp.com/
Écrit par
Ludovic Guénetsoftware engineer • mentor • bassist
Tu veux commenter ? Crée un compte ou connecte-toi.
A lire
Autres articles de la même catégorie
Découverte de NativePHP
Transformez votre Laravel en application desktop !
Tester les performances avec phpbench
Surveillez les performances de votre code grâce aux tests de benchmark !
La validation d'emails
Décortiquons ensemble la validation des adresses email avec Laravel !