Invoice & Statement Generator
In Hostinger File Manager, navigate to public_html and create these folders:
Go to Hostinger Panel ā Databases ā MySQL Databases:
ā
Your database name: u226996936_invoices_db
ā
Your database username: u226996936_clyde_reese
ā ļø IMPORTANT: On Hostinger, database usernames are typically prefixed with your account ID (e.g., u226996936_). Check the exact username in your Hostinger panel under Databases ā MySQL Databases ā Users.
3. Make sure the user has ALL PRIVILEGES on u226996936_invoices_db
Open phpMyAdmin from Hostinger panel, select u226996936_invoices_db, click SQL tab, paste and run:
ā ļø Your table is missing the deposit_amount column. Run this ALTER statement first:
ALTER TABLE guests ADD COLUMN deposit_amount DECIMAL(10,2) DEFAULT 500.00 AFTER quoted_rate;
Or if starting fresh, use the full CREATE TABLE:
CREATE TABLE IF NOT EXISTS guests (
id INT AUTO_INCREMENT PRIMARY KEY,
guest_key VARCHAR(100) UNIQUE NOT NULL,
invoice_number VARCHAR(50),
invoice_date DATE,
account_id VARCHAR(50),
renter_name VARCHAR(200) NOT NULL,
renter_phone VARCHAR(50),
renter_street VARCHAR(255),
renter_city_state_zip VARCHAR(255),
renter_email VARCHAR(255),
check_in_date DATE,
check_out_date DATE,
quoted_rate DECIMAL(10,2),
deposit_amount DECIMAL(10,2) DEFAULT 500.00,
invoice_schedule JSON,
payments JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Your diagnostics show that the PHP files are outputting their raw source code instead of being executed by the server. This is caused by:
1. Missing or corrupted <?php opening tag. The output shows ?php instead of <?php. This means the opening < character was lost when you copied/pasted the file.
2. Wrong username in config.php. Your config.php has clyde_reese but it should be u226996936_clyde_reese (with the Hostinger prefix).
How to fix:
1. Open Hostinger File Manager
2. Navigate to invoices/api/
3. Delete the existing config.php and guests.php
4. Create new files using the corrected code in Steps 4 and 5 below
5. Make sure the VERY FIRST characters in each file are <?php with NO spaces or blank lines before it
ā ļø IMPORTANT: Copy this EXACTLY. The very first line must be <?php with NO spaces before it.
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
$host = 'localhost';
$dbname = 'u226996936_invoices_db';
$username = 'u226996936_clyde_reese';
$password = '2026vnkwMPsn974@!';
try {
$pdo = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
$username,
$password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
'error' => 'Database connection failed',
'detail' => $e->getMessage()
]);
exit();
}
?>
ā ļø The username MUST be u226996936_clyde_reese (with the prefix), NOT just clyde_reese
ā ļø Make sure there are NO blank lines or spaces BEFORE <?php
ā ļø IMPORTANT: Copy this EXACTLY. The very first line must be <?php with NO spaces before it.
<?php
require_once 'config.php';
$method = $_SERVER['REQUEST_METHOD'];
try {
switch ($method) {
case 'GET':
if (isset($_GET['id'])) {
$stmt = $pdo->prepare('SELECT * FROM guests WHERE guest_key = ?');
$stmt->execute([$_GET['id']]);
$guest = $stmt->fetch();
if ($guest) {
$guest['invoice_schedule'] = json_decode($guest['invoice_schedule'], true);
$guest['payments'] = json_decode($guest['payments'], true);
echo json_encode($guest);
} else {
http_response_code(404);
echo json_encode(['error' => 'Guest not found']);
}
} else {
$stmt = $pdo->query('SELECT guest_key, renter_name, check_in_date, account_id FROM guests ORDER BY renter_name ASC');
echo json_encode($stmt->fetchAll());
}
break;
case 'POST':
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || !isset($data['renterName'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields']);
break;
}
$guestKey = 'guest_' . time() . '_' . bin2hex(random_bytes(4));
$stmt = $pdo->prepare('INSERT INTO guests (guest_key, invoice_number, invoice_date, account_id, renter_name, renter_phone, renter_street, renter_city_state_zip, renter_email, check_in_date, check_out_date, quoted_rate, deposit_amount, invoice_schedule, payments) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
$stmt->execute([
$guestKey,
$data['invoiceNumber'] ?? '',
$data['invoiceDate'] ?? null,
$data['accountId'] ?? '',
$data['renterName'],
$data['renterPhone'] ?? '',
$data['renterStreet'] ?? '',
$data['renterCityStateZip'] ?? '',
$data['renterEmail'] ?? '',
$data['checkInDate'] ?? null,
$data['checkOutDate'] ?? null,
$data['quotedRate'] ?? 0,
$data['depositAmount'] ?? 500,
json_encode($data['invoiceSchedule'] ?? []),
json_encode($data['payments'] ?? [])
]);
echo json_encode(['success' => true, 'guest_key' => $guestKey]);
break;
case 'PUT':
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || !isset($data['guest_key'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing guest_key']);
break;
}
$stmt = $pdo->prepare('UPDATE guests SET invoice_number=?, invoice_date=?, account_id=?, renter_name=?, renter_phone=?, renter_street=?, renter_city_state_zip=?, renter_email=?, check_in_date=?, check_out_date=?, quoted_rate=?, deposit_amount=?, invoice_schedule=?, payments=? WHERE guest_key=?');
$stmt->execute([
$data['invoiceNumber'] ?? '',
$data['invoiceDate'] ?? null,
$data['accountId'] ?? '',
$data['renterName'] ?? '',
$data['renterPhone'] ?? '',
$data['renterStreet'] ?? '',
$data['renterCityStateZip'] ?? '',
$data['renterEmail'] ?? '',
$data['checkInDate'] ?? null,
$data['checkOutDate'] ?? null,
$data['quotedRate'] ?? 0,
$data['depositAmount'] ?? 500,
json_encode($data['invoiceSchedule'] ?? []),
json_encode($data['payments'] ?? []),
$data['guest_key']
]);
echo json_encode(['success' => true]);
break;
case 'DELETE':
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || !isset($data['guest_key'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing guest_key']);
break;
}
$stmt = $pdo->prepare('DELETE FROM guests WHERE guest_key = ?');
$stmt->execute([$data['guest_key']]);
echo json_encode(['success' => true]);
break;
default:
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'error' => 'Server error',
'detail' => $e->getMessage()
]);
}
?>
Create a file named test.php inside the api/ folder to help diagnose connection issues:
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$results = [];
$results['php_version'] = phpversion();
$results['pdo_available'] = extension_loaded('pdo_mysql');
$host = 'localhost';
$dbname = 'u226996936_invoices_db';
$username = 'u226996936_clyde_reese';
$password = '2026vnkwMPsn974@!';
$results['config'] = [
'host' => $host,
'dbname' => $dbname,
'username' => $username,
'password_set' => !empty($password) && $password !== 'YOUR_DB_PASSWORD_HERE'
];
try {
$pdo = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
$username,
$password,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$results['connection'] = 'SUCCESS';
$stmt = $pdo->query("SHOW TABLES LIKE 'guests'");
$tableExists = $stmt->rowCount() > 0;
$results['guests_table_exists'] = $tableExists;
if ($tableExists) {
$stmt = $pdo->query("SELECT COUNT(*) as cnt FROM guests");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$results['guest_count'] = $row['cnt'];
$stmt = $pdo->query("DESCRIBE guests");
$cols = $stmt->fetchAll(PDO::FETCH_COLUMN);
$results['table_columns'] = $cols;
}
$stmt = $pdo->query("SELECT CURRENT_USER() as u");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$results['connected_as'] = $row['u'];
$stmt = $pdo->query("SHOW GRANTS FOR CURRENT_USER()");
$grants = $stmt->fetchAll(PDO::FETCH_COLUMN);
$results['grants'] = $grants;
} catch (PDOException $e) {
$results['connection'] = 'FAILED';
$results['error_code'] = $e->getCode();
$results['error_message'] = $e->getMessage();
}
echo json_encode($results, JSON_PRETTY_PRINT);
?>
ā ļø Delete test.php after debugging for security
Upload a file named .htaccess inside the api/ folder with this content:
Options -Indexes
<Files "config.php">
Order Allow,Deny
Deny from all
</Files>
Upload a file named .htaccess in the invoices/ folder:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
In Hostinger ā Domains ā Subdomains:
1. Create subdomain: invoices.condolawguide.com
2. Point it to folder: public_html/invoices
3. Enable SSL certificate for the subdomain
Once all files are uploaded and the database is ready, click the button below to enable MySQL storage instead of browser localStorage:
Current mode: localStorage (browser only)
Click the button below to toggle between localStorage and MySQL:
This setting is saved in your browser. You only need to do this once per device.
Click the button below to run diagnostics against your API and database. Make sure you have uploaded api/test.php first.
This entire HTML file is your index.html. Upload it to public_html/invoices/index.html on Hostinger. When you visit https://invoices.condolawguide.com, this page will load automatically.