ENFRKOantony langlois
projets

XiLearn

visitersite webNext.jsNotion APIStripeMoodle

Plateforme d'e-learning trilingue pour le Laos · une vitrine Next.js avec Notion comme unique base de données vend les cours · un Moodle auto-hébergé les enseigne · paiement Stripe en en / fr / lo

Architecture

Next.js client
Frontend · vitrine
Storefront UI
src/app/[locale] · app router
Catalogue, pages de cours, paiement · rend le PaymentElement Stripe
SaaS
Externe · paiements
Stripe
Payment Intents · confirmation carte dans le navigateur
Node
Cœur · serveur Next.js
App Router server
src/app · SSR + API routes
Lit Notion à chaque rendu · /api/create-payment-intent · /api/addStudent
next-intl locale middlewaredonnées serveur vers contextes clientécritures d'inscription idempotenteshreflang + sitemap SEOclés secrètes côté serveur uniquement
php · apache
Service · LMS
Moodle LMS
xilearn-moodle · Docker
Auto-hébergé · battement cron par minute intégré
SaaS
Externe · CMS + back-office
Notion databases
Cours · catégories · file étudiante (Need to be added)
localhost:8081
Outillage · admin BD
phpMyAdmin
Inspection des tables Moodle en développement uniquement
Docker volume
Base de données · données LMS
MariaDB
Tables mdl_* · fichiers dans le volume moodledata

Un visiteur arrive sur la Storefront UI à /lo et le serveur App Router répond en récupérant le catalogue trilingue depuis Notion et en le rendant côté serveur. Au paiement, le serveur génère un PaymentIntent via /api/create-payment-intent, le navigateur confirme la carte avec le Payment Element de Stripe, et /api/addStudent inscrit l'apprenant dans la file d'attente étudiante de Notion avec le statut Need to be added, dédoublonné sur email + cours. Les ops traitent cette file et inscrivent l'apprenant à la main dans Moodle, qui tourne comme sa propre stack Docker et persiste tout dans MariaDB · phpMyAdmin l'accompagne pour l'inspection en développement.

Déploiement : Vitrine → Next.js · xilearn.com · Stack LMS → Docker Compose · Moodle + MariaDB + phpMyAdmin

Résultats

  • 3 locales · en / fr / lo · l'écriture lao a sa propre police
  • 0 interface admin · Notion est le CMS, le back-office et la file d'inscription
  • 2 systèmes · vitrine et LMS se déploient indépendamment
  • 1 commande · stack LMS complète lancée via docker compose

Compétences démontrées : modélisation de données en CMS headless · cycle de vie des Payment Intents Stripe · routage SEO préfixé par locale · packaging d'un monolithe PHP dans Docker · garde-fous d'écriture idempotents

Problèmes et solutions

Chaque choix répond à une contrainte : une petite équipe qui vend des cours trilingues ne peut pas se permettre de construire ou de surveiller des systèmes backend sur mesure.

▩ Acheter un LMS, construire la vitrine

Problème : Une vraie plateforme de cours a besoin de leçons, de quiz, de suivi de progression et de certificats. Construire tout cela seul prend des années, mais un Moodle prêt à l'emploi paraît daté et ne sait faire ni marketing, ni SEO, ni paiement par carte.

Scinder le produit en deux : une vitrine Next.js possède la découverte, les langues et les paiements, tandis qu'un Moodle auto-hébergé possède tout l'enseignement. Ils ne se rencontrent qu'à l'inscription.

  • Rejet de la construction d'un LMS sur mesure : les quiz et le suivi de progression sont des problèmes résolus, pas des différenciateurs
  • Rejet du thème Moodle transformé en site marketing : se battre contre un monolithe PHP pour chaque pixel
  • Compromis accepté : deux déploiements et une jointure à faire le pont

▦ Notion comme CMS, base de données et back-office

Problème : Le contenu des cours existe en anglais, français et lao et est édité par des non-développeurs. Un panneau d'administration sur mesure, ou un abonnement à un CMS headless, est un projet entier avant même la première vente de cours.

Les bases de données Notion sont le seul datastore de la vitrine. Cours, catégories et inscriptions étudiantes vivent dans des tables que l'équipe édite déjà quotidiennement ; le serveur les lit via l'API officielle à chaque rendu.

  • Bat le SaaS façon Contentful : aucun nouvel outillage pour les éditeurs, aucun coût supplémentaire
  • Une seule ligne contient les trois langues côte à côte, donc les traductions ne dérivent jamais
  • Requêtage faible et latence de l'API acceptés ; la taille du catalogue rend les deux sans importance

▧ Une file d'inscription humaine plutôt qu'un pont API Moodle

Problème : Après le paiement, un apprenant a besoin d'un compte Moodle et d'une inscription au cours. L'automatisation par web-service Moodle est fragile à mettre en place, et au début chaque paiement mérite un regard humain.

Le paiement inscrit l'apprenant dans une table Notion avec le statut Need to be added ; les ops traitent la file et inscrivent à la main. Un filtre de déduplication sur email + cours + statut empêche les réessais de créer des lignes en double.

  • Rejet de l'auto-provisionnement via les web services Moodle : des jours d'intégration pour une étape qu'un humain fait en quelques secondes au volume actuel
  • La revue manuelle sert aussi de vérification du paiement
  • Compromis accepté : l'accès n'est pas instantané, ce que les cours en cohortes tolèrent

▥ Paiement Stripe embarqué, pas une redirection hébergée

Problème : Envoyer les acheteurs sur une page de paiement externe brise la confiance sur un site desservant un marché où payer par carte en ligne est déjà un saut.

Le serveur génère un PaymentIntent par paiement et le navigateur affiche le Payment Element de Stripe en ligne, confirmant avec une redirection vers /payment-success. La clé secrète ne quitte jamais le serveur.

  • Rejet du Checkout hébergé par Stripe : une redirection hors marque au moment le plus sensible
  • Un parcours de paiement par QR-code a été prototypé, puis mis de côté jusqu'à ce qu'un fournisseur local vaille l'entretien
  • Les moyens de paiement automatiques laissent Stripe décider quoi proposer à chaque acheteur

▨ Un routage trilingue que les moteurs de recherche peuvent indexer

Problème : L'écriture lao s'affiche mal dans les polices latines, et la traduction côté client cache le catalogue lao et français aux moteurs de recherche.

Le middleware next-intl sert des URLs préfixées par locale (/en, /fr, /lo) avec des alternatives hreflang, et le layout bascule sur Noto Sans Lao quand la locale est lo. Les chaînes d'UI vivent en JSON, les traductions de contenu dans les colonnes Notion.

  • Rejet des bibliothèques d'i18n côté client : pas d'URLs indexables par langue
  • Le chargement de police par locale garde le texte lao lisible sans expédier chaque police à tout le monde
  • Coût accepté : chaque champ de contenu existe trois fois dans le CMS

▣ Moodle depuis les sources dans une image Docker sur mesure

Problème : Moodle a besoin d'extensions PHP exactes, d'un répertoire de données inscriptible et d'un battement cron. Les serveurs installés à la main pourrissent et ne peuvent pas être reconstruits quand ils cassent.

Un Dockerfile php-apache sur mesure intègre chaque extension, opcache et un cron par minute ; config.php et les sources Moodle se montent en volumes et toute la stack démarre avec une seule commande compose.

  • Rejet de l'image Bitnami préconstruite : couches opaques, difficile d'épingler la version exacte de Moodle
  • Checkout des sources épinglé sur une branche stable, donc les mises à niveau sont délibérées
  • Possession de l'image acceptée ; en échange dev et prod sont identiques au bit près