Back to ProjectsWeb Application
Travel Agency Flight Inquiry System
A single-page flight inquiry form with automated email notifications and reCAPTCHA protection for a Montreal-based travel agency
Overview
A Laravel 11 flight inquiry system built for Travel Agency, a Montreal-based travel agency. This is a lead generation tool — not a booking system. Customers fill out a single-page flight inquiry form with their travel details, which triggers automated email notifications to both the customer and agency staff. The system includes REST API access for programmatic inquiry management, secured by Laravel Sanctum.
Problem
The travel agency relied on manual processes for handling flight inquiries:
- ▸Customers had to call or email travel details, leading to miscommunication
- ▸No structured format for capturing flight preferences (origin, destination, dates)
- ▸Manual data entry was error-prone and time-consuming for staff
- ▸No audit trail or reference system for tracking inquiries
- ▸Spam and bot submissions clogged manual processes
Solution
A comprehensive flight inquiry platform with three main layers:
| Component | Stack | Role |
|---|---|---|
| Web Form | Blade + Vite + Vanilla JS | Single-page inquiry form with airport autocomplete, country code selector, and AJAX submission |
| Backend | Laravel 11 + PHP 8.2 | Inquiry processing, validation, reCAPTCHA verification, email dispatch (Brevo SMTP) |
| API | Laravel + Sanctum | RESTful endpoints for programmatic inquiry creation, lookup by reference, and admin listing |
Request Lifecycle
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Submit │───►│ Throttle│───►│ Validate │───►│ Process │───►│ JSON │ │ Inquiry │ │ 10/hr IP │ │+reCAPTCHA│ │ + Emails │ │ Response │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
Key Features
- ▸Single-page flight inquiry form with real-time validation
- ▸Google reCAPTCHA v3 + honeypot field for bot/spam protection
- ▸Automated email notifications via Brevo SMTP (user confirmation + admin alert)
- ▸REST API with Laravel Sanctum authentication for inquiry management
- ▸Auto-generated reference numbers (BT-YYYYMMDD-NNNNNN format)
- ▸Encrypted PII at rest via Laravel encrypted casts (7 sensitive fields)
- ▸Airport autocomplete with static dataset and country code selector
- ▸Custom rate limiting middleware (10 submissions/hr per IP)
- ▸Docker multi-stage build with Nginx + PHP-FPM + Supervisor
Security Implementation
| Concern | Implementation |
|---|---|
| PII at rest | Laravel encrypted casts on 7 sensitive fields (names, email, phone, IP, UA) |
| CSRF | @csrf directive in form, Laravel automatic protection |
| XSS | Blade auto-escaping |
| SQL injection | Eloquent ORM with parameterized queries |
| Rate limiting | Custom middleware: 10/hr per IP (web); Laravel throttle: 10/min (API store) |
| Spam/bot | reCAPTCHA v3 (score thresholds 0.3–0.5) + honeypot field |
Deployment & CI/CD
- ▸Hosting: Render (Docker, free tier, Oregon) + PostgreSQL (bta-db)
- ▸Container: PHP 8.2-fpm-alpine + Nginx + Supervisor multi-stage Docker build
- ▸CI/CD: GitHub Actions — lint, tests, Docker buildx, Render deploy
- ▸Health check: /health.php endpoint for Render monitoring
Technology Stack
| Layer | Technology |
|---|---|
| Backend | Laravel 11 / PHP 8.2 |
| Database | PostgreSQL 16 / MySQL 8.0 |
| Frontend | Blade / Vite / Vanilla JS |
| Security | reCAPTCHA v3 / Laravel Encrypted Casts / Sanctum |
| Testing | PHPUnit / SQLite in-memory |
| Container | Docker / Alpine / Nginx / PHP-FPM |
| CI/CD | GitHub Actions / Render |
Notable Design Decisions
- ▸Single table design — inquiries stored in one table with no relationships for simplicity and fast queries
- ▸Encrypted casts for all PII — leverages Laravel's built-in encryption (depends on APP_KEY), keeping sensitive data encrypted at rest
- ▸After-response email dispatch — emails sent after the HTTP response to avoid blocking the user
- ▸ThrottleInquiries middleware — custom rate limiter with configurable limit via INQUIRY_RATE_LIMIT env variable (default 10/hr)
- ▸reCAPTCHA v3 double validation — score threshold of 0.3 in form request (lenient) vs 0.5 in service (strict), with action name verification
- ▸Honeypot field — invisible website field that must remain empty, catching basic bots without user friction
- ▸Auto-generated reference numbers — collision-resistant BT-YYYYMMDD-NNNNNN format for easy customer lookup