# Prix et abonnements — référence exacte

Ce document décrit **uniquement** le comportement actuel du code (chemins, formules, endpoints). Aucune phrase générique : chaque affirmation correspond à une implémentation précise.

---

## 1. Où sont stockés les données tarifaires

| Rôle | Emplacement | Contenu |
|------|-------------|---------|
| Surcharges (prix modifiables par l’admin) | Fichier **`data/pricing_overrides.json`** à la racine du dépôt backend (répertoire de travail du processus Python) | Pour chaque clé `starter`, `pro`, `business` : `price_monthly`, `price_yearly` (recalculé par le serveur), `stripe_price_id_monthly`, `stripe_price_id_yearly` (optionnel, chaînes vides possibles). |
| Variables d’environnement | Fichier **`.env`** à la racine du backend | Clés `STRIPE_SECRET_KEY`, `STRIPE_PUBLISHABLE_KEY`, `STRIPE_WEBHOOK_SECRET`, et `STRIPE_PRICE_STARTER_MONTHLY`, `STRIPE_PRICE_STARTER_YEARLY`, idem pour `PRO` et `BUSINESS`. L’admin peut mettre à jour ce fichier lors d’une sauvegarde. |
| Valeurs par défaut du code | **`models/subscription.py`**, dictionnaire **`PLANS`** | Utilisé si une valeur n’existe pas dans `pricing_overrides.json` pour le **prix mensuel** de chaque plan. Les quotas (documents/mois, pages, etc.) et les textes de fonctionnalités viennent **toujours** de `PLANS`, pas du JSON d’override. |

---

## 2. Qui peut modifier les prix

- **Interface admin** : page **Pricing & Stripe** (`frontend/src/app/admin/pricing/page.tsx`).
- **API** : uniquement avec un **jeton administrateur** valide.
  - **Lecture** : `GET /api/v1/admin/pricing`
  - **Écriture** : `PUT /api/v1/admin/pricing`
  - **Création automatique Stripe** : `POST /api/v1/admin/pricing/setup-stripe`

Les utilisateurs non admin **ne** peuvent **pas** modifier les tarifs. Ils ne voient que la liste publique (voir section 5).

---

## 3. Règle de calcul du prix annuel (pas de choix libre)

Le serveur impose une seule règle :

```
prix_annuel = arrondi(prix_mensuel × 12 × 0,8 ; 2 décimales)
```

- **Constante dans le code** : `YEARLY_DISCOUNT_FACTOR = 0.8` dans `services/pricing_config.py`.
- **Interprétation** : le total annuel payé d’un coup = **80 %** de ce que coûteraient **12 mois** au tarif mensuel. C’est **équivalent** à une réduction de **20 %** sur ce total « 12 × mensuel ».
- **Constante affichée** : `ANNUAL_DISCOUNT_PERCENT = 20` (même valeur, autre formulation).

**Conséquence** : tu ne définis pas un annuel arbitraire. Tu définis le **mensuel** ; le serveur **recalcule** l’annuel et l’enregistre dans `pricing_overrides.json` lors d’un `PUT`. Un champ `price_yearly` envoyé par un client est **ignoré** pour la logique métier (le serveur normalise).

**Plage autorisée pour le prix mensuel** (forfaits payants `starter` / `pro` / `business`) : **entre 0,01 € et 50 000 €** (validation dans `validate_monthly_price_eur`).

---

## 4. Ordre de résolution pour les prix affichés et le paiement

### 4.1 Montants en euros (mensuel / annuel)

Pour un plan `starter`, `pro` ou `business` :

1. Lire `price_monthly` dans **`data/pricing_overrides.json`** pour ce plan.
2. Si absent, utiliser `price_monthly` dans **`PLANS`** (`models/subscription.py`).
3. Calculer `price_yearly` avec la formule de la section 3.

Pour les plans **gratuit** (`free`) et **entreprise** (`enterprise`), les montants spéciaux (0 ou « sur devis ») viennent de **`PLANS`** ; **pas** de même logique de remise annuelle.

### 4.2 Identifiants Stripe Price (`price_…`)

Pour savoir quel Stripe Price utiliser au checkout :

1. **D’abord** : champs `stripe_price_id_monthly` and `stripe_price_id_yearly` dans **`data/pricing_overrides.json`** pour le plan.
2. **Sinon** : variables d’environnement `STRIPE_PRICE_<PLAN>_MONTHLY` et `STRIPE_PRICE_<PLAN>_YEARLY` (ex. `STRIPE_PRICE_STARTER_MONTHLY`), après **rechargement** du fichier `.env` en mémoire pour le processus (voir `reload_dotenv_from_dotenv_file` dans `services/pricing_config.py`).

Il n’y a **plus** de repli sur les `os.getenv` figés dans le dictionnaire `PLANS` au moment de l’import du module pour ces identifiants.

### 4.3 Comportement du checkout Stripe (`services/payment_service.py`)

1. Si un **identifiant** `price_…` valide est trouvé pour la période **mensuelle** ou **annuelle** : la session Checkout utilise **ce** Stripe Price (montant défini **dans Stripe**).
2. Sinon : création d’une ligne avec **`price_data`** (montant en centimes) calculé à partir des **mêmes** règles euros que l’affichage (`get_subscription_line_amount_eur`), **sans** redémarrage du serveur tant que `pricing_overrides.json` est à jour.

---

## 5. Ce que voit le site public (sans être admin)

- **Endpoint HTTP** : `GET /api/v1/auth/plans` (sans authentification).
- **Réponse** : liste des plans avec `price_monthly`, `price_yearly`, `annual_discount_percent`, et les limites (documents, pages, etc.) issues de **`PLANS`**.
- **Métadonnées** : `meta.annual_discount_percent` (20) et `meta.yearly_discount_factor` (0,8).

La page **`/pricing`** du frontend charge cet endpoint et remplace les données statiques de secours si la réponse est valide.

---

## 6. Packs de crédits (hors admin)

Les **prix des packs de crédits** (nombre de crédits, prix en euros) sont définis dans **`models/subscription.py`**, structure **`CREDIT_PACKAGES`**. Ils **ne** sont **pas** modifiables via l’écran **Pricing & Stripe** actuel. Ils apparaissent dans `GET /api/v1/auth/plans` sous `credit_packages`.

---

## 7. Bouton « Créer automatiquement » (Stripe)

- **Endpoint** : `POST /api/v1/admin/pricing/setup-stripe`.
- **Condition** : `STRIPE_SECRET_KEY` présente dans l’environnement et commençant par `sk_`.
- **Effet** : le backend crée ou retrouve des produits Stripe et des **prix mensuels et annuels** en **euros** dont les montants **en centimes** sont calculés à partir des **mêmes** règles euros que ci-dessus (mensuel effectif + annuel = `mensuel × 12 × 0,8`). Les identifiants `price_…` sont enregistrés dans **`.env`** et dans **`data/pricing_overrides.json`**.

---

## 8. Redémarrage du serveur

- **Pas requis** pour que les **montants** (JSON) soient pris en compte : le fichier est lu à chaque requête qui appelle `load_pricing_overrides()`.
- Après modification du **`.env`** par l’admin, le code appelle **`apply_runtime_config_after_admin_write()`** pour recharger les variables dans le processus courant.

En cas de **plusieurs processus** (plusieurs workers), chaque processus recharge les variables lorsqu’il en a besoin ou après une écriture admin sur le même worker ; le fichier **`data/pricing_overrides.json`** est partagé sur le disque.

---

## 9. Résumé opérationnel (ce que tu fais concrètement)

1. Te connecter en **admin**.
2. Ouvrir **Pricing & Stripe**.
3. Saisir le **prix mensuel** pour Starter, Pro, Business (l’annuel affiché est calculé ; il n’est pas saisi librement).
4. Optionnel : renseigner les **Price ID** Stripe ou utiliser **Créer automatiquement** si la clé secrète Stripe est configurée.
5. Cliquer sur **Sauvegarder la configuration**.

Les changements sont persistés dans **`data/pricing_overrides.json`** et, le cas échéant, dans **`.env`**. Les pages publiques qui consomment **`GET /api/v1/auth/plans`** reflètent les nouveaux montants sans redémarrage obligatoire du serveur.
