Documentation
Everything about Meridian
This page is the reference manual: how the platform is organised, what each role can do, how every integration works under the hood, and what the API looks like for your future Flutter app. New to Meridian? Start with the Setup Guide instead.
Overview
Meridian is a Laravel 12 learning management system covering four jobs at once: selling courses, running live classes, administering exams, and issuing certificates — with every third-party integration (payments, SMS, WhatsApp, AI) controlled from the Admin Panel rather than hard-coded into the app.
PaymentManager,
SmsManager, WhatsappManager, AiManager), not just hidden in the UI —
so it can't be bypassed by a stray route or a forgotten checkbox.
| Stack | Choice |
|---|---|
| Backend | Laravel 12, PHP 8.2+ |
| Auth | Session (web) + Sanctum tokens (API/Flutter) |
| Roles | spatie/laravel-permission — Admin, Instructor, Student |
| Frontend | Blade + Tailwind (responsive, no build step required) |
| Uploads | Written directly to public/uploads/... |
Architecture & folders
The codebase follows standard Laravel conventions with a few dedicated folders for the integration layer, so you always know where to look:
app/
├── Http/Controllers/
│ ├── Admin/ # Platform management + gateway settings
│ ├── Instructor/ # Course/exam/live-class authoring
│ ├── Student/ # Learning experience
│ └── Api/ # Sanctum-authenticated REST endpoints (Flutter)
├── Services/
│ ├── Payment/Drivers/ # Cash, Stripe, Razorpay, PayPal, Paystack...
│ ├── Sms/Drivers/ # Twilio, Vonage, MSG91, TextLocal...
│ ├── Whatsapp/Drivers/ # Meta Cloud API, Twilio WhatsApp
│ └── Ai/AiManager.php # Every AI feature, one gated entry point
├── Models/ # One Eloquent model per table, relationships wired
routes/
├── web.php, api.php, admin.php, instructor.php, student.php
public/uploads/ # courses, users, certificates, live-classes, exams, site
User roles
Every account has exactly one primary role, enforced by an role: middleware alias on
each route group, and gets its own dashboard layout so nobody sees controls they don't need.
| Role | Can do | Route prefix |
|---|---|---|
| Admin | Approve courses, manage users/categories/curricula, configure every integration, confirm cash orders, view reports | /admin/* |
| Instructor | Build courses, schedule live classes, author exams, use AI authoring tools, request payouts | /instructor/* |
| Student | Browse, enroll, track progress, sit exams, join live classes, chat with the AI tutor, download certificates | /student/* |
Course & sale lifecycle
- Instructor creates a course (draft) with sections, lessons, pricing and curriculum/grade-level tags.
- Instructor submits it for review → status becomes
pending_review. - Admin approves → status becomes
publishedand it appears in the public catalog. - Student adds it to cart, applies a coupon if any, and checks out through any Admin-enabled gateway.
- On payment confirmation (or immediately, for cash/free), an
Enrollmentrow is created. - Progress is tracked per lesson; once 100% complete, a certificate PDF is generated automatically.
Payment gateways
Admin can enable any number of gateways at the same time from
/admin/settings/payment-gateways; the student chooses one at checkout. Cash is
always enabled as the guaranteed fallback and can't be switched off.
| Gateway | Driver class | Credentials needed |
|---|---|---|
| Cash / Offline | CashGateway | None — always on |
| Bank Transfer | BankTransferGateway | Bank details text block |
| Stripe | StripeGateway | Key, Secret, Webhook secret |
| Razorpay | RazorpayGateway | Key, Secret |
| PayPal | PaypalGateway | Client ID, Client secret, Mode |
| Paystack | PaystackGateway | Public key, Secret key |
| Flutterwave | FlutterwaveGateway | Public key, Secret key |
| SSLCommerz | SslCommerzGateway | Store ID, Store password, Mode |
Every driver implements the same three methods, so adding a ninth gateway later is a matter of writing one class:
interface PaymentGatewayInterface {
public function boot(array $credentials, string $mode = 'sandbox'): void;
public function initiate(Order $order, array $options = []): array;
public function verify(Order $order, array $payload = []): bool;
public function refund(Order $order, ?float $amount = null): bool;
}
SMS gateways
Admin can store credentials for as many SMS providers as they like, but exactly
one is ever is_active — activating one automatically deactivates the rest,
so there's never ambiguity about which provider actually sends a message.
| Provider | Best for |
|---|---|
| Twilio | Global coverage, reliable delivery |
| Vonage (Nexmo) | Global coverage, competitive pricing |
| MSG91 | India / South Asia |
| TextLocal | UK / India |
| Generic HTTP | Any local/regional gateway with a simple HTTP API |
WhatsApp integration
Built on the official Meta WhatsApp Cloud API by default (Twilio WhatsApp also included). A master switch turns the whole integration on or off, and six event-level toggles control exactly which system events actually send a message — all of which are genuinely wired into the checkout, exam grading, and certificate-issuing flows, not just present in Settings:
- Enrollment confirmation
- Payment receipt
- Live class reminder
- Exam result
- Certificate issued
- OTP verification
AI features
One master switch (Admin → Settings → AI) plus fifteen independent feature toggles,
all read from the database so they can change instantly without a redeploy. Every single one
below has a real, working implementation behind it — not just a checkbox:
| Feature | Used by |
|---|---|
| Course description generator | Instructor authoring |
| Lesson summary generator | Instructor authoring |
| Quiz question generator | Instructor authoring |
| Exam auto-grading (essay/short-answer) | Assessment |
| AI essay feedback | Assessment |
| AI tutor chatbot | Student learning |
| Course recommendation engine | Student discovery |
| Student performance insights | Analytics |
| Auto-translate content | Localization |
| Video transcript generator | Accessibility (real Whisper API call) |
| Plagiarism / similarity check | Assessment integrity |
| Smart certificate notes | Certificates |
| AI proctoring flagging | Exam integrity (tab-switch/blur detection) |
| Chat & review moderation | Safety |
| SEO meta generator | Marketing |
AiManager calls
assertFeature() first and throws if that specific toggle is off — even if the master
switch is on.
REST API (Flutter-ready)
Every learner-facing action already exists as a versioned, Sanctum-authenticated JSON
endpoint under /api/v1/... — the Flutter app is a client of this API, not a reason to
change the backend.
# Auth
POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/auth/otp/request
POST /api/v1/auth/otp/verify
POST /api/v1/auth/social
# Catalog (public)
GET /api/v1/courses
GET /api/v1/courses/{slug}
# Authenticated
POST /api/v1/checkout
GET /api/v1/my-courses
POST /api/v1/my-courses/{slug}/lessons/{lesson}/progress
GET /api/v1/exams/{exam}
POST /api/v1/exam-attempts/{attempt}/submit
GET /api/v1/live-classes
GET /api/v1/certificates
Uploads & storage
Every upload — course videos and thumbnails, avatars, certificates, exam images, site
branding — is written directly to public/uploads/<type>. There's no
storage:link symlink dependency for the media the app actually serves, which
keeps things simple on shared hosting.
| Folder | Contains |
|---|---|
public/uploads/courses | Thumbnails, lesson videos, attachments |
public/uploads/users | Avatars |
public/uploads/certificates | Generated PDF certificates |
public/uploads/live-classes | Recordings/thumbnails |
public/uploads/exams | Question images |
public/uploads/site | Logo, favicon, branding assets |
FAQ
Can I add a payment gateway that isn't listed?
Yes — implement PaymentGatewayInterface, register it in config/payment.php,
and it appears in the Admin settings screen automatically.
Does disabling AI remove the database columns?
No. Disabling AI just stops the feature methods from running — nothing is deleted, so you can re-enable it later with no data loss.
Is the API versioned for future breaking changes?
Yes — everything lives under /api/v1, so a future /api/v2 can be introduced
without breaking an already-shipped mobile app.
Ready to install it?
The Setup Guide walks through every step, start to first login.