# Serveur de licence – Comment ça fonctionne

## Vue d'ensemble

Le **serveur de licence** (SERVEURDELICENCE) crée des licences **à la demande** quand un client paie via Stripe. Pour les **CMS installés chez des tiers**, le paiement doit être **centralisé** : seul ce serveur détient `sk_live` et crée les sessions Checkout (voir ci‑dessous).

### Mode production distribuée (CMS)

Sur les archives officielles, le marketplace est en **remote** par défaut (`config/gcms.php`). La clé API n’est **pas** dans le `.env` client : fichier **`bootstrap/gcms-infrastructure.key`** (une ligne, même secret que `LICENSE_API_KEY` sur le serveur de licence). Les contributeurs utilisent `GCMS_DEV_INFRASTRUCTURE=true` et les variables documentées en fin de `.env.example`.

### Mode recommandé : paiement remote (CMS)

```
┌─────────────┐  POST checkout   ┌──────────────┐   Stripe API    ┌─────────────┐
│ CMS client  │ ───────────────► │ Serveur      │ ─────────────► │ Stripe      │
│ (pas de sk) │                  │ de licence   │                │ Checkout    │
└─────────────┘                  └──────────────┘                └─────────────┘
       ▲                                 │                              │
       │  redirect success_url            │  webhook checkout.session.*  │
       │  (session_id=…)                  ▼                              │
       │                          Même compte Stripe (vous)               │
       │  GET/POST from-session ──► vérif session + licence               │
```

### Mode historique : `MARKETPLACE_PAYMENT_DRIVER=local` (CMS)

Chaque instance CMS appelle Stripe avec ses propres `MARKETPLACE_STRIPE_*` — un hébergeur peut substituer ses clés. À réserver au dev ou à une seule instance que vous contrôlez.

```
┌─────────────┐     Acheter     ┌─────────────┐     Paiement     ┌──────────────┐
│   CMS       │ ──────────────► │   Stripe     │ ──────────────► │  Serveur     │
│ (boutique)  │                 │  Checkout   │                 │  de licence  │
└─────────────┘                 └─────────────┘                 └──────────────┘
       │                                │                                │
       │  "Donne-moi la clé pour       │                                │
       │   cette session payée"        │                                │
       │ ──────────────────────────────────────────────────────────────►│
       │                                │                                │
       │  license_key: "ABC123..."      │                                │
       │ ◄──────────────────────────────────────────────────────────────│
       │                                │                                │
       │  Enregistre la clé dans marketplace_purchases                   │
```

## API à implémenter sur le serveur : création de session Checkout

Lorsque le CMS est en **remote**, il appelle (Authorization Bearer = clé dans `bootstrap/gcms-infrastructure.key` ou `MARKETPLACE_LICENSE_API_KEY` en dev) :

`POST {LICENSE_URL}/api/marketplace/create-checkout-session`  
(URL production par défaut : `https://gcmslicence.gdevstudio.fr` — voir `config/gcms.php` du CMS.)

Corps JSON attendu (envoyé par le CMS) :

| Champ | Description |
|--------|-------------|
| `item_type` | `theme` ou `plugin` |
| `item_slug` | ex. `section-plus` |
| `success_url` | URL absolue ; doit contenir le placeholder Stripe `{CHECKOUT_SESSION_ID}` là où le CMS l’a mis |
| `cancel_url` | URL absolue (retour boutique) |
| `app_url` | URL de base du site client (`APP_URL`), **sans** slash final |

**Sécurité (obligatoire côté serveur)** :

1. Vérifier le Bearer (même clé que `LICENSE_API_KEY` / `from-session`).
2. **Ne pas** faire confiance à un prix envoyé par le client : résoudre **prix et libellé** depuis **votre** catalogue (miroir de `config/marketplace_catalog.php` du CMS, ou liste blanche slug → montant).
3. Vérifier que `success_url` et `cancel_url` sont en `https` (sauf dev / hôtes locaux) et que leur **hôte** est identique à celui de `app_url` (empêche d’envoyer le client vers un domaine tiers). Si le CMS est encore en **HTTP** sur un domaine public (staging), tu peux assouplir côté serveur avec **`LICENSE_ALLOW_HTTP_CALLBACKS=true`** dans le `.env` du SERVEURDELICENCE puis `php artisan config:cache` — à **éviter en production** ouverte.

**Session Stripe** : mêmes métadonnées que le CMS en local :

- `metadata.marketplace` = `1`
- `metadata.item_type`, `metadata.item_slug`

Réponse JSON en cas de succès : `{ "url": "https://checkout.stripe.com/...", "session_id": "cs_..." }` (`session_id` optionnel mais utile pour le debug).

**Webhook Stripe** : en mode remote, enregistrez l’URL webhook sur le **serveur de licence** uniquement (pas sur chaque instance CMS). Traitez `checkout.session.completed` comme aujourd’hui pour créer la licence côté serveur ; le CMS finalise via `from-session` après redirection.

## Où sont définis les plugins ?

Dans le **CMS** : `config/marketplace_catalog.php` (affichage boutique).

Sur le **serveur de licence**, pour le mode **remote**, vous devez disposer d’une **source de vérité** alignée sur ce catalogue (slug, prix EUR, libellé) pour créer la ligne Stripe. Soit un fichier de config dupliqué, soit une liste blanche en base.

```php
'plugins' => [
    ['slug' => 'section-plus', 'name' => 'Section +', 'price' => 29, ...],
    ['slug' => 'ecommerce', 'name' => 'E-commerce', 'price' => 99, ...],
    // ...
],
```

> **Liste blanche** : n’acceptez que les slugs que vous vendez réellement, avec les prix officiels.

## Flux en production

### Remote (recommandé pour GCMS distribué)

1. **Client clique « Acheter »** → le CMS appelle `create-checkout-session` sur votre serveur.
2. **Serveur** crée la session Stripe (votre compte) et renvoie l’URL Checkout.
3. **Client paie** → Stripe redirige vers la **page de succès du CMS** avec `session_id`.
4. **Page succès** appelle `from-session` : le serveur vérifie la session Stripe et renvoie la `license_key`.
5. **CMS** enregistre dans `marketplace_purchases`.

### Local (legacy)

1. **Client clique « Acheter »** sur le CMS → le CMS crée la session avec `MARKETPLACE_STRIPE_SECRET`.
2. La suite est identique (redirection Stripe → succès → `from-session`).

## Configuration requise

### Sur le serveur de licence (SERVEURDELICENCE)

Fichier `.env` :

```env
LICENSE_API_KEY=xxx          # Même valeur que le contenu de bootstrap/gcms-infrastructure.key sur le CMS distribué
LICENSE_STRIPE_SECRET=sk_xxx # Compte Stripe sur lequel vous encaissez (GdevStudio)
LICENSE_STRIPE_WEBHOOK_SECRET=whsec_xxx   # Webhook sur ce serveur (optionnel mais recommandé)
# Staging uniquement : CMS sans SSL — autorise success/cancel en http
# LICENSE_ALLOW_HTTP_CALLBACKS=true
```

En **remote**, le CMS client **n’a pas** besoin de `LICENSE_STRIPE_SECRET` ni des clés marketplace Stripe.

Le dépôt **SERVEURDELICENCE** expose l’endpoint `POST /api/marketplace/create-checkout-session` et le catalogue `config/marketplace_catalog.php` (à maintenir aligné avec le CMS).

### Sur le CMS (instances clients)

- **`APP_URL`** : obligatoire (déjà le cas), utilisé pour valider les URLs de retour côté serveur de licence.
- **`bootstrap/gcms-infrastructure.key`** : une ligne = `LICENSE_API_KEY` (fichier ajouté dans les ZIP officiels par GdevStudio, non versionné dans le dépôt public).

Pas de variables marketplace / mises à jour GdevStudio dans le `.env` des clients (`config/gcms.php` fige les URL).

### Sur le CMS (développement du cœur GCMS)

```env
GCMS_DEV_INFRASTRUCTURE=true
MARKETPLACE_LICENSE_URL=http://127.0.0.1:8001
MARKETPLACE_LICENSE_API_KEY=xxx
MARKETPLACE_PAYMENT_DRIVER=remote
APP_URL=http://127.0.0.1:8000
```

Optionnel : `MARKETPLACE_REMOTE_CHECKOUT_URL` si l’URL de création de session diffère de `{LICENSE_URL}/api/marketplace/create-checkout-session`.

La validation des licences auprès du serveur est **toujours activée** pour les plugins (aucune variable pour la désactiver).

## Créer des licences de test (sans Stripe)

**Sécurité :** l’endpoint HTTP `POST /api/licenses/create-test` utilise la **même** clé que le CMS (`LICENSE_API_KEY` / fichier infrastructure). S’il restait ouvert en production, **n’importe quelle copie du CMS** pourrait s’octroyer des licences pour n’importe quel domaine. Sur le serveur de licence public, **`LICENSE_ALLOW_API_CREATE_TEST` doit rester `false`** (défaut) : désactive l’API et impose `php artisan license:create-test` en SSH. Tu peux mettre `true` uniquement sur un serveur de licence **local / staging** dédié.

Pour tester sans passer par un vrai paiement :

### Méthode simple (depuis le CMS)

Uniquement si le serveur de licence a **`LICENSE_ALLOW_API_CREATE_TEST=true`** (ex. dev local).

```bash
cd /chemin/vers/gcms
php artisan license:add-test section-plus
```

Crée une licence sur le serveur, met à jour l'achat et active le plugin. Pour un autre domaine : `--domain=monsite.fr`

### Méthode manuelle

**1. Sur le serveur de licence**

```bash
cd SERVEURDELICENCE
php artisan license:create-test section-plus localhost
```

Cela crée une licence et affiche la clé (ex. `DEMO-A1B2C3D4`).

### 2. Sur le CMS

Mettre à jour l’achat dans la base (ou via php artisan tinker) :

```bash
php artisan tinker
>>> $p = App\Models\MarketplacePurchase::where('item_slug', 'section-plus')->first();
>>> $p->update(['license_key' => 'DEMO-A1B2C3D4', 'is_active' => true]);
>>> exit
```

Puis vider le cache :

```bash
php artisan license:test --clear-cache
```

## Déploiement sur un sous-domaine (ex. `https://gcmslicence.gdevstudio.fr`)

### 1. DNS

Chez ton registrar / Cloudflare : enregistrement **A** (ou **AAAA**) pour `gcmslicence` → **IP publique** du VPS (même serveur que l’app Laravel).

### 2. Nginx (Debian / Ubuntu)

Exemple de site (`/etc/nginx/sites-available/gcmslicence.gdevstudio.fr`) ; adapte `root` et le socket PHP-FPM (`php8.2-fpm.sock`, `php8.3-fpm.sock`, etc.) :

```nginx
server {
    listen 80;
    listen [::]:80;
    server_name gcmslicence.gdevstudio.fr;
    root /var/www/serveur-licence/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_hide_header X-Powered-By;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
```

Activer le site et recharger :

```bash
sudo ln -sf /etc/nginx/sites-available/gcmslicence.gdevstudio.fr /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```

> **Erreur « HTTP 404 » depuis le CMS** : la cause la plus fréquente est un **`root` Nginx sur le mauvais dossier**. Il doit impérativement pointer vers **`…/serveur-licence/public`**, pas vers la racine du projet Laravel. Sinon `/api/…` ne passe pas par `index.php` et Nginx renvoie 404.

**Test rapide** (après déploiement du dernier code) : ouvre dans le navigateur  
`https://gcmslicence.gdevstudio.fr/api/health`  
Tu dois voir du JSON `{"ok":true,"service":"gcms-license-server",…}`. Si tu as encore une 404 → Nginx / `root` / mauvais vhost. Si ça répond en 200 mais le checkout reste en 404 en POST → surtout **`php artisan route:clear`** puis **`php artisan route:cache`** : avec rsync qui **exclut** `bootstrap/cache/`, le fichier de cache des routes reste **ancien** et Laravel ne connaît pas `POST /api/marketplace/...` → 404. Voir aussi `SERVEURDELICENCE/DEPLOY.md` § « Après chaque rsync ».

### 3. HTTPS (Let’s Encrypt)

```bash
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d gcmslicence.gdevstudio.fr
```

Certbot ajoute `listen 443 ssl` et renouvelle automatiquement.

### 4. `.env` du serveur de licence

```env
APP_NAME="GCMS Licence"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://gcmslicence.gdevstudio.fr

# … base de données, APP_KEY, etc. (voir .env.example du projet)

LICENSE_API_KEY=…
LICENSE_STRIPE_SECRET=sk_live_…
LICENSE_STRIPE_WEBHOOK_SECRET=whsec_…
```

Puis sur le serveur :

```bash
cd /var/www/serveur-licence
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
```

**Droits (obligatoire — sinon 500 « Permission denied » sur les logs, sessions SQLite en lecture seule) :**

PHP-FPM écrit sous l’utilisateur **`www-data`**. Après chaque `rsync` en tant que `gcms-deploy`, réapplique :

```bash
sudo chown -R www-data:www-data /var/www/serveur-licence/storage /var/www/serveur-licence/bootstrap/cache
sudo find /var/www/serveur-licence/storage -type d -exec chmod 775 {} \;
sudo find /var/www/serveur-licence/storage -type f -exec chmod 664 {} \;
sudo chmod -R 775 /var/www/serveur-licence/bootstrap/cache
```

Si tu utilises **SQLite** en prod (`database/database.sqlite`) :

```bash
sudo chown www-data:www-data /var/www/serveur-licence/database/database.sqlite
sudo chmod 664 /var/www/serveur-licence/database/database.sqlite
sudo chown www-data:www-data /var/www/serveur-licence/database
```

**Recommandé en production** : passer le serveur de licence en **MySQL** / MariaDB (`.env` `DB_CONNECTION=mysql`, etc.) pour éviter les soucis de droits sur le fichier SQLite.

**Déploiement par rsync** : soit tu lances les `chown` ci-dessus après chaque sync, soit tu ajoutes `gcms-deploy` au groupe `www-data` et tu utilises `chmod g+w` sur `storage`, `bootstrap/cache` et `database/` — le plus simple reste **`chown www-data` sur les dossiers que PHP doit écrire**.

### 5. Stripe — webhook

Dans le dashboard Stripe : **Developers → Webhooks → Add endpoint**

- **URL** : `https://gcmslicence.gdevstudio.fr/api/webhook/stripe`
- **Événements** : au minimum ceux que tu traiteras (ex. `checkout.session.completed`, remboursements si implémentés).

Copie le **signing secret** dans `LICENSE_STRIPE_WEBHOOK_SECRET`.

### 6. Endpoints utilisés par le CMS

| Usage | URL |
|--------|-----|
| Validation licence | `POST https://gcmslicence.gdevstudio.fr/api/validate-license` |
| Licence après paiement | `POST https://gcmslicence.gdevstudio.fr/api/licenses/from-session` |
| Création session Checkout marketplace | `POST https://gcmslicence.gdevstudio.fr/api/marketplace/create-checkout-session` |
| Santé (diagnostic) | `GET https://gcmslicence.gdevstudio.fr/api/health` |

Le CMS en production utilise déjà la base `https://gcmslicence.gdevstudio.fr` via `config/gcms.php` (sans `GCMS_DEV_INFRASTRUCTURE`).

### 7. CMS local pointant vers ce sous-domaine

Dans le `.env` du CMS (dev uniquement) :

```env
GCMS_DEV_INFRASTRUCTURE=true
MARKETPLACE_LICENSE_URL=https://gcmslicence.gdevstudio.fr
MARKETPLACE_LICENSE_API_KEY=même_valeur_que_LICENSE_API_KEY
```

---

## Commandes utiles

| Où | Commande | Description |
|----|----------|-------------|
| Serveur | `php artisan license:list` | Lister les licences |
| Serveur | `php artisan license:create-test {plugin} {domain}` | Créer une licence de test |
| Serveur | `php artisan license:revoke {clé}` | Révoquer une licence |
| CMS | `php artisan license:test` | Tester la connexion et la validation |
| CMS | `php artisan license:test --clear-cache` | Vider le cache des licences |
| CMS | `php artisan license:add-test {plugin} [--domain=]` | Créer une licence de test et l'appliquer |
