### Prompt d’intégration Flutter – Gestion des abonnements (Backend Sails prêt)

- Base URL: `https://<votre-domaine>` (remplacez)
- Auth: Toutes les routes ci-dessous nécessitent `Authorization: Bearer <JWT>` (le backend lit `codeEcole` et `codeUser` depuis le token)
- En-têtes: `Content-Type: application/json`, `Accept: application/json`

### 1) Lister les offres d’abonnement
- Méthode/Route: GET `/api/offre-abonnement/find`
- Query params (optionnels): 
  - `typeAbonnement` ∈ [gratuit, mensuel, trimestriel, semestriel, annuel, essai]
  - `actif` (bool), `montantMin` (number), `montantMax` (number), `limit` (number), `skip` (number), `sort` (string)
- Réponse 200 (exemple):
```json
{
  "message": "Offres d'abonnement trouvées avec succès",
  "data": {
    "offres": [
      {
        "codeOffre": "OFFRE_...",
        "nom": "Abonnement Mensuel",
        "description": "Accès complet 30 jours",
        "typeAbonnement": "mensuel",
        "dureeJours": 30,
        "montant": 700,
        "devise": "XOF",
        "fonctionnalites": ["Suivi complet", "Notifications"],
        "codeEcole": "ECL001",
        "actif": true
      }
    ],
    "pagination": { "total": 3, "limit": 10, "skip": 0, "page": 1, "totalPages": 1 }
  }
}
```

### 2) Valider un code promotionnel
- Méthode/Route: POST `/api/promotion-abonnement/validate`
- Body requis:
  - `codePromotion` (string)
  - `codeOffre` (string)
  - `montantOriginal` (number)
  - `codeParent` (string) = identifiant parent (`user.codeUser` du JWT)
- Réponse 200 (exemple):
```json
{
  "message": "Code promotionnel validé avec succès",
  "data": {
    "promotion": {
      "codePromotion": "PROMO_...",
      "typeReduction": "pourcentage",
      "valeurReduction": 10,
      "devise": "XOF"
    },
    "calcul": {
      "montantOriginal": 700,
      "montantReduction": 70,
      "montantFinal": 630,
      "pourcentageReduction": 10
    },
    "validite": {
      "dateDebut": "2025-01-01T00:00:00.000Z",
      "dateFin": "2025-12-31T23:59:59.000Z",
      "utilisationsRestantes": 998,
      "utilisationsParParentRestantes": 1
    }
  }
}
```
- Erreurs possibles: 404 PROMOTION_NOT_FOUND, 400 PROMOTION_EXPIRED, PROMOTION_EXHAUSTED, PROMOTION_ALREADY_USED, INCOMPATIBLE_OFFER, INSUFFICIENT_AMOUNT

### 3) Créer un abonnement
- Méthode/Route: POST `/api/abonnement/create`
- Body requis:
  - `codeOffre` (string)
- Body optionnels:
  - `codePromotion` (string)
  - `methodePaiement` ∈ [mobile_money, carte_bancaire, virement, especes, gratuit]
  - `renouvellementAuto` (bool)
  - `commentaires` (string)
- Note: `codeParent` et `codeEcole` sont pris du JWT côté backend.
- Réponse 200:
```json
{
  "message": "Abonnement créé avec succès",
  "data": {
    "codeAbonnement": "ABO_...",
    "codeParent": "PAR001",
    "codeEcole": "ECL001",
    "codeOffre": "OFFRE_...",
    "typeAbonnement": "mensuel",
    "statut": "en_attente_paiement|actif|essai",
    "dateDebut": "2025-01-01T10:30:00.000Z",
    "dateFin": "2025-01-31T10:30:00.000Z",
    "montant": 700,
    "montantReduction": 70,
    "montantFinal": 630,
    "devise": "XOF",
    "renouvellementAuto": false
  }
}
```
- Erreurs: 404 PARENT_NOT_FOUND, OFFRE_NOT_FOUND, 409 SUBSCRIPTION_ALREADY_EXISTS, 400 INVALID_PROMOTION

### 4) Payer un abonnement
- Méthode/Route: POST `/api/abonnement/process-payment`
- Body requis:
  - `codeAbonnement` (string)
  - `methodePaiement` ∈ [mobile_money, carte_bancaire, virement, especes, gratuit]
  - `montant` (number)
  - `devise` (string)
- Body optionnels:
  - `referencePaiement` (string), `operateur` (string), `numeroTelephone` (string), `fraisTransaction` (number), `description` (string), `donneesTechniques` (json)
- Réponse 200:
```json
{
  "message": "Paiement traité avec succès",
  "data": {
    "transaction": {
      "codeTransaction": "TXN_...",
      "typeTransaction": "paiement",
      "statut": "reussi",
      "montant": 630,
      "devise": "XOF",
      "methodePaiement": "mobile_money",
      "referencePaiement": "PMT-123",
      "dateTransaction": "2025-01-01T10:31:00.000Z"
    },
    "abonnement": {
      "codeAbonnement": "ABO_...",
      "statut": "actif",
      "offre": {
        "codeOffre": "OFFRE_...",
        "nom": "Abonnement Mensuel",
        "typeAbonnement": "mensuel"
      }
    }
  }
}
```
- Erreurs: 404 SUBSCRIPTION_NOT_FOUND, 400 INVALID_STATUS_FOR_PAYMENT, INCORRECT_AMOUNT, 409 SUBSCRIPTION_ALREADY_PAID

### 5) Vérifier le statut d’un abonnement
- Méthode/Route: GET `/api/abonnement/check-status`
- Query params:
  - `codeAbonnement` (string, requis)
- Réponse 200:
```json
{
  "message": "Statut vérifié avec succès",
  "data": {
    "abonnement": {
      "codeAbonnement": "ABO_...",
      "statut": "actif|expire|essai|suspendu",
      "dateDebut": "...",
      "dateFin": "...",
      "offre": { "codeOffre": "OFFRE_...", "nom": "Abonnement Mensuel" }
    },
    "statut": {
      "actuel": "actif",
      "misAJour": false,
      "joursRestants": 12,
      "expire": false,
      "expirationProche": false
    },
    "notifications": null
  }
}
```
- Erreurs: 404 SUBSCRIPTION_NOT_FOUND

### 6) Lister les abonnements de l’utilisateur (ou de l’école)
- Méthode/Route: GET `/api/abonnement/find`
- Query params (optionnels):
  - `statut` ∈ [actif, expire, suspendu, annule, en_attente_paiement, essai]
  - `typeAbonnement` ∈ [gratuit, mensuel, trimestriel, semestriel, annuel, essai]
  - `dateDebut` (YYYY-MM-DD), `dateFin` (YYYY-MM-DD)
  - `limit` (number, défaut 10), `skip` (number), `sort` (string, ex: `dateDebut DESC`)
- Note: côté backend, si l’utilisateur est PARENT, la recherche est automatiquement filtrée sur son `codeUser` et son `codeEcole` (via le JWT).
- Réponse 200:
```json
{
  "message": "Abonnements trouvés avec succès",
  "data": {
    "abonnements": [
      {
        "codeAbonnement": "ABO_...",
        "statut": "actif",
        "dateDebut": "...",
        "dateFin": "...",
        "offre": { "codeOffre": "OFFRE_...", "nom": "Abonnement Mensuel" },
        "parent": { "codeParent": "PAR001", "nom": "Kouassi", "prenom": "Marie" },
        "ecole": { "codeEcole": "ECL001", "nom": "Lycée ABC" }
      }
    ],
    "pagination": { "total": 1, "limit": 10, "skip": 0, "page": 1, "totalPages": 1 }
  }
}
```

### 7) (Admin) Mettre à jour un abonnement
- Méthode/Route: PUT `/api/abonnement/update`
- Body:
  - `codeAbonnement` (string, requis)
  - Optionnels: `statut`, `dateFin` (YYYY-MM-DD), `renouvellementAuto` (bool), `methodePaiement`, `referencePaiement`, `datePaiement` (YYYY-MM-DD), `commentaires`
- Réponse 200:
```json
{
  "message": "Abonnement mis à jour avec succès",
  "data": {
    "codeAbonnement": "ABO_...",
    "statut": "suspendu",
    "offre": { "codeOffre": "OFFRE_...", "nom": "Abonnement Mensuel" }
  }
}
```

### Codes d’erreurs standard (exemples)
- 400: `{"message":"<texte>", "code":"<CODE>"}`
- 404: `{"message":"<texte>", "code":"<CODE>"}`
- 409: `{"message":"<texte>", "code":"<CODE>"}`

### Conseils d’implémentation Flutter
- Créez des DTO: `OffreDto`, `AbonnementDto`, `TransactionDto`, `PromotionValidationDto`, `PaginationDto`
- Centralisez l’`Authorization` header via un `Dio`/`http` interceptor
- Utilisez des guards UI:
  - Si `statut == "actif"` → accès premium
  - Si `statut == "essai"` → affichage badge “Essai”
  - Si `joursRestants <= 7` → banner “expiration proche”
- Flux recommandé:
  - Lister offres → Valider promo → Créer abonnement → Process-payment → Check-status → Enregistrer statut local

- Notifications: côté backend, l’envoi Email/WhatsApp/SMS est automatique (instantané + cron). Aucune action requise côté Flutter.

- Données sensibles: le backend lit `codeEcole`/`codeUser` depuis le JWT. Assurez-vous que l’utilisateur Flutter est correctement authentifié.

- Formats dates: envoyer/afficher en ISO ou `YYYY-MM-DD`. Le backend accepte `YYYY-MM-DD` pour filtres.

- Montants: toujours préciser `devise` (XOF) et `montant` en entier (CFA).

- Offres test (si script d’init lancé): montants mensuel=700, trimestriel=2000, semestriel=3500, annuel=6000; promo “Bienvenue” (10%).

- Exemple d’en-têtes pour toutes requêtes:
```http
Authorization: Bearer <JWT>
Content-Type: application/json
Accept: application/json
```

- Exemple de création d’abonnement (Flutter):
```json
POST /api/abonnement/create
{
  "codeOffre": "OFFRE_123",
  "codePromotion": "PROMO_BIENVENUE",
  "methodePaiement": "mobile_money",
  "renouvellementAuto": true
}
```

- Exemple de paiement:
```json
POST /api/abonnement/process-payment
{
  "codeAbonnement": "ABO_123",
  "methodePaiement": "mobile_money",
  "montant": 630,
  "devise": "XOF",
  "referencePaiement": "PMT-123",
  "operateur": "Orange",
  "numeroTelephone": "+2250700000000",
  "fraisTransaction": 0
}
```

- Exemple de promo validation:
```json
POST /api/promotion-abonnement/validate
{
  "codePromotion": "PROMO_BIENVENUE",
  "codeOffre": "OFFRE_123",
  "montantOriginal": 700,
  "codeParent": "PAR001"
}
```

- Récupérer statut:
```http
GET /api/abonnement/check-status?codeAbonnement=ABO_123
```

- Lister abonnements parent:
```http
GET /api/abonnement/find?statut=actif&limit=1
```

- Lister offres:
```http
GET /api/offre-abonnement/find?actif=true&typeAbonnement=mensuel
```

- Gérer erreurs: mappez `code` pour UX adaptée (ex: PROMOTION_EXPIRED, SUBSCRIPTION_ALREADY_EXISTS)

- Critique: Pour `validate`, envoyez `codeParent = user.codeUser` (depuis JWT) tant que le backend le requiert.

- Besoin d’une route “abonnement actif du parent” simplifiée ? On peut l’ajouter si nécessaire. 