A modern, mobile-friendly invoice management application with OTP authentication for creating, editing, viewing, and managing invoices. Perfect for small businesses and freelancers.
| Feature | Description |
|---|---|
| π OTP Login | Secure one-time password authentication |
| π Create Invoices | Create new invoices with customer details and line items |
| βοΈ Edit Invoices | Modify existing invoices and update details |
| ποΈ View Invoices | View detailed invoice information |
| ποΈ Delete Invoices | Remove unwanted invoices |
| π Export to PDF | Generate and download PDF versions |
| π Share Invoices | Create shareable public links |
| π± Responsive Design | Works perfectly on phones, tablets, and desktops |
| π’ Auto Numbering | Automatic invoice number generation (INV-2026-00001) |
| π° Flexible Discounts | Item-level and invoice-level discounts |
| π Tax Calculation | Automatic VAT/tax calculation (15%) |
OTP (One-Time Password) Login System
- Login via OTP code sent to email
- Test credentials available for all environments (localhost and deployed):
- Email:
test@invoyce.com - OTP Code:
123456
- Email:
- On production (cPanel), OTP codes are emailed via PHP
mail()function - On localhost, OTP code is shown in debug message for convenience
- PHP 7.4 or higher with
mail()support (for production OTP emails) - MySQL 5.7 or higher
- Web Server Apache, Nginx, or built-in PHP server
- Modern Browser (Chrome, Firefox, Safari, Edge)
Create the database and run all SQL to create tables:
CREATE DATABASE IF NOT EXISTS invoice_app;
USE invoice_app;
CREATE TABLE customers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255),
phone VARCHAR(20),
address TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_email (email),
INDEX idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE invoices (
id INT AUTO_INCREMENT PRIMARY KEY,
invoice_number VARCHAR(20) UNIQUE,
customer_id INT NOT NULL,
invoice_date DATE NOT NULL,
due_date DATE NOT NULL,
subtotal DECIMAL(10,2) NOT NULL DEFAULT 0.00,
tax DECIMAL(10,2) NOT NULL DEFAULT 0.00,
discount_percent DECIMAL(5,2) DEFAULT 0.00,
discount_amount DECIMAL(10,2) DEFAULT 0.00,
total DECIMAL(10,2) NOT NULL DEFAULT 0.00,
status ENUM('draft', 'sent', 'paid', 'overdue') DEFAULT 'draft',
share_token VARCHAR(64) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE,
INDEX idx_customer (customer_id),
INDEX idx_status (status),
INDEX idx_date (invoice_date),
INDEX idx_invoice_number (invoice_number)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE invoice_items (
id INT AUTO_INCREMENT PRIMARY KEY,
invoice_id INT NOT NULL,
description VARCHAR(255) NOT NULL,
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
unit_price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
discount_percent DECIMAL(5,2) DEFAULT 0.00,
discount_amount DECIMAL(10,2) DEFAULT 0.00,
line_total DECIMAL(10,2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE CASCADE,
INDEX idx_invoice (invoice_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE otp_codes (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
code VARCHAR(10) NOT NULL,
expires_at DATETIME NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_email (email),
INDEX idx_expires_at (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;Edit config/db.php:
<?php
$host = "localhost";
$db = "invoice_app";
$user = "root";
$pass = "";
$charset = "utf8mb4";
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
$pdo = new PDO($dsn, $user, $pass, $options);Using PHP's built-in server:
php -S localhost:8000Then open http://localhost:8000/login.html in your browser to login.
Use test credentials:
- Email:
test@invoyce.com - OTP:
123456
The application uses OTP (One-Time Password) authentication. Users login with their email and receive a code.
- Open
http://localhost:8000/login.html - Enter
test@invoyce.com - Click "Request OTP"
- A debug message shows the code:
123456 - Enter the code and click "Verify OTP"
- You'll be logged in and redirected to the dashboard
- Open
login.htmlon your domain - Enter
test@invoyce.com - Click "Request OTP"
- Check your email for the OTP code (uses server's
mail()function) - Enter the code and verify
- Access your dashboard
TODO
invoyce/
βββ login.html # OTP Login page
βββ index.html # Dashboard - List all invoices (protected)
βββ create_invoice.html # Create new invoice (protected)
βββ view_invoice.html # View invoice details (protected)
βββ edit_invoice.html # Edit existing invoice (protected)
βββ config/
β βββ db.php # Database configuration
βββ api/
β βββ auth.php # OTP authentication (request & verify)
β βββ invoices.php # Main invoice API (create, read, update, delete)
β βββ customers.php # Customer management
β βββ invoices_list.php # List all invoices
β βββ delete_invoice.php # Delete invoice endpoint
β βββ export_pdf.php # PDF export
β βββ share_invoice.php # Public invoice view
βββ assets/
β βββ styles.css # Main stylesheet (responsive, modern design)
β βββ app.js # Frontend logic (auth guard, invoice forms)
βββ sql/
β βββ add_invoice_number.sql # Add invoice_number column migration
β βββ add_auth_tables.sql # Create users & otp_codes tables
βββ tools/
β βββ extract_pdf.py # PDF extraction tool (internal)
β βββ extract_images.py # Image extraction tool (internal)
βββ README.md # This file - setup & usage guide
- Go to
/login.html - Enter email (use
test@invoyce.comfor testing) - Click "Request OTP"
- Check email (or see debug code on localhost)
- Enter OTP code and verify
- You're logged in and redirected to dashboard
- Click "Create Invoice" on dashboard or nav
- Select or add a customer
- Set invoice and due dates
- Add line items with descriptions, quantities, and prices
- Set item-level or invoice-level discounts
- Review totals (auto-calculated with 15% VAT)
- Save invoice
- Go to dashboard and click "Edit" on any invoice
- Modify customer, dates, items, or discounts
- Totals update automatically
- Save changes
- Click "View" on the dashboard
- See full details, customer info, and line items
- Options to edit, delete, export PDF, or generate share link
- Open invoice and click "Get Share Link"
- Share the URL with customer
- Customer can view invoice without login (public link)
- Click your email in the top-right nav
- Click "Logout"
- You're logged out and redirected to login page
| Method | Endpoint | Action |
|---|---|---|
| POST | /api/invoices.php?action=create_invoice |
Create invoice |
| GET | /api/invoices.php?action=get_invoice&id=1 |
Get invoice details |
| POST | /api/invoices.php?action=update_invoice |
Update invoice |
| POST | /api/invoices.php?action=delete_invoice |
Delete invoice |
| GET | /api/invoices_list.php |
List all invoices |
| GET/POST | /api/customers.php |
Get/create customers |
β Fully responsive design for all devices β Touch-friendly buttons and inputs β Optimized form layouts for mobile β Hidden columns on small screens β 16px font size (prevents iOS zoom) β 48px minimum button height
"OTP sent to email" but I don't receive it
- β On localhost: code is shown in debug alert (not emailed)
- β On production: ensure
mail()is configured on your server - β Check email spam folder
- β Verify cPanel mail settings if hosted
Test credentials not working
- β Ensure
sql/add_auth_tables.sqlhas been run - β Try email:
test@invoyce.comand OTP:123456 - β Reload the page if it seems stuck
- β Check
config/db.phpcredentials match your MySQL setup - β Verify MySQL is running (
mysql -u rootshould connect) - β Confirm database exists:
mysql -u root -e "SHOW DATABASES" - β Ensure all migrations have been run
Run all migrations:
mysql -u root invoice_app < sql/add_invoice_number.sql
mysql -u root invoice_app < sql/add_auth_tables.sql- β You must login first (OTP required)
- β All pages except
login.htmlrequire authentication - β Clear browser storage:
localStorage.removeItem('invoyce_user')
- β Ensure all files are in correct directories
- β Check web server is serving the app root
- β Verify file permissions (644 for files, 755 for folders)
- β Check browser console for JavaScript errors (F12 β Console)
- β Ensure
assets/app.jsis loading - β Clear browser cache (Ctrl+Shift+Delete)
- β Try incognito/private browsing mode
Edit assets/app.js, find the recalc() function:
const tax = subtotal * 0.15; // Change 0.15 to desired rate (e.g., 0.10 for 10%)Edit currency symbol in:
assets/app.js(search forRin display functions)view_invoice.html(search forRin totals display)
Replace R with your currency (e.g., $, β¬, Β£, Β₯)
Edit api/auth.php, find the email sending section:
$headers = "From: admin@dreyerventures\r\n" .
"Reply-To: admin@dreyerventures\r\n";Replace with your email address.
Edit api/auth.php, find OTP generation:
$expiresAt = (new DateTime('+10 minutes'))->format('Y-m-d H:i:s');
// Change '+10 minutes' to desired timeframe- β OTP login system implemented
- β Session storage via localStorage (can be improved to server sessions)
- β Add authorization checks (users can only see their own invoices)
- β Implement server-side session validation
- β Use HTTPS (SSL certificate required)
- β Update domain in
api/auth.phpfor email sender - β Configure proper mail() or SMTP for email delivery
- β Add CORS headers if API is accessed from different domain
- β All inputs are validated and sanitized
- β PDO prepared statements prevent SQL injection
- β Add rate limiting on OTP requests (prevent brute force)
- β Add audit logging for invoice changes
- β Implement IP-based rate limiting
- β Set
display_errors = Offin productionphp.ini - β Store database credentials securely (not in repo)
- β Use environment variables for sensitive config
- β Keep PHP and MySQL updated
- β Regular database backups
MIT License - Free to use and modify for personal or commercial projects.
- Payment gateway integration (Stripe, PayPal)
- Email invoice delivery to customers
- Recurring/subscription invoices
- Invoice templates (different styles/layouts)
- Analytics and financial reports
- Multi-currency support
- Advanced search and filtering
- Invoice reminders (automatic emails)
- Multi-user teams (shared access)
- Two-factor authentication (2FA)
- Webhook support for integrations
- Mobile app (iOS/Android)