filemtime($b); }); $toDelete = count($files) - $retention; for ($i = 0; $i < $toDelete; $i++) { @unlink($files[$i]); } } // --------------------------------------------------------------------------- // Helper: create a backup of the config file; returns backup filename or ''. // --------------------------------------------------------------------------- function billing_admin_create_backup(string $cfgPath, string $bakDir): string { @mkdir($bakDir, 0775, true); $bakName = $bakDir . '/config.inc.php.' . date('Ymd-His') . '.' . bin2hex(random_bytes(4)) . '.bak'; if (!copy($cfgPath, $bakName)) { return ''; } return $bakName; } // --------------------------------------------------------------------------- // Helper: run php -l on a file and return [ok, output]. // --------------------------------------------------------------------------- function billing_admin_lint(string $filePath): array { $phpExec = PHP_BINARY ?: null; if (!$phpExec) { return [true, 'PHP executable not found; skipping syntax check.']; } $cmd = escapeshellarg($phpExec) . ' -l ' . escapeshellarg($filePath); $out = []; $rc = 0; @exec($cmd . ' 2>&1', $out, $rc); return [$rc === 0, implode("\n", $out)]; } // --------------------------------------------------------------------------- // Helper: generate canonical config.inc.php content from an array of values. // DB settings are preserved from the existing file; only billing fields change. // --------------------------------------------------------------------------- function billing_admin_build_config(string $existingContent, array $vals): string { // Extract current DB settings from existing file content so we never lose them. $dbLines = []; foreach (['db_host', 'db_port', 'db_user', 'db_pass', 'db_name', 'table_prefix', 'db_type'] as $var) { if (preg_match('/^\s*\$' . preg_quote($var, '/') . '\s*=.*$/m', $existingContent, $m)) { $dbLines[$var] = rtrim($m[0]); } } $q = static function (string $v): string { return '"' . addslashes($v) . '"'; }; $sandbox = (bool)$vals['paypal_sandbox']; $retention = max(1, min(10, (int)($vals['backup_retention'] ?? 5))); $baseUrl = trim($vals['SITE_BASE_URL'] ?? ''); $bg = trim($vals['SITE_BACKGROUND'] ?? 'images/dark.jpg'); $dataDir = trim($vals['SITE_DATA_DIR'] ?? ''); $dbBlock = ''; foreach (['db_host', 'db_port', 'db_user', 'db_pass', 'db_name', 'table_prefix', 'db_type'] as $var) { if (isset($dbLines[$var])) { $dbBlock .= $dbLines[$var] . "\n"; } } $dataDirLine = ($dataDir !== '' && $dataDir !== 'auto') ? '$SITE_DATA_DIR = ' . $q($dataDir) . ';' : '$SITE_DATA_DIR = realpath(__DIR__ . \'/..\')' . ' . DIRECTORY_SEPARATOR . \'data\';'; return ' Edit Config.' . "\n" . '###############################################' . "\n" . $dbBlock . "\n" . '// Optional: base URL used by admin pages to build absolute image previews.' . "\n" . '// Leave empty to prefer relative paths (local folder).' . "\n" . '$SITE_BASE_URL = ' . $q($baseUrl) . ';' . "\n" . "\n" . '// Normalize: ensure either empty or ends without trailing slash' . "\n" . '$SITE_BASE_URL = trim((string)$SITE_BASE_URL);' . "\n" . "\n" . '// Site-wide background image (relative to site root).' . "\n" . '$SITE_BACKGROUND = ' . $q($bg) . ';' . "\n" . '// Normalize' . "\n" . '$SITE_BACKGROUND = trim((string)$SITE_BACKGROUND);' . "\n" . "\n" . '// Data directory for persisted payment webhook JSON files (relative to repo root)' . "\n" . $dataDirLine . "\n" . "\n" . '// PayPal configuration — set credentials here, never in API files' . "\n" . '$paypal_sandbox = ' . ($sandbox ? 'true' : 'false') . '; // Set to false for live payments' . "\n" . '$paypal_client_id = ' . $q($vals['paypal_client_id'] ?? '') . '; // Your PayPal Client ID' . "\n" . '$paypal_client_secret = ' . $q($vals['paypal_client_secret'] ?? '') . '; // Your PayPal Client Secret' . "\n" . '$paypal_webhook_id = ' . $q($vals['paypal_webhook_id'] ?? '') . '; // Your PayPal Webhook ID' . "\n" . "\n" . '// Admin config backup retention: how many backups to keep (1–10). Default 5.' . "\n" . '$SITE_CONFIG_BACKUP_RETENTION = ' . $retention . ';' . "\n" . '?>' . "\n"; } // --------------------------------------------------------------------------- // Read current values from config (already loaded by config_loader above). // --------------------------------------------------------------------------- $cfgVals = [ 'SITE_BASE_URL' => $SITE_BASE_URL ?? '', 'SITE_BACKGROUND' => $SITE_BACKGROUND ?? 'images/dark.jpg', 'SITE_DATA_DIR' => isset($SITE_DATA_DIR) ? $SITE_DATA_DIR : '', 'paypal_sandbox' => $paypal_sandbox ?? true, 'paypal_client_id' => $paypal_client_id ?? '', 'paypal_client_secret' => $paypal_client_secret ?? '', 'paypal_webhook_id' => $paypal_webhook_id ?? '', 'backup_retention' => $SITE_CONFIG_BACKUP_RETENTION ?? 5, ]; // Detect panel-mode (DB settings are managed by the panel) $panelMode = defined('BILLING_PANEL_CONFIG_PATH'); $panelCfgPath = $panelMode ? BILLING_PANEL_CONFIG_PATH : null; $status = ''; $statusType = 'info'; // 'success' | 'error' | 'info' // --------------------------------------------------------------------------- // POST: Save interactive form // --------------------------------------------------------------------------- if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_form') { $token = $_POST['csrf'] ?? ''; if (!hash_equals($csrf, (string)$token)) { $status = 'Invalid CSRF token.'; $statusType = 'error'; } elseif (!is_writable($cfgPath)) { $status = 'Config file is not writable: ' . h($cfgPath); $statusType = 'error'; } else { // Collect and validate form values $formVals = [ 'SITE_BASE_URL' => trim($_POST['SITE_BASE_URL'] ?? ''), 'SITE_BACKGROUND' => trim($_POST['SITE_BACKGROUND'] ?? 'images/dark.jpg'), 'SITE_DATA_DIR' => trim($_POST['SITE_DATA_DIR'] ?? ''), 'paypal_sandbox' => (($_POST['paypal_sandbox'] ?? 'true') === 'true'), 'paypal_client_id' => trim($_POST['paypal_client_id'] ?? ''), 'paypal_client_secret' => trim($_POST['paypal_client_secret'] ?? ''), 'paypal_webhook_id' => trim($_POST['paypal_webhook_id'] ?? ''), 'backup_retention' => (int)($_POST['backup_retention'] ?? 5), ]; // Validate $validationError = ''; if ($formVals['backup_retention'] < 1 || $formVals['backup_retention'] > 10) { $validationError = 'Backup retention must be a number between 1 and 10.'; } if ($validationError) { $status = $validationError; $statusType = 'error'; } else { $existingContent = (string)file_get_contents($cfgPath); $newContent = billing_admin_build_config($existingContent, $formVals); // Backup before write $bakName = billing_admin_create_backup($cfgPath, $bakDir); if (!$bakName) { $status = 'Failed to create backup. Aborting save.'; $statusType = 'error'; } else { if (file_put_contents($cfgPath, $newContent, LOCK_EX) === false) { $status = 'Failed to write config file.'; $statusType = 'error'; } else { // Syntax check [$lintOk, $lintOut] = billing_admin_lint($cfgPath); if (!$lintOk) { @copy($bakName, $cfgPath); // rollback $status = 'Syntax error in generated config; rolled back. Lint: ' . h($lintOut); $statusType = 'error'; } else { // Apply backup retention $retention = max(1, min(10, $formVals['backup_retention'])); billing_admin_apply_retention($bakDir, $retention); $cfgVals = $formVals; // update displayed values $status = 'Config saved successfully. Backup: ' . basename($bakName); $statusType = 'success'; } } } } } } // --------------------------------------------------------------------------- // POST: Save raw editor // --------------------------------------------------------------------------- if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save_raw') { $token = $_POST['csrf'] ?? ''; if (!hash_equals($csrf, (string)$token)) { $status = 'Invalid CSRF token.'; $statusType = 'error'; } elseif (!is_writable($cfgPath)) { $status = 'Config file is not writable: ' . h($cfgPath); $statusType = 'error'; } else { $newRaw = $_POST['config_text'] ?? ''; if (strpos(trim($newRaw), ' Admin — Edit Config

Edit Site Config

⚠️

Site Settings

ℹ️ Panel-integrated mode. Database settings are managed by the panel and synced automatically from . They are shown below for reference only.
Managed by the panel config. Edit the panel's includes/config.inc.php to change.
Full base URL without trailing slash (e.g. https://gameservers.world). Leave empty to use relative paths.
Path to background image relative to the billing site root (e.g. images/dark.jpg).
Absolute path where payment webhook JSON files are stored. Leave empty to use the default: modules/billing/data/.

PayPal Configuration

Use Sandbox for testing, Live for real payments. Make sure you use the matching Client ID and Secret for the selected mode.
Your PayPal app Client ID. Safe to expose in browser JS. Found in your PayPal Developer Dashboard under your app credentials.
Your PayPal app Client Secret. Server-side only — never sent to the browser.
Webhook ID from your PayPal app (used for webhook signature verification). Leave empty to skip signature verification (not recommended for production).

Backup Settings

Number of config backups to keep (1–10). The oldest backup beyond this limit is deleted after each save. Backups are stored in .

Advanced: Raw Config Editor

⚠️ Warning: Manually editing the raw PHP file can break the billing website if you introduce a syntax error or remove required variables. A backup is created automatically before saving, and a syntax check runs after. The file is rolled back if a parse error is detected.

Backup directory:
backup(s) stored. Most recent:
No backups yet.