---
# System prepended metadata

title: "\U0001F6CD️ Luxe Shop — Documentation technique complète"

---

# 🛍️ Luxe Shop — Documentation technique complète

> **E-commerce fullstack** : Laravel 12 (API) + React 19 (SPA) + Stripe Checkout
> Projet réalisé en février 2026

---

## 📑 Table des matières

1. [Présentation du projet](#-présentation-du-projet)
2. [Stack technique](#-stack-technique)
3. [Architecture du projet](#-architecture-du-projet)
4. [Prérequis](#-prérequis)
5. [Installation Backend (Laravel)](#-installation-backend-laravel)
6. [Installation Frontend (React)](#-installation-frontend-react)
7. [Configuration de la base de données](#-configuration-de-la-base-de-données)
8. [Migrations — Structure des tables](#-migrations--structure-des-tables)
9. [Modèles Eloquent et relations](#-modèles-eloquent-et-relations)
10. [Seeder — Données de démonstration](#-seeder--données-de-démonstration)
11. [Authentification avec Sanctum](#-authentification-avec-sanctum)
12. [Routes API](#-routes-api)
13. [Controllers Backend](#-controllers-backend)
14. [Intégration Stripe Checkout](#-intégration-stripe-checkout)
15. [Frontend React — Structure](#-frontend-react--structure)
16. [Service API (Axios)](#-service-api-axios)
17. [Routage React](#-routage-react)
18. [Pages Frontend](#-pages-frontend)
19. [Composants réutilisables](#-composants-réutilisables)
20. [Gestion des images produits](#-gestion-des-images-produits)
21. [Styles et thème](#-styles-et-thème)
22. [Lancer le projet](#-lancer-le-projet)
23. [Résumé des fonctionnalités](#-résumé-des-fonctionnalités)

---

## 🎯 Présentation du projet

**Luxe Shop** est une boutique en ligne complète avec :
- Catalogue de **30 produits** répartis en **9 catégories**
- Système d'**inscription / connexion** sécurisé par token
- **Panier** persistant (côté serveur)
- **Paiement en ligne** via Stripe Checkout
- **Historique des commandes** avec statut de paiement
- **Dashboard** utilisateur
- Interface **responsive** avec thème sombre luxueux

---

## 🧰 Stack technique

| Couche | Technologie | Version |
|--------|------------|---------|
| **Backend** | Laravel (PHP) | 12.x |
| **Authentification** | Laravel Sanctum | 4.3 |
| **Base de données** | MySQL (via XAMPP) | 8.x |
| **Paiement** | Stripe PHP SDK | 19.3 |
| **Frontend** | React | 19.x |
| **Routing SPA** | React Router DOM | 7.x |
| **HTTP Client** | Axios | 1.x |
| **Serveur local** | XAMPP (Apache + MySQL) | — |

---

## 🏗️ Architecture du projet

```
Laravel 3/
├── backend/          ← API Laravel (port 8000)
│   ├── app/
│   │   ├── Http/Controllers/Api/
│   │   │   ├── AuthController.php
│   │   │   ├── CartController.php
│   │   │   ├── CategoryController.php
│   │   │   ├── OrderController.php
│   │   │   └── ProductController.php
│   │   ├── Models/
│   │   │   ├── User.php
│   │   │   ├── Product.php
│   │   │   ├── Category.php
│   │   │   ├── CartItem.php
│   │   │   ├── Order.php
│   │   │   └── OrderItem.php
│   │   └── Providers/
│   ├── config/
│   │   └── sanctum.php
│   ├── database/
│   │   ├── migrations/
│   │   └── seeders/
│   │       └── ShopSeeder.php
│   └── routes/
│       └── api.php
│
└── frontend/         ← SPA React (port 3000)
    └── src/
        ├── components/
        │   ├── Navbar.js
        │   └── PrivateRoute.js
        ├── pages/
        │   ├── Home.js
        │   ├── Login.js / Register.js
        │   ├── Products.js / ProductDetail.js
        │   ├── Cart.js
        │   ├── Orders.js / OrderDetail.js
        │   ├── Dashboard.js
        │   ├── Success.js / Cancel.js
        │   └── ...
        ├── services/
        │   └── api.js
        ├── utils/
        │   └── productUtils.js
        └── index.css
```

---

## ✅ Prérequis

- **XAMPP** installé (Apache + MySQL démarrés)
- **PHP** 8.2+
- **Composer** installé globalement
- **Node.js** 18+ et **npm**
- **Compte Stripe** (clé secrète pour le paiement)

---

## ⚙️ Installation Backend (Laravel)

### 1. Créer le projet Laravel

```bash
cd C:\xampp\htdocs
composer create-project laravel/laravel backend
cd backend
```

### 2. Installer les dépendances supplémentaires

```bash
composer require laravel/sanctum
composer require stripe/stripe-php
```

### 3. Configurer le fichier `.env`

```env
APP_NAME="Luxe Shop"
APP_URL=http://127.0.0.1:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ecommerce_db
DB_USERNAME=root
DB_PASSWORD=

SANCTUM_STATEFUL_DOMAINS=localhost:3000,127.0.0.1:3000

STRIPE_SECRET=sk_test_VOTRE_CLE_STRIPE_SECRETE
FRONTEND_URL=http://localhost:3000
```

### 4. Configurer CORS

Dans `config/cors.php` (ou middleware), s'assurer que `localhost:3000` est autorisé :

```php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_origins' => ['http://localhost:3000'],
'supports_credentials' => true,
```

---

## ⚙️ Installation Frontend (React)

### 1. Créer l'application React

```bash
cd C:\xampp\htdocs\Laravel 3
npx create-react-app frontend
cd frontend
```

### 2. Installer les dépendances

```bash
npm install axios react-router-dom
```

---

## 🗄️ Configuration de la base de données

### 1. Créer la base dans phpMyAdmin

1. Ouvrir [http://localhost/phpmyadmin](http://localhost/phpmyadmin)
2. Créer une nouvelle base de données : `ecommerce_db`
3. Encodage : `utf8mb4_unicode_ci`

### 2. Configurer `.env` (déjà fait ci-dessus)

### 3. Lancer les migrations

```bash
cd backend
php artisan migrate
```

---

## 📐 Migrations — Structure des tables

### `categories`
```php
Schema::create('categories', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->timestamps();
});
```

### `products`
```php
Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->text('description');
    $table->decimal('price', 8, 2);
    $table->string('image')->nullable();
    $table->foreignId('category_id')->constrained()->onDelete('cascade');
    $table->timestamps();
});
```

### `cart_items`
```php
Schema::create('cart_items', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->foreignId('product_id')->constrained()->onDelete('cascade');
    $table->integer('quantity')->default(1);
    $table->timestamps();
});
```

### `orders`
```php
Schema::create('orders', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->decimal('total', 10, 2);
    $table->string('status')->default('pending');
    $table->timestamps();
});
```

### `order_items`
```php
Schema::create('order_items', function (Blueprint $table) {
    $table->id();
    $table->foreignId('order_id')->constrained()->onDelete('cascade');
    $table->foreignId('product_id')->constrained()->onDelete('cascade');
    $table->integer('quantity');
    $table->decimal('price', 8, 2);
    $table->timestamps();
});
```

### Migration ajout Stripe (sur `orders`)
```php
Schema::table('orders', function (Blueprint $table) {
    $table->string('stripe_session_id')->nullable()->after('status');
    $table->timestamp('paid_at')->nullable()->after('stripe_session_id');
});
```

### Diagramme des relations

```
┌──────────┐     ┌────────────┐     ┌──────────┐
│  users   │────<│ cart_items  │>────│ products │
│          │     └────────────┘     │          │
│          │                        │          │>───┐
│          │     ┌────────────┐     └──────────┘    │
│          │────<│  orders    │                      │
│          │     │            │     ┌─────────────┐  │
└──────────┘     │            │────<│ order_items  │>┘
                 └────────────┘     └─────────────┘
                                    
                 ┌────────────┐
                 │ categories │>────── products
                 └────────────┘
```

---

## 🧱 Modèles Eloquent et relations

### `User.php`

```php
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $fillable = ['name', 'email', 'password'];
    protected $hidden = ['password', 'remember_token'];

    public function cartItems()
    {
        return $this->hasMany(CartItem::class);
    }

    public function orders()
    {
        return $this->hasMany(Order::class);
    }
}
```

> ⚠️ **Important** : Le trait `HasApiTokens` est indispensable pour que Sanctum fonctionne.

### `Product.php`
```php
protected $fillable = ['name', 'description', 'price', 'image', 'category_id'];

public function category()
{
    return $this->belongsTo(Category::class);
}
```

### `Category.php`
```php
protected $fillable = ['name'];

public function products()
{
    return $this->hasMany(Product::class);
}
```

### `CartItem.php`
```php
protected $fillable = ['user_id', 'product_id', 'quantity'];

public function product()
{
    return $this->belongsTo(Product::class);
}
```

### `Order.php`
```php
protected $fillable = ['user_id', 'total', 'status'];

public function items()
{
    return $this->hasMany(OrderItem::class);
}

public function user()
{
    return $this->belongsTo(User::class);
}
```

### `OrderItem.php`
```php
protected $fillable = ['order_id', 'product_id', 'quantity', 'price'];

public function product()
{
    return $this->belongsTo(Product::class);
}

public function order()
{
    return $this->belongsTo(Order::class);
}
```

---

## 🌱 Seeder — Données de démonstration

Le fichier `ShopSeeder.php` injecte **9 catégories** et **30 produits** avec images.

```bash
php artisan db:seed --class=ShopSeeder
```

**Catégories :**
| # | Catégorie |
|---|-----------|
| 1 | Informatique |
| 2 | Gaming |
| 3 | Audio |
| 4 | Smartphone |
| 5 | Maison |
| 6 | Cuisine |
| 7 | Sport |
| 8 | Bureau |
| 9 | Accessoires |

**Extrait produits (30 au total) :**
| Produit | Prix | Catégorie |
|---------|------|-----------|
| PC Portable 15" | 699.90 CHF | Informatique |
| Clavier mécanique | 79.90 CHF | Gaming |
| Écouteurs sans fil | 39.90 CHF | Audio |
| Chargeur rapide 30W | 19.90 CHF | Smartphone |
| Lampe de bureau | 19.90 CHF | Maison |
| Blender | 49.90 CHF | Cuisine |
| Tapis de yoga | 24.90 CHF | Sport |
| Chaise ergonomique | 129.90 CHF | Bureau |
| Sac à dos tech | 39.90 CHF | Accessoires |

Chaque produit possède une URL d'image externe (Unsplash ou liens directs).

---

## 🔐 Authentification avec Sanctum

### Principe

Laravel Sanctum gère l'authentification par **tokens API (Bearer Token)** :

1. L'utilisateur s'inscrit ou se connecte → reçoit un **token**
2. Le token est stocké dans `localStorage` côté React
3. Chaque requête API protégée envoie le header `Authorization: Bearer <token>`
4. Le middleware `auth:sanctum` vérifie le token côté Laravel

### Configuration `config/sanctum.php`

```php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS',
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
)),
```

### Flux d'authentification

```
┌─────────┐   POST /api/register    ┌─────────┐
│  React  │ ──────────────────────> │ Laravel │
│ (front) │ <────────────────────── │  (API)  │
│         │   { token, user }       │         │
│         │                         │         │
│         │   GET /api/cart          │         │
│         │   Authorization: Bearer │         │
│         │ ──────────────────────> │         │
│         │ <────────────────────── │         │
└─────────┘   [cart items]          └─────────┘
```

---

## 🛣️ Routes API

### Routes publiques

| Méthode | URI | Description |
|---------|-----|-------------|
| `POST` | `/api/register` | Inscription |
| `POST` | `/api/login` | Connexion |
| `GET` | `/api/categories` | Liste des catégories |
| `GET` | `/api/products` | Liste des produits |
| `GET` | `/api/products/{id}` | Détail d'un produit |

### Routes protégées (`auth:sanctum`)

| Méthode | URI | Description |
|---------|-----|-------------|
| `GET` | `/api/user` | Infos utilisateur connecté |
| `POST` | `/api/logout` | Déconnexion |
| `GET` | `/api/cart` | Afficher le panier |
| `POST` | `/api/cart/add` | Ajouter au panier |
| `DELETE` | `/api/cart/{id}` | Retirer du panier |
| `POST` | `/api/checkout` | Créer commande + Stripe |
| `GET` | `/api/orders` | Historique commandes |
| `GET` | `/api/orders/{id}` | Détail d'une commande |
| `POST` | `/api/payment/confirm` | Confirmer paiement Stripe |

---

## 🎮 Controllers Backend

### `AuthController`

| Action | Description |
|--------|-------------|
| `register()` | Valide les données, crée le user, retourne un token Sanctum |
| `login()` | Vérifie email/password avec `Hash::check`, retourne un token |
| `logout()` | Supprime le token courant |

```php
// Inscription
$user = User::create([...]);
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['token' => $token, 'user' => $user], 201);
```

### `ProductController`

```php
// Liste tous les produits avec leur catégorie
public function index()
{
    return Product::with('category')->get();
}

// Détail d'un produit
public function show($id)
{
    return Product::with('category')->findOrFail($id);
}
```

### `CategoryController`

```php
public function index()
{
    return Category::all();
}
```

### `CartController`

| Action | Description |
|--------|-------------|
| `index()` | Récupère les articles du panier de l'utilisateur connecté |
| `add()` | Ajoute un produit au panier (ou incrémente la quantité) |
| `remove()` | Supprime un article du panier |

```php
// Ajout intelligent : firstOrCreate + increment
$item = CartItem::firstOrCreate(
    ['user_id' => $user->id, 'product_id' => $request->product_id],
    ['quantity' => 0]
);
$item->increment('quantity');
```

### `OrderController`

| Action | Description |
|--------|-------------|
| `checkout()` | Crée la commande + session Stripe Checkout |
| `index()` | Liste les commandes de l'utilisateur |
| `show()` | Détail d'une commande |
| `confirmPayment()` | Vérifie le paiement via l'API Stripe |

---

## 💳 Intégration Stripe Checkout

### Principe

1. L'utilisateur clique **"Payer avec Stripe"** dans le panier
2. Le backend crée une **session Stripe Checkout** avec les articles
3. L'utilisateur est **redirigé vers la page Stripe** pour payer
4. Après paiement, redirection vers `/success?session_id=...`
5. Le frontend appelle `/api/payment/confirm` pour valider

### Code serveur (dans `OrderController@checkout`)

```php
\Stripe\Stripe::setApiKey(env('STRIPE_SECRET'));

$session = \Stripe\Checkout\Session::create([
    'mode' => 'payment',
    'line_items' => $lineItems,  // produits du panier
    'success_url' => $frontend . '/success?session_id={CHECKOUT_SESSION_ID}',
    'cancel_url'  => $frontend . '/cancel',
    'client_reference_id' => (string) $order->id,
    'metadata' => [
        'user_id'  => (string) $user->id,
        'order_id' => (string) $order->id,
    ],
]);
```

### Confirmation de paiement

```php
// POST /api/payment/confirm
$session = \Stripe\Checkout\Session::retrieve($request->session_id);

if ($session->payment_status === 'paid') {
    $order->status = 'paid';
    $order->paid_at = now();
    $order->save();
}
```

### Flux complet

```
Panier → POST /checkout → Stripe Checkout → /success → POST /payment/confirm
                                    ↓
                               Paiement annulé → /cancel
```

---

## ⚛️ Frontend React — Structure

### `src/services/api.js` — Client HTTP

```javascript
import axios from "axios";

const api = axios.create({
  baseURL: "http://127.0.0.1:8000/api",
});

// Intercepteur : injecte le token Bearer automatiquement
api.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

export default api;
```

> Le token est stocké dans `localStorage` et envoyé automatiquement sur chaque requête.

---

## 🗺️ Routage React

Fichier : `App.js`

```javascript
<BrowserRouter>
  <Navbar />
  <Routes>
    {/* Publiques */}
    <Route path="/" element={<Home />} />
    <Route path="/login" element={<Login />} />
    <Route path="/register" element={<Register />} />
    <Route path="/products" element={<Products />} />
    <Route path="/products/:id" element={<ProductDetail />} />

    {/* Stripe */}
    <Route path="/success" element={<PrivateRoute><Success /></PrivateRoute>} />
    <Route path="/cancel" element={<Cancel />} />

    {/* Protégées */}
    <Route path="/cart" element={<PrivateRoute><Cart /></PrivateRoute>} />
    <Route path="/orders" element={<PrivateRoute><Orders /></PrivateRoute>} />
    <Route path="/orders/:id" element={<PrivateRoute><OrderDetail /></PrivateRoute>} />
    <Route path="/dashboard" element={<PrivateRoute><Dashboard /></PrivateRoute>} />

    {/* Fallback */}
    <Route path="*" element={<Navigate to="/" replace />} />
  </Routes>
</BrowserRouter>
```

---

## 📄 Pages Frontend

| Page | Route | Description |
|------|-------|-------------|
| **Home** | `/` | Page d'accueil avec hero et présentation |
| **Login** | `/login` | Formulaire de connexion |
| **Register** | `/register` | Formulaire d'inscription |
| **Products** | `/products` | Catalogue avec filtres par catégorie + recherche |
| **ProductDetail** | `/products/:id` | Fiche produit avec image, bouton panier |
| **Cart** | `/cart` | Panier avec résumé + bouton Stripe |
| **Orders** | `/orders` | Historique des commandes |
| **OrderDetail** | `/orders/:id` | Détail d'une commande |
| **Dashboard** | `/dashboard` | Tableau de bord utilisateur |
| **Success** | `/success` | Confirmation de paiement réussi |
| **Cancel** | `/cancel` | Paiement annulé |

---

## 🧩 Composants réutilisables

### `Navbar.js`

- Logo **Luxe Shop**
- Navigation : Produits, Panier, Commandes, Dashboard
- Bouton **Connexion** / **Déconnexion** selon l'état

### `PrivateRoute.js`

```javascript
// Redirige vers /login si pas de token
const token = localStorage.getItem("token");
if (!token) return <Navigate to="/login" />;
return children;
```

### `productUtils.js`

Deux fonctions utilitaires :
- `getProductEmoji(category, name)` → retourne un emoji selon la catégorie
- `getCategoryColor(category)` → retourne une couleur de fond RGBA

---

## 🖼️ Gestion des images produits

### Côté backend (Seeder)

Chaque produit a un champ `image` contenant une **URL directe** vers une image :

```php
['PC Portable 15"', 'Laptop polyvalent.', 699.90, 'Informatique',
 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=800&h=800&fit=crop'],
```

Sources d'images utilisées :
- **Unsplash** (images gratuites haute qualité)
- **Liens directs** de sites marchands (pour certains produits spécifiques)

### Côté frontend

Les images sont affichées avec un **fallback emoji** si l'image ne charge pas :

```jsx
{product.image ? (
  <img
    src={product.image}
    alt={product.name}
    style={{ objectFit: "contain", background: "#1a1a2e" }}
    onError={(e) => {
      e.target.style.display = 'none';
      e.target.nextSibling.style.display = 'flex';
    }}
  />
) : null}

{/* Fallback emoji */}
<div className="product-img-placeholder"
     style={{ display: product.image ? 'none' : 'flex' }}>
  <span>{getProductEmoji(category, name)}</span>
</div>
```

**Pages concernées :**
- `Products.js` — carte produit (grille)
- `ProductDetail.js` — page détail
- `Cart.js` — miniature dans le panier

---

## 🎨 Styles et thème

Le thème est défini dans `index.css` avec des **variables CSS** :

```css
:root {
  --bg: #0a0a0f;
  --surface: #12121a;
  --surface2: #1a1a2e;
  --border: #2a2a3e;
  --text: #f5f5f5;
  --muted: #888;
  --gold: #c9a84c;
  --gold2: #e8c547;
  --green: #22c55e;
  /* ... */
}
```

### Caractéristiques du design
- **Thème sombre** (dark mode exclusif)
- Accents **dorés** pour le côté luxe
- Typographie : **Playfair Display** (titres) + **Inter** (corps)
- Border-radius arrondis (16px / 24px)
- Grille responsive 4 colonnes → 2 → 1

---

## 🚀 Lancer le projet

### Terminal 1 — Backend Laravel

```bash
cd C:\xampp\htdocs\Laravel 3\backend
php artisan serve
```
> API disponible sur `http://127.0.0.1:8000`

### Terminal 2 — Frontend React

```bash
cd C:\xampp\htdocs\Laravel 3\frontend
npm start
```
> App disponible sur `http://localhost:3000`

### Seeder (première fois ou reset)

```bash
cd backend
php artisan migrate:fresh
php artisan db:seed --class=ShopSeeder
```

---

## ✨ Résumé des fonctionnalités

| Fonctionnalité | Status |
|----------------|--------|
| Inscription / Connexion (Sanctum) | ✅ |
| Catalogue produits avec images | ✅ |
| Filtres par catégorie | ✅ |
| Recherche de produits | ✅ |
| Fiche produit détaillée | ✅ |
| Panier (ajout / suppression) | ✅ |
| Paiement Stripe Checkout | ✅ |
| Confirmation de paiement | ✅ |
| Historique des commandes | ✅ |
| Dashboard utilisateur | ✅ |
| Responsive design | ✅ |
| Thème sombre luxueux | ✅ |
| Fallback images (emoji) | ✅ |
| 30 produits / 9 catégories | ✅ |

---

> **Luxe Shop** — Projet fullstack Laravel + React avec paiement Stripe
> Réalisé en février 2026
