some fixes
This commit is contained in:
parent
71522e17f2
commit
90c7eef36b
2 changed files with 213 additions and 191 deletions
|
|
@ -1,191 +1,210 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Payment Processing Helper
|
* Standalone Payment Processing Helper (module-local)
|
||||||
* Handles marking invoices as paid and creating/extending orders
|
*
|
||||||
|
* This file is intentionally self-contained and does NOT include or rely on
|
||||||
|
* panel-wide files. Configure the DB connection information below (edit
|
||||||
|
* the $DB_* variables) or provide a module-local `config.local.php` that defines
|
||||||
|
* the same variables.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* require_once __DIR__ . '/payment_processor.php';
|
||||||
|
* process_payment_record($record);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!function_exists('process_payment_record')) {
|
// Include the module-local config (must be present in the module includes folder).
|
||||||
/**
|
// Use ONLY `config.inc.php` — do NOT fall back to other filenames.
|
||||||
* Process payment record from webhook or capture
|
$moduleConfig = __DIR__ . '/config.inc.php';
|
||||||
* Marks invoices as paid and creates/extends orders
|
if (is_file($moduleConfig)) {
|
||||||
*
|
require_once $moduleConfig;
|
||||||
* @param array $record Payment record with invoice, custom, amount, txid, etc.
|
} else {
|
||||||
* @return bool True if successful, false otherwise
|
error_log('[payment_processor] Module config not found: expected ' . $moduleConfig);
|
||||||
*/
|
}
|
||||||
function process_payment_record($record) {
|
|
||||||
global $db_host, $db_user, $db_pass, $db_name, $db_port, $table_prefix;
|
|
||||||
|
|
||||||
// Extract payment details
|
// Normalize table prefix variable: many files use $table_prefix (lowercase)
|
||||||
$invoice = $record['invoice'] ?? null;
|
if (!isset($TABLE_PREFIX) && isset($table_prefix)) {
|
||||||
$custom = $record['custom'] ?? null;
|
$TABLE_PREFIX = $table_prefix;
|
||||||
$txid = $record['resource_id'] ?? null;
|
}
|
||||||
$amount = $record['amount'] ?? 0;
|
|
||||||
|
|
||||||
// Require database connection
|
/**
|
||||||
// When deployed at website root: includes/payment_processor.php -> ../../includes/database_mysqli.php
|
* Create and return a mysqli connection using the site's config values.
|
||||||
// When in repo: modules/billing/includes/payment_processor.php -> ../../../includes/database_mysqli.php
|
* Returns mysqli or false on failure.
|
||||||
$db_mysqli_path = file_exists(__DIR__ . '/../../includes/database_mysqli.php')
|
*/
|
||||||
? __DIR__ . '/../../includes/database_mysqli.php' // Deployed at website root
|
function payment_db_connect() {
|
||||||
: __DIR__ . '/../../../includes/database_mysqli.php'; // In repo structure
|
global $db_host, $db_user, $db_pass, $db_name, $db_port;
|
||||||
require_once($db_mysqli_path);
|
|
||||||
$db = createDatabaseConnection($db_host, $db_user, $db_pass, $db_name, $db_port);
|
if (empty($db_host) || empty($db_user) || empty($db_name)) {
|
||||||
if (!$db) {
|
error_log('[payment_processor] DB globals not available from includes/config.inc.php');
|
||||||
if (function_exists('site_log_error')) site_log_error('process_payment_db_fail', ['invoice'=>$invoice]);
|
return false;
|
||||||
else error_log('[payment_success] DB connection failed for invoice=' . $invoice);
|
}
|
||||||
return false;
|
|
||||||
|
$port = intval($db_port) ?: 3306;
|
||||||
|
$db = @mysqli_connect($db_host, $db_user, $db_pass, $db_name, $port);
|
||||||
|
if (!$db) {
|
||||||
|
error_log('[payment_processor] mysqli_connect failed: ' . mysqli_connect_error());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mysqli_set_charset($db, 'utf8mb4');
|
||||||
|
return $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a payment record. Self-contained: uses module-local DB config.
|
||||||
|
* @param array $record { invoice, custom, resource_id, amount }
|
||||||
|
* @return bool true if at least one invoice processed
|
||||||
|
*/
|
||||||
|
function process_payment_record(array $record) {
|
||||||
|
global $TABLE_PREFIX;
|
||||||
|
|
||||||
|
// Normalize inputs
|
||||||
|
$invoice_ref = $record['invoice'] ?? null; // e.g. PayPal invoice id or custom label
|
||||||
|
$custom = $record['custom'] ?? null; // our custom_id (often invoice_id)
|
||||||
|
$txid = $record['resource_id'] ?? ($record['txid'] ?? null);
|
||||||
|
$amount = isset($record['amount']) ? floatval($record['amount']) : null;
|
||||||
|
|
||||||
|
$db = payment_db_connect();
|
||||||
|
if (!$db) return false;
|
||||||
|
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$esc_txid = mysqli_real_escape_string($db, (string)$txid);
|
||||||
|
|
||||||
|
$invoices_to_process = [];
|
||||||
|
|
||||||
|
// 1) If custom is a single numeric invoice id, try to fetch that exact invoice
|
||||||
|
if ($custom && ctype_digit((string)$custom)) {
|
||||||
|
$invoice_id = intval($custom);
|
||||||
|
$sql = "SELECT * FROM `" . $TABLE_PREFIX . "billing_invoices` WHERE invoice_id = ? AND status = 'due' LIMIT 1";
|
||||||
|
if ($stmt = mysqli_prepare($db, $sql)) {
|
||||||
|
mysqli_stmt_bind_param($stmt, 'i', $invoice_id);
|
||||||
|
mysqli_stmt_execute($stmt);
|
||||||
|
$res = mysqli_stmt_get_result($stmt);
|
||||||
|
if ($res && $row = mysqli_fetch_assoc($res)) $invoices_to_process[] = $row;
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) If we didn't find by custom and invoice_ref is provided, match by description containing invoice_ref
|
||||||
|
if (empty($invoices_to_process) && $invoice_ref) {
|
||||||
|
$like = '%' . mysqli_real_escape_string($db, $invoice_ref) . '%';
|
||||||
|
$sql = "SELECT * FROM `" . $TABLE_PREFIX . "billing_invoices` WHERE status = 'due' AND description LIKE ?";
|
||||||
|
if ($stmt = mysqli_prepare($db, $sql)) {
|
||||||
|
mysqli_stmt_bind_param($stmt, 's', $like);
|
||||||
|
mysqli_stmt_execute($stmt);
|
||||||
|
$res = mysqli_stmt_get_result($stmt);
|
||||||
|
if ($res) {
|
||||||
|
while ($row = mysqli_fetch_assoc($res)) $invoices_to_process[] = $row;
|
||||||
|
}
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) As a fallback, match unpaid invoices by amount (exact match). Useful for simple carts.
|
||||||
|
if (empty($invoices_to_process) && $amount !== null) {
|
||||||
|
$sql = "SELECT * FROM `" . $TABLE_PREFIX . "billing_invoices` WHERE status = 'due' AND amount = ?";
|
||||||
|
if ($stmt = mysqli_prepare($db, $sql)) {
|
||||||
|
mysqli_stmt_bind_param($stmt, 'd', $amount);
|
||||||
|
mysqli_stmt_execute($stmt);
|
||||||
|
$res = mysqli_stmt_get_result($stmt);
|
||||||
|
if ($res) {
|
||||||
|
while ($row = mysqli_fetch_assoc($res)) $invoices_to_process[] = $row;
|
||||||
|
}
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$processed_count = 0;
|
||||||
|
|
||||||
|
foreach ($invoices_to_process as $inv) {
|
||||||
|
$invoice_id = intval($inv['invoice_id']);
|
||||||
|
$order_id = intval($inv['order_id'] ?? 0);
|
||||||
|
$user_id = intval($inv['user_id']);
|
||||||
|
$service_id = intval($inv['service_id'] ?? 0);
|
||||||
|
$home_name = mysqli_real_escape_string($db, $inv['home_name'] ?? '');
|
||||||
|
$ip = intval($inv['ip'] ?? 0);
|
||||||
|
$max_players = intval($inv['max_players'] ?? 0);
|
||||||
|
$qty = intval($inv['qty'] ?? 1);
|
||||||
|
$duration = $inv['invoice_duration'] ?? 'month';
|
||||||
|
$invoice_amount = floatval($inv['amount'] ?? 0);
|
||||||
|
$rcon_pw = mysqli_real_escape_string($db, $inv['remote_control_password'] ?? '');
|
||||||
|
$ftp_pw = mysqli_real_escape_string($db, $inv['ftp_password'] ?? '');
|
||||||
|
|
||||||
|
// Mark invoice as paid
|
||||||
|
$upd = "UPDATE `" . $TABLE_PREFIX . "billing_invoices` SET status='paid', paid_date=?, payment_txid=?, payment_method='paypal' WHERE invoice_id = ? LIMIT 1";
|
||||||
|
if ($stmt = mysqli_prepare($db, $upd)) {
|
||||||
|
mysqli_stmt_bind_param($stmt, 'ssi', $now, $esc_txid, $invoice_id);
|
||||||
|
mysqli_stmt_execute($stmt);
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
$now = date('Y-m-d H:i:s');
|
// If this invoice already has an order -> treat as renewal
|
||||||
$esc_txid = mysqli_real_escape_string($db, (string)$txid);
|
if ($order_id > 0) {
|
||||||
|
// compute months
|
||||||
// Find invoices to mark as paid
|
$months = (stripos($duration, 'year') !== false) ? ($qty * 12) : $qty;
|
||||||
$invoices_to_process = [];
|
// fetch current end_date
|
||||||
|
$get = "SELECT end_date FROM `" . $TABLE_PREFIX . "billing_orders` WHERE order_id = ? LIMIT 1";
|
||||||
// Try to match by custom_id (which should be invoice_id for single-item carts)
|
if ($stmt = mysqli_prepare($db, $get)) {
|
||||||
if ($custom && ctype_digit((string)$custom)) {
|
mysqli_stmt_bind_param($stmt, 'i', $order_id);
|
||||||
$invoice_id = intval($custom);
|
mysqli_stmt_execute($stmt);
|
||||||
$stmt = $db->prepare("SELECT * FROM " . $table_prefix . "billing_invoices WHERE invoice_id = ? AND status = 'due' LIMIT 1");
|
$res = mysqli_stmt_get_result($stmt);
|
||||||
if ($stmt) {
|
if ($res && $row = mysqli_fetch_assoc($res)) {
|
||||||
$stmt->bind_param('i', $invoice_id);
|
$current_end = $row['end_date'] ?? date('Y-m-d H:i:s');
|
||||||
$stmt->execute();
|
|
||||||
$result = $stmt->get_result();
|
|
||||||
if ($result && $row = $result->fetch_assoc()) {
|
|
||||||
$invoices_to_process[] = $row;
|
|
||||||
}
|
|
||||||
$stmt->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no match by custom_id, try matching all unpaid invoices for this payment amount
|
|
||||||
// (This handles multi-item carts where custom_id isn't a single invoice_id)
|
|
||||||
if (empty($invoices_to_process) && $invoice) {
|
|
||||||
// Match by invoice reference from PayPal
|
|
||||||
$esc_invoice = mysqli_real_escape_string($db, $invoice);
|
|
||||||
$query = "SELECT * FROM " . $table_prefix . "billing_invoices WHERE status = 'due' AND description LIKE '%$esc_invoice%'";
|
|
||||||
$result = mysqli_query($db, $query);
|
|
||||||
if ($result) {
|
|
||||||
while ($row = mysqli_fetch_assoc($result)) {
|
|
||||||
$invoices_to_process[] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process each invoice
|
|
||||||
$processed_count = 0;
|
|
||||||
foreach ($invoices_to_process as $inv) {
|
|
||||||
$invoice_id = intval($inv['invoice_id']);
|
|
||||||
$existing_order_id = intval($inv['order_id'] ?? 0);
|
|
||||||
$user_id = intval($inv['user_id']);
|
|
||||||
$service_id = intval($inv['service_id']);
|
|
||||||
$home_name = mysqli_real_escape_string($db, $inv['home_name']);
|
|
||||||
$ip = intval($inv['ip']);
|
|
||||||
$max_players = intval($inv['max_players']);
|
|
||||||
$qty = intval($inv['qty']);
|
|
||||||
$duration = mysqli_real_escape_string($db, $inv['invoice_duration']);
|
|
||||||
$invoice_amount = floatval($inv['amount']);
|
|
||||||
$rcon_pw = mysqli_real_escape_string($db, $inv['remote_control_password'] ?? '');
|
|
||||||
$ftp_pw = mysqli_real_escape_string($db, $inv['ftp_password'] ?? '');
|
|
||||||
|
|
||||||
// Mark invoice as paid
|
|
||||||
$upd_inv = $db->prepare("UPDATE " . $table_prefix . "billing_invoices SET status = 'paid', paid_date = ?, payment_txid = ?, payment_method = 'paypal' WHERE invoice_id = ? LIMIT 1");
|
|
||||||
if ($upd_inv) {
|
|
||||||
$upd_inv->bind_param('ssi', $now, $esc_txid, $invoice_id);
|
|
||||||
$upd_inv->execute();
|
|
||||||
$upd_inv->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a renewal (existing order_id > 0) or new order (order_id = 0)
|
|
||||||
if ($existing_order_id > 0) {
|
|
||||||
// RENEWAL: Extend the existing order's end_date
|
|
||||||
// Calculate months to add
|
|
||||||
$months = 0;
|
|
||||||
$q = intval($qty);
|
|
||||||
$invdur = strtolower(trim($duration));
|
|
||||||
if (strpos($invdur, 'year') !== false) {
|
|
||||||
$months = $q * 12;
|
|
||||||
} else {
|
|
||||||
$months = $q;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current end_date and extend it
|
|
||||||
$getEndDate = "SELECT end_date FROM " . $table_prefix . "billing_orders WHERE order_id = $existing_order_id LIMIT 1";
|
|
||||||
$endDateResult = mysqli_query($db, $getEndDate);
|
|
||||||
if ($endDateResult && mysqli_num_rows($endDateResult) === 1) {
|
|
||||||
$endRow = mysqli_fetch_assoc($endDateResult);
|
|
||||||
$current_end = $endRow['end_date'] ?? date('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
// Extend from current end_date or now (whichever is later)
|
|
||||||
$extend_from = (strtotime($current_end) > time()) ? $current_end : date('Y-m-d H:i:s');
|
$extend_from = (strtotime($current_end) > time()) ? $current_end : date('Y-m-d H:i:s');
|
||||||
$dt = new DateTime($extend_from);
|
$dt = new DateTime($extend_from);
|
||||||
if ($months > 0) {
|
if ($months > 0) $dt->modify('+' . intval($months) . ' months');
|
||||||
$dt->modify('+' . intval($months) . ' months');
|
$new_end = $dt->format('Y-m-d H:i:s');
|
||||||
}
|
$update = "UPDATE `" . $TABLE_PREFIX . "billing_orders` SET end_date = ?, status='paid', payment_txid = ?, paid_ts = ? WHERE order_id = ?";
|
||||||
$new_end_date = $dt->format('Y-m-d H:i:s');
|
if ($u = mysqli_prepare($db, $update)) {
|
||||||
|
mysqli_stmt_bind_param($u, 'sssi', $new_end, $esc_txid, $now, $order_id);
|
||||||
// Update order with new end_date and payment info
|
mysqli_stmt_execute($u);
|
||||||
$updateOrder = "UPDATE " . $table_prefix . "billing_orders
|
mysqli_stmt_close($u);
|
||||||
SET end_date = '$new_end_date', status = 'paid', payment_txid = '$esc_txid', paid_ts = '$now'
|
|
||||||
WHERE order_id = $existing_order_id";
|
|
||||||
if (mysqli_query($db, $updateOrder)) {
|
|
||||||
if (function_exists('site_log_info')) site_log_info('payment_renewal_processed', ['order_id'=>$existing_order_id, 'invoice_id'=>$invoice_id, 'new_end_date'=>$new_end_date]);
|
|
||||||
else error_log("[payment_success] Extended order $existing_order_id to $new_end_date for invoice $invoice_id");
|
|
||||||
$processed_count++;
|
$processed_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create new order
|
||||||
|
$months = (stripos($duration, 'year') !== false) ? ($qty * 12) : $qty;
|
||||||
|
$dt = new DateTime('now');
|
||||||
|
if ($months > 0) $dt->modify('+' . intval($months) . ' months');
|
||||||
|
$end_date = $dt->format('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
// Simpler insert using properly escaped values
|
||||||
|
$esc_home = mysqli_real_escape_string($db, $home_name);
|
||||||
|
$esc_rcon = mysqli_real_escape_string($db, $rcon_pw);
|
||||||
|
$esc_ftp = mysqli_real_escape_string($db, $ftp_pw);
|
||||||
|
$esc_duration = mysqli_real_escape_string($db, $duration);
|
||||||
|
$price = number_format($invoice_amount, 2, '.', '');
|
||||||
|
|
||||||
|
$insert2 = sprintf(
|
||||||
|
"INSERT INTO `%s` (user_id, service_id, home_name, ip, max_players, qty, invoice_duration, price, remote_control_password, ftp_password, status, order_date, end_date, payment_txid, paid_ts) VALUES (%d, %d, '%s', %d, %d, %d, '%s', %s, '%s', '%s', 'paid', '%s', '%s', '%s', '%s')",
|
||||||
|
$TABLE_PREFIX . 'billing_orders',
|
||||||
|
$user_id, $service_id, $esc_home, $ip, $max_players, $qty, $esc_duration, $price, $esc_rcon, $esc_ftp, $now, $end_date, $esc_txid, $now
|
||||||
|
);
|
||||||
|
if (mysqli_query($db, $insert2)) {
|
||||||
|
$new_order_id = mysqli_insert_id($db);
|
||||||
|
$link = "UPDATE `" . $TABLE_PREFIX . "billing_invoices` SET order_id = ? WHERE invoice_id = ?";
|
||||||
|
if ($u = mysqli_prepare($db, $link)) {
|
||||||
|
mysqli_stmt_bind_param($u, 'ii', $new_order_id, $invoice_id);
|
||||||
|
mysqli_stmt_execute($u);
|
||||||
|
mysqli_stmt_close($u);
|
||||||
|
}
|
||||||
|
$processed_count++;
|
||||||
} else {
|
} else {
|
||||||
// NEW ORDER: Create a new order record
|
error_log('[payment_processor] Failed to insert order: ' . mysqli_error($db));
|
||||||
// Calculate months for end_date
|
|
||||||
$months = 0;
|
|
||||||
$q = intval($qty);
|
|
||||||
$invdur = strtolower(trim($duration));
|
|
||||||
if (strpos($invdur, 'year') !== false) {
|
|
||||||
$months = $q * 12;
|
|
||||||
} else {
|
|
||||||
$months = $q;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dt = new DateTime('now');
|
|
||||||
if ($months > 0) {
|
|
||||||
$dt->modify('+' . intval($months) . ' months');
|
|
||||||
}
|
|
||||||
$end_date = $dt->format('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
// Insert order
|
|
||||||
$insertOrder = "INSERT INTO " . $table_prefix . "billing_orders (
|
|
||||||
user_id, service_id, home_name, ip, max_players, qty, invoice_duration,
|
|
||||||
price, remote_control_password, ftp_password, status, order_date, end_date,
|
|
||||||
payment_txid, paid_ts
|
|
||||||
) VALUES (
|
|
||||||
$user_id, $service_id, '$home_name', $ip, $max_players, $qty, '$duration',
|
|
||||||
$invoice_amount, '$rcon_pw', '$ftp_pw', 'paid', '$now', '$end_date',
|
|
||||||
'$esc_txid', '$now'
|
|
||||||
)";
|
|
||||||
|
|
||||||
if (mysqli_query($db, $insertOrder)) {
|
|
||||||
$new_order_id = mysqli_insert_id($db);
|
|
||||||
|
|
||||||
// Link invoice to order
|
|
||||||
$linkInvoice = "UPDATE " . $table_prefix . "billing_invoices SET order_id = $new_order_id WHERE invoice_id = $invoice_id";
|
|
||||||
mysqli_query($db, $linkInvoice);
|
|
||||||
|
|
||||||
if (function_exists('site_log_info')) site_log_info('payment_new_order_created', ['order_id'=>$new_order_id, 'invoice_id'=>$invoice_id, 'end_date'=>$end_date]);
|
|
||||||
else error_log("[payment_success] Created order $new_order_id for invoice $invoice_id");
|
|
||||||
$processed_count++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mysqli_close($db);
|
|
||||||
|
|
||||||
if ($processed_count > 0) {
|
|
||||||
if (function_exists('site_log_info')) site_log_info('payment_success_processed', ['count'=>$processed_count,'invoice'=>$invoice,'custom'=>$custom]);
|
|
||||||
else error_log('[payment_success] Processed ' . $processed_count . ' invoice(s) - invoice=' . $invoice . ' custom=' . $custom);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (function_exists('site_log_warn')) site_log_warn('payment_success_no_match', ['invoice'=>$invoice,'custom'=>$custom]);
|
|
||||||
else error_log('[payment_success] No matching invoices found for invoice=' . $invoice . ' custom=' . $custom);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mysqli_close($db);
|
||||||
|
|
||||||
|
if ($processed_count > 0) {
|
||||||
|
error_log('[payment_processor] Processed ' . $processed_count . ' invoice(s)');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('[payment_processor] No matching invoices processed for record: ' . json_encode($record));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
||||||
|
|
@ -131,12 +131,13 @@ mysqli_close($db);
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-container {
|
.login-container {
|
||||||
background: white;
|
background: #ffffff; /* explicit white */
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.28);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-header {
|
.login-header {
|
||||||
|
|
@ -146,12 +147,12 @@ mysqli_close($db);
|
||||||
|
|
||||||
.login-header h1 {
|
.login-header h1 {
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
color: #333;
|
color: #111111; /* high contrast */
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-header p {
|
.login-header p {
|
||||||
color: #666;
|
color: #4b5563; /* neutral dark gray */
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,25 +163,27 @@ mysqli_close($db);
|
||||||
.form-group label {
|
.form-group label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: #333;
|
color: #111111 !important; /* ensure labels are dark and readable (override external styles) */
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group input {
|
.form-group input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
border: 2px solid #e1e8ed;
|
border: 1px solid #cbd5e1;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
transition: border-color 0.3s;
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
color: #333;
|
color: #111111;
|
||||||
background-color: #fff;
|
background-color: #ffffff;
|
||||||
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group input:focus {
|
.form-group input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #667eea;
|
border-color: #4f46e5;
|
||||||
|
box-shadow: 0 6px 20px rgba(79,70,229,0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-login {
|
.btn-login {
|
||||||
|
|
@ -273,12 +276,12 @@ mysqli_close($db);
|
||||||
<form method="POST" action="login.php">
|
<form method="POST" action="login.php">
|
||||||
<input type="hidden" name="return_to" value="<?php echo htmlspecialchars($return_to_raw); ?>">
|
<input type="hidden" name="return_to" value="<?php echo htmlspecialchars($return_to_raw); ?>">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="ulogin">Username</label>
|
<label>Username</label>
|
||||||
<input type="text" id="ulogin" name="ulogin" required autofocus>
|
<input type="text" id="ulogin" name="ulogin" required autofocus>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="upassword">Password</label>
|
<label>Password</label>
|
||||||
<input type="password" id="upassword" name="upassword" required>
|
<input type="password" id="upassword" name="upassword" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue