moved website outside of panel folder
This commit is contained in:
parent
92ac778956
commit
08f07dca97
10328 changed files with 90 additions and 501 deletions
28
Website/includes/README.md
Normal file
28
Website/includes/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Website Includes Directory
|
||||
|
||||
This directory contains configuration and shared files for the standalone _website folder.
|
||||
|
||||
## config.inc.php
|
||||
|
||||
Central database configuration file for the website. This file contains the database connection settings that are used by all website PHP files through the `db.php` file.
|
||||
|
||||
**Important:** The values in this file should match the panel's database configuration in `/includes/config.inc.php` to ensure the website can access the same database as the panel.
|
||||
|
||||
### Configuration Variables
|
||||
|
||||
- `$db_host` - Database server hostname
|
||||
- `$db_user` - Database username
|
||||
- `$db_pass` - Database password
|
||||
- `$db_name` - Database name
|
||||
- `$table_prefix` - Table prefix (default: "ogp_")
|
||||
- `$db_type` - Database type (default: "mysql")
|
||||
|
||||
### Usage
|
||||
|
||||
The website files include `db.php`, which in turn loads this configuration file:
|
||||
|
||||
```php
|
||||
require_once('db.php'); // db.php loads includes/config.inc.php
|
||||
```
|
||||
|
||||
This centralizes database credentials in one place, making the website easier to configure and maintain as a standalone site.
|
||||
50
Website/includes/admin_auth.php
Normal file
50
Website/includes/admin_auth.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
// Admin authorization include — include early (before output) on admin pages
|
||||
require_once(__DIR__ . '/session_bridge.php');
|
||||
|
||||
// If not logged in, redirect to login
|
||||
if (empty($_SESSION['website_user_id'])) {
|
||||
$loginUrl = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/\\') . '/login.php';
|
||||
$returnTo = $_SERVER['SCRIPT_NAME'] ?? '/';
|
||||
header('Location: ' . $loginUrl . '?return_to=' . urlencode($returnTo));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Require DB config and check role live from panel DB
|
||||
require_once(__DIR__ . '/config_loader.php');
|
||||
|
||||
// Variables from config.inc.php (helps IDEs understand scope)
|
||||
/** @var string $db_host Database host */
|
||||
/** @var string $db_user Database user */
|
||||
/** @var string $db_pass Database password */
|
||||
/** @var string $db_name Database name */
|
||||
/** @var string $table_prefix Table prefix for database tables */
|
||||
|
||||
$auth_db_port = isset($db_port) ? (int)$db_port : null;
|
||||
// Use a local connection variable so we don't clash with pages that also use $db
|
||||
$auth_db = @mysqli_connect($db_host, $db_user, $db_pass, $db_name, $auth_db_port);
|
||||
if (!$auth_db) {
|
||||
// If DB unavailable, deny access gracefully
|
||||
$loginUrl = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/\\') . '/login.php';
|
||||
header('Location: ' . $loginUrl);
|
||||
exit();
|
||||
}
|
||||
|
||||
$uid = intval($_SESSION['website_user_id']);
|
||||
$role = '';
|
||||
$res = mysqli_query($auth_db, "SELECT users_role FROM {$table_prefix}users WHERE user_id = $uid LIMIT 1");
|
||||
if ($res && mysqli_num_rows($res) === 1) {
|
||||
$row = mysqli_fetch_assoc($res);
|
||||
$role = (string)($row['users_role'] ?? '');
|
||||
}
|
||||
mysqli_close($auth_db);
|
||||
|
||||
if (strtolower($role) !== 'admin') {
|
||||
// Not an admin — redirect to login or home
|
||||
$loginUrl = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/\\') . '/login.php';
|
||||
header('Location: ' . $loginUrl);
|
||||
exit();
|
||||
}
|
||||
|
||||
// If we reach here, user is an admin
|
||||
?>
|
||||
22
Website/includes/cart_helper.php
Normal file
22
Website/includes/cart_helper.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
// Helper to read cart items stored in session and return count
|
||||
// Non-invasive: reads $_SESSION['cart'] if present and returns total quantity or items count
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
@session_start();
|
||||
}
|
||||
|
||||
function get_cart_count() {
|
||||
if (!isset($_SESSION['cart']) || !is_array($_SESSION['cart'])) {
|
||||
return 0;
|
||||
}
|
||||
$count = 0;
|
||||
foreach ((array)$_SESSION['cart'] as $item) {
|
||||
if (is_array($item) && isset($item['quantity'])) {
|
||||
$count += (int) $item['quantity'];
|
||||
} else {
|
||||
$count += 1;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
59
Website/includes/config.example.php
Normal file
59
Website/includes/config.example.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
###############################################
|
||||
# Billing Website Configuration Example
|
||||
#
|
||||
# Copy this file to config.inc.php and fill in
|
||||
# your actual settings.
|
||||
# config.inc.php is excluded from version control.
|
||||
#
|
||||
# This file is used by modules/billing/ both as a
|
||||
# standalone website and as a panel-integrated module.
|
||||
# The billing module reads ONLY this file — it does NOT
|
||||
# depend on the parent panel's includes/config.inc.php.
|
||||
###############################################
|
||||
|
||||
# --- Database connection ---
|
||||
$db_host = "localhost";
|
||||
$db_port = "3306"; // MySQL port (default 3306)
|
||||
$db_user = "your_db_user";
|
||||
$db_pass = "your_db_password";
|
||||
$db_name = "your_db_name"; // Panel database name (e.g. "gsp" or "panel")
|
||||
$table_prefix = "gsp_"; // Table prefix used in the panel database
|
||||
$db_type = "mysql";
|
||||
|
||||
# --- Site base URL ---
|
||||
# Full base URL WITHOUT trailing slash. Leave empty to use relative paths.
|
||||
# Example: "https://gameservers.world" or "https://your-domain.com"
|
||||
$SITE_BASE_URL = '';
|
||||
|
||||
# --- Background image ---
|
||||
# Relative to the billing site root.
|
||||
$SITE_BACKGROUND = 'images/dark.jpg';
|
||||
|
||||
# --- Data directory ---
|
||||
# Absolute path where payment webhook JSON files are stored.
|
||||
# Default: modules/billing/data/
|
||||
$SITE_DATA_DIR = realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data';
|
||||
|
||||
# --- PayPal settings ---
|
||||
# Mode: 'sandbox' for testing, 'live' for real payments.
|
||||
$paypal_mode = 'sandbox';
|
||||
|
||||
# Sandbox credentials (PayPal Developer Dashboard → sandbox app)
|
||||
$paypal_sandbox_client_id = ''; // e.g. AfvY_...
|
||||
$paypal_sandbox_client_secret = ''; // Keep server-side only
|
||||
$paypal_sandbox_webhook_id = ''; // Set after registering webhook in PayPal
|
||||
|
||||
# Live credentials (leave blank until ready for production)
|
||||
$paypal_live_client_id = '';
|
||||
$paypal_live_client_secret = '';
|
||||
$paypal_live_webhook_id = '';
|
||||
|
||||
# Webhook path (relative to billing site root, must start with /)
|
||||
# Full public URL = $SITE_BASE_URL + $paypal_webhook_path
|
||||
# Example full URL: https://gameservers.world/paypal/webhook.php
|
||||
$paypal_webhook_path = '/paypal/webhook.php';
|
||||
|
||||
# --- Admin config backup retention ---
|
||||
# Number of config backups to keep (1–10). Oldest backups beyond this limit are deleted.
|
||||
$SITE_CONFIG_BACKUP_RETENTION = 5;
|
||||
360
Website/includes/config_loader.php
Normal file
360
Website/includes/config_loader.php
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
<?php
|
||||
/**
|
||||
* Billing config loader
|
||||
*
|
||||
* Load order:
|
||||
* 1. modules/billing/includes/config.inc.php — always loaded first; contains billing-specific
|
||||
* settings (PayPal credentials, SITE_BASE_URL, SITE_BACKGROUND, SITE_DATA_DIR, backup
|
||||
* retention, and default DB settings for standalone installs).
|
||||
* 2. <panel_root>/includes/config.inc.php — read via regex (no side-effects) when the
|
||||
* billing module is installed inside a GSP panel tree. DB variables extracted from the
|
||||
* panel config override the billing config DB variables in memory so that the module
|
||||
* always connects to the active panel database.
|
||||
*
|
||||
* Panel-child detection:
|
||||
* Walk up from modules/billing/includes/ looking for the pattern
|
||||
* <ancestor>/includes/config.inc.php that contains the GSP panel DB variables
|
||||
* ($db_host, $db_user, $db_name, $table_prefix). Stop after six levels.
|
||||
*
|
||||
* Config sync:
|
||||
* If the panel config DB variables differ from the billing config file on disk, the loader
|
||||
* updates only the DB variable lines in billing/includes/config.inc.php so that subsequent
|
||||
* page loads (and standalone tools) always see current credentials. The sync only runs when
|
||||
* the file is writable. If it cannot write, a non-fatal admin-visible warning is set in
|
||||
* $billing_config_warning.
|
||||
*
|
||||
* Standalone installs:
|
||||
* When no panel config is found the billing config is used as-is; standalone mode is fully
|
||||
* supported.
|
||||
*/
|
||||
if (defined('BILLING_CONFIG_LOADED')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: extract DB variable values from a PHP config file without including it.
|
||||
// Uses regex on the raw file text so there are no side-effects.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (!function_exists('_billing_extract_db_vars_from_file')) {
|
||||
function _billing_extract_db_vars_from_file(string $path): array
|
||||
{
|
||||
$content = @file_get_contents($path);
|
||||
if ($content === false) {
|
||||
return [];
|
||||
}
|
||||
$result = [];
|
||||
foreach (['db_host', 'db_port', 'db_user', 'db_pass', 'db_name', 'table_prefix', 'db_type'] as $var) {
|
||||
// Match: $varname = "value"; or $varname="value"; (single or double quotes, no escaped quotes)
|
||||
// Note: credentials containing escaped quotes or special chars are not supported by this
|
||||
// regex-based extractor — use var_export() to write values and keep creds simple.
|
||||
if (preg_match('/^\s*\$' . preg_quote($var, '/') . '\s*=\s*"([^"]*)"/m', $content, $m) ||
|
||||
preg_match('/^\s*\$' . preg_quote($var, '/') . "\s*=\s*'([^']*)'/m", $content, $m)) {
|
||||
$result[$var] = $m[1];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: update DB variable lines in the billing config file without touching
|
||||
// any other settings. Returns true when the file was updated, false otherwise.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (!function_exists('_billing_sync_db_vars_to_file')) {
|
||||
function _billing_sync_db_vars_to_file(string $filePath, array $panelVars): bool
|
||||
{
|
||||
if (!is_writable($filePath)) {
|
||||
return false;
|
||||
}
|
||||
$content = file_get_contents($filePath);
|
||||
if ($content === false) {
|
||||
return false;
|
||||
}
|
||||
$changed = false;
|
||||
foreach (['db_host', 'db_port', 'db_user', 'db_pass', 'db_name', 'table_prefix', 'db_type'] as $var) {
|
||||
if (!array_key_exists($var, $panelVars)) {
|
||||
continue;
|
||||
}
|
||||
$newVal = $panelVars[$var];
|
||||
// Match any existing assignment for this var (double or single quotes, no escaped quotes)
|
||||
// Use var_export() to produce the replacement value so special characters are handled
|
||||
// correctly (var_export produces a valid PHP string literal).
|
||||
$pattern = '/^(\s*\$' . preg_quote($var, '/') . '\s*=\s*)["\'][^"\']*["\'](.*)$/m';
|
||||
$exportedVal = var_export($newVal, true); // produces 'value' with proper escaping
|
||||
$newLine = '${1}' . str_replace('\\', '\\\\', $exportedVal) . '${2}';
|
||||
$updated = preg_replace($pattern, $newLine, $content, 1, $count);
|
||||
if ($count > 0 && $updated !== $content) {
|
||||
$content = (string)$updated;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
if (!$changed) {
|
||||
return false;
|
||||
}
|
||||
return file_put_contents($filePath, $content, LOCK_EX) !== false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: locate the panel config by walking up ancestor directories.
|
||||
// Returns an absolute path when found, or null.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (!function_exists('_billing_find_panel_config')) {
|
||||
function _billing_find_panel_config(string $startDir): ?string
|
||||
{
|
||||
$dir = realpath($startDir);
|
||||
if ($dir === false) {
|
||||
return null;
|
||||
}
|
||||
// Walk up at most 6 levels (covers: includes/ → billing/ → modules/ → panel_root/)
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$candidate = $dir . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'config.inc.php';
|
||||
if (is_readable($candidate)) {
|
||||
// Confirm it is a GSP/panel config by looking for $db_host and $table_prefix
|
||||
$content = @file_get_contents($candidate);
|
||||
if ($content !== false &&
|
||||
strpos($content, '$db_host') !== false &&
|
||||
strpos($content, '$table_prefix') !== false) {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
$parent = dirname($dir);
|
||||
if ($parent === $dir) {
|
||||
break; // reached filesystem root
|
||||
}
|
||||
$dir = $parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 1: Load the billing config (always — it holds billing-specific settings).
|
||||
// ---------------------------------------------------------------------------
|
||||
$billing_config_warning = null; // surfaced to admin pages when non-null
|
||||
|
||||
$localConfig = __DIR__ . '/config.inc.php';
|
||||
|
||||
if (!is_readable($localConfig)) {
|
||||
// No billing config found — render an informative error for the admin.
|
||||
$message = "GSP Billing module cannot find modules/billing/includes/config.inc.php.\n";
|
||||
$message .= "Expected: " . $localConfig . "\n";
|
||||
$message .= "\nCreate the file from the example (config.example.php) and fill in your settings.\n";
|
||||
if (!headers_sent()) {
|
||||
header('Content-Type: text/plain; charset=UTF-8', true, 500);
|
||||
}
|
||||
echo $message;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once $localConfig;
|
||||
|
||||
if (!defined('BILLING_CONFIG_PATH')) {
|
||||
define('BILLING_CONFIG_PATH', $localConfig);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 2: Child-of-panel detection.
|
||||
// ---------------------------------------------------------------------------
|
||||
$_billing_panel_config = _billing_find_panel_config(dirname(__DIR__, 2));
|
||||
|
||||
if ($_billing_panel_config !== null) {
|
||||
// Found a panel config — extract its DB variables (no side-effects).
|
||||
$panelDbVars = _billing_extract_db_vars_from_file($_billing_panel_config);
|
||||
|
||||
if (!empty($panelDbVars)) {
|
||||
// Override DB settings in the current scope with panel values.
|
||||
foreach ($panelDbVars as $_bk => $_bv) {
|
||||
$$_bk = $_bv;
|
||||
}
|
||||
unset($_bk, $_bv);
|
||||
|
||||
// Record which panel config was found (admin pages may display this).
|
||||
if (!defined('BILLING_PANEL_CONFIG_PATH')) {
|
||||
define('BILLING_PANEL_CONFIG_PATH', $_billing_panel_config);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Step 3: Config sync — keep billing config.inc.php DB vars in sync.
|
||||
// Only rewrite the file when the on-disk values actually differ so that
|
||||
// we never touch the file on normal page loads where nothing changed.
|
||||
// -------------------------------------------------------------------
|
||||
$diskVars = _billing_extract_db_vars_from_file($localConfig);
|
||||
$needsSync = false;
|
||||
foreach ($panelDbVars as $k => $v) {
|
||||
if (!array_key_exists($k, $diskVars) || $diskVars[$k] !== $v) {
|
||||
$needsSync = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($needsSync) {
|
||||
if (!_billing_sync_db_vars_to_file($localConfig, $panelDbVars)) {
|
||||
// Non-fatal: show admin warning; runtime DB vars are already overridden above.
|
||||
$billing_config_warning =
|
||||
'Panel DB settings differ from billing/includes/config.inc.php but the file '
|
||||
. 'is not writable. The billing module will use the panel DB settings for this '
|
||||
. 'request, but consider updating file permissions or manually editing config.inc.php '
|
||||
. 'to match the panel config at: ' . $_billing_panel_config;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset($_billing_panel_config, $panelDbVars, $diskVars, $needsSync, $k, $v);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 4: Apply safe defaults for billing-specific variables that may be absent
|
||||
// in older config files (never overwrite values already set by the config).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// --- PayPal mode (new-style) ---
|
||||
// Backward compat: if $paypal_sandbox was set (old config) and $paypal_mode is absent,
|
||||
// derive $paypal_mode from $paypal_sandbox.
|
||||
if (!isset($paypal_mode)) {
|
||||
if (isset($paypal_sandbox)) {
|
||||
$paypal_mode = $paypal_sandbox ? 'sandbox' : 'live';
|
||||
} else {
|
||||
$paypal_mode = 'sandbox';
|
||||
}
|
||||
}
|
||||
$paypal_mode = (strtolower((string)$paypal_mode) === 'live') ? 'live' : 'sandbox';
|
||||
|
||||
// --- Sandbox credentials ---
|
||||
if (!isset($paypal_sandbox_client_id)) {
|
||||
// Backward compat: if old $paypal_client_id was set while in sandbox mode, use it
|
||||
$paypal_sandbox_client_id = (isset($paypal_client_id) && $paypal_mode === 'sandbox') ? $paypal_client_id : '';
|
||||
}
|
||||
if (!isset($paypal_sandbox_client_secret)) {
|
||||
$paypal_sandbox_client_secret = (isset($paypal_client_secret) && $paypal_mode === 'sandbox') ? $paypal_client_secret : '';
|
||||
}
|
||||
if (!isset($paypal_sandbox_webhook_id)) {
|
||||
$paypal_sandbox_webhook_id = (isset($paypal_webhook_id) && $paypal_mode === 'sandbox') ? $paypal_webhook_id : '';
|
||||
}
|
||||
|
||||
// --- Live credentials ---
|
||||
if (!isset($paypal_live_client_id)) {
|
||||
$paypal_live_client_id = (isset($paypal_client_id) && $paypal_mode === 'live') ? $paypal_client_id : '';
|
||||
}
|
||||
if (!isset($paypal_live_client_secret)) {
|
||||
$paypal_live_client_secret = (isset($paypal_client_secret) && $paypal_mode === 'live') ? $paypal_client_secret : '';
|
||||
}
|
||||
if (!isset($paypal_live_webhook_id)) {
|
||||
$paypal_live_webhook_id = (isset($paypal_webhook_id) && $paypal_mode === 'live') ? $paypal_webhook_id : '';
|
||||
}
|
||||
|
||||
// --- Legacy compatibility shims (read-only derived values) ---
|
||||
// Keep old variable names populated so any code that still reads $paypal_sandbox,
|
||||
// $paypal_client_id, $paypal_client_secret, $paypal_webhook_id keeps working.
|
||||
if (!isset($paypal_sandbox)) {
|
||||
$paypal_sandbox = ($paypal_mode !== 'live');
|
||||
}
|
||||
if (!isset($paypal_client_id)) {
|
||||
$paypal_client_id = ($paypal_mode === 'live') ? $paypal_live_client_id : $paypal_sandbox_client_id;
|
||||
}
|
||||
if (!isset($paypal_client_secret)) {
|
||||
$paypal_client_secret = ($paypal_mode === 'live') ? $paypal_live_client_secret : $paypal_sandbox_client_secret;
|
||||
}
|
||||
if (!isset($paypal_webhook_id)) {
|
||||
$paypal_webhook_id = ($paypal_mode === 'live') ? $paypal_live_webhook_id : $paypal_sandbox_webhook_id;
|
||||
}
|
||||
|
||||
// --- Webhook path ---
|
||||
if (!isset($paypal_webhook_path) || (string)$paypal_webhook_path === '') {
|
||||
$paypal_webhook_path = '/paypal/webhook.php';
|
||||
}
|
||||
// Ensure webhook path starts with /
|
||||
$paypal_webhook_path = '/' . ltrim((string)$paypal_webhook_path, '/');
|
||||
|
||||
// --- Site settings ---
|
||||
if (!isset($SITE_BASE_URL)) {
|
||||
$SITE_BASE_URL = '';
|
||||
}
|
||||
$SITE_BASE_URL = rtrim(trim((string)$SITE_BASE_URL), '/');
|
||||
|
||||
if (!isset($SITE_BACKGROUND)) {
|
||||
$SITE_BACKGROUND = 'images/dark.jpg';
|
||||
}
|
||||
if (!isset($SITE_DATA_DIR)) {
|
||||
$SITE_DATA_DIR = realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data';
|
||||
}
|
||||
if (!isset($SITE_CONFIG_BACKUP_RETENTION) || !is_int($SITE_CONFIG_BACKUP_RETENTION)) {
|
||||
$SITE_CONFIG_BACKUP_RETENTION = 5;
|
||||
}
|
||||
$SITE_CONFIG_BACKUP_RETENTION = max(1, min(10, (int)$SITE_CONFIG_BACKUP_RETENTION));
|
||||
|
||||
define('BILLING_CONFIG_LOADED', true);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PayPal helper functions — use these everywhere instead of reading globals.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
if (!function_exists('gsp_paypal_get_mode')) {
|
||||
function gsp_paypal_get_mode(): string
|
||||
{
|
||||
return $GLOBALS['paypal_mode'] === 'live' ? 'live' : 'sandbox';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_is_sandbox')) {
|
||||
function gsp_paypal_is_sandbox(): bool
|
||||
{
|
||||
return gsp_paypal_get_mode() !== 'live';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_get_client_id')) {
|
||||
function gsp_paypal_get_client_id(): string
|
||||
{
|
||||
if (gsp_paypal_is_sandbox()) {
|
||||
return (string)($GLOBALS['paypal_sandbox_client_id'] ?? '');
|
||||
}
|
||||
return (string)($GLOBALS['paypal_live_client_id'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_get_client_secret')) {
|
||||
function gsp_paypal_get_client_secret(): string
|
||||
{
|
||||
if (gsp_paypal_is_sandbox()) {
|
||||
return (string)($GLOBALS['paypal_sandbox_client_secret'] ?? '');
|
||||
}
|
||||
return (string)($GLOBALS['paypal_live_client_secret'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_get_webhook_id')) {
|
||||
function gsp_paypal_get_webhook_id(): string
|
||||
{
|
||||
if (gsp_paypal_is_sandbox()) {
|
||||
return (string)($GLOBALS['paypal_sandbox_webhook_id'] ?? '');
|
||||
}
|
||||
return (string)($GLOBALS['paypal_live_webhook_id'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_get_api_base')) {
|
||||
function gsp_paypal_get_api_base(): string
|
||||
{
|
||||
return gsp_paypal_is_sandbox()
|
||||
? 'https://api-m.sandbox.paypal.com'
|
||||
: 'https://api-m.paypal.com';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_get_webhook_path')) {
|
||||
function gsp_paypal_get_webhook_path(): string
|
||||
{
|
||||
$path = (string)($GLOBALS['paypal_webhook_path'] ?? '/paypal/webhook.php');
|
||||
return '/' . ltrim($path, '/');
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_paypal_get_full_webhook_url')) {
|
||||
function gsp_paypal_get_full_webhook_url(): string
|
||||
{
|
||||
$base = rtrim((string)($GLOBALS['SITE_BASE_URL'] ?? ''), '/');
|
||||
$path = gsp_paypal_get_webhook_path();
|
||||
return $base . $path;
|
||||
}
|
||||
}
|
||||
21
Website/includes/footer.php
Normal file
21
Website/includes/footer.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
// Simple footer include
|
||||
?>
|
||||
<footer class="gsw-footer">
|
||||
<div class="container-wide">
|
||||
<a href="privacy.php">Privacy</a> | <a href="tos.php">TOS</a> | <a href="server_status.php">Server Status</a> | <a href="https://worlddomination.dev" target="_blank" rel="noopener">Worlddomination.dev</a>
|
||||
|
||||
</div>
|
||||
<div class="last-updated" style="color:#999;font-size:0.9em;">
|
||||
<?php
|
||||
// Include the canonical billing timestamp text file (plain text).
|
||||
$billing_ts = __DIR__ . '/../timestamp.txt';
|
||||
if (file_exists($billing_ts)) {
|
||||
echo trim(file_get_contents($billing_ts));
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- close site wrapper started in menu.php -->
|
||||
</div>
|
||||
33
Website/includes/log.php
Normal file
33
Website/includes/log.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
// Simple site logging helper for _website
|
||||
// Writes to _website/logs/YYYY-MM-DD.log
|
||||
|
||||
function site_log_dir(){
|
||||
$d = __DIR__ . '/../logs';
|
||||
if (!is_dir($d)) @mkdir($d, 0775, true);
|
||||
return $d;
|
||||
}
|
||||
|
||||
function site_log_filename(){
|
||||
$d = site_log_dir();
|
||||
return rtrim($d, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log';
|
||||
}
|
||||
|
||||
function site_log($level, $message, $meta = null){
|
||||
$ts = date('c');
|
||||
$lvl = strtoupper(substr((string)$level,0,10));
|
||||
$line = "[{$ts}] [{$lvl}] {$message}";
|
||||
if ($meta !== null) {
|
||||
if (!is_string($meta)) $meta = json_encode($meta, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
|
||||
$line .= ' | ' . $meta;
|
||||
}
|
||||
$file = site_log_filename();
|
||||
@file_put_contents($file, $line . PHP_EOL, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
// Convenience wrappers
|
||||
function site_log_info($msg, $meta=null){ site_log('INFO', $msg, $meta); }
|
||||
function site_log_warn($msg, $meta=null){ site_log('WARN', $msg, $meta); }
|
||||
function site_log_error($msg, $meta=null){ site_log('ERROR', $msg, $meta); }
|
||||
|
||||
?>
|
||||
9
Website/includes/login_required.php
Normal file
9
Website/includes/login_required.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
require_once(__DIR__ . '/session_bridge.php');
|
||||
|
||||
// Debugging mode: do not enforce login redirects. Pages can load without authentication.
|
||||
// If you later want to re-enable, restore the original redirect behavior.
|
||||
// (This file intentionally left as a no-op during debugging.)
|
||||
return;
|
||||
?>
|
||||
|
||||
158
Website/includes/menu.php
Normal file
158
Website/includes/menu.php
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
/**
|
||||
* Navigation Menu for GameServers.World Website
|
||||
* This file provides a consistent navigation menu across all website pages
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/session_bridge.php');
|
||||
|
||||
if (!function_exists('billing_nav_escape')) {
|
||||
function billing_nav_escape($value) {
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
$nav_prefix = '';
|
||||
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||
if (is_string($scriptName) && $scriptName !== '') {
|
||||
if (preg_match('#/modules/billing/(.*)$#', $scriptName, $match)) {
|
||||
// Panel-embedded or non-root deployment: depth relative to modules/billing/
|
||||
$subPath = $match[1];
|
||||
if ($subPath !== '') {
|
||||
$depth = substr_count($subPath, '/');
|
||||
if ($depth > 0) {
|
||||
$nav_prefix = str_repeat('../', $depth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Root deployment: compute prefix from the script's directory depth so that
|
||||
// links such as index.php correctly resolve to /index.php even when the
|
||||
// current page lives in a subdirectory like /docs/.
|
||||
$dir = dirname($scriptName);
|
||||
if ($dir !== '/' && $dir !== '' && $dir !== '.') {
|
||||
$segments = array_filter(explode('/', $dir), static function ($s) { return $s !== ''; });
|
||||
if (!empty($segments)) {
|
||||
$nav_prefix = str_repeat('../', count($segments));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$nav_prefix = $nav_prefix ?: '';
|
||||
|
||||
// Check login status
|
||||
// Primary check uses website_user_id, but some remote deployments may only set website_username.
|
||||
// Treat presence of website_username as a fallback to consider the user logged in for UI purposes.
|
||||
$is_logged_in = (isset($_SESSION['website_user_id']) && !empty($_SESSION['website_user_id'])) || (isset($_SESSION['website_username']) && !empty($_SESSION['website_username']));
|
||||
$username = '';
|
||||
if (isset($_SESSION['website_username']) && !empty($_SESSION['website_username'])) {
|
||||
$username = htmlspecialchars($_SESSION['website_username']);
|
||||
} elseif (isset($_SESSION['website_user_id']) && !empty($_SESSION['website_user_id'])) {
|
||||
// fetch username lazily if only user_id is present
|
||||
$username = htmlspecialchars((string)($_SESSION['website_user_id']));
|
||||
}
|
||||
|
||||
// Determine if the logged-in user is an admin by checking the panel DB
|
||||
$is_admin = false;
|
||||
if ($is_logged_in) {
|
||||
// load DB credentials
|
||||
require_once(__DIR__ . '/config_loader.php');
|
||||
|
||||
// Variables from config.inc.php (helps IDEs understand scope)
|
||||
/** @var string $db_host Database host */
|
||||
/** @var string $db_user Database user */
|
||||
/** @var string $db_pass Database password */
|
||||
/** @var string $db_name Database name */
|
||||
/** @var string $table_prefix Table prefix for database tables */
|
||||
|
||||
// Prefer reusing an existing $db if present, otherwise open a local connection
|
||||
$menu_db = null;
|
||||
$menu_db_opened = false;
|
||||
// Only reuse $db if it is still an open (non-closed) connection.
|
||||
// mysqli_thread_id() returns 0 on a closed handle; no @ needed since instanceof
|
||||
// already guarantees $db is a mysqli object.
|
||||
if (isset($db) && $db instanceof mysqli && mysqli_thread_id($db)) {
|
||||
$menu_db = $db;
|
||||
} else {
|
||||
$menu_db_port = isset($db_port) ? (int)$db_port : null;
|
||||
$menu_db = @mysqli_connect($db_host, $db_user, $db_pass, $db_name, $menu_db_port);
|
||||
$menu_db_opened = true;
|
||||
}
|
||||
|
||||
if ($menu_db) {
|
||||
$uid = null;
|
||||
if (isset($_SESSION['website_user_id']) && !empty($_SESSION['website_user_id'])) {
|
||||
$uid = intval($_SESSION['website_user_id']);
|
||||
}
|
||||
if (!empty($uid)) {
|
||||
$res = mysqli_query($menu_db, "SELECT users_role FROM {$table_prefix}users WHERE user_id = $uid LIMIT 1");
|
||||
if ($res && mysqli_num_rows($res) === 1) {
|
||||
$row = mysqli_fetch_assoc($res);
|
||||
if (strtolower((string)($row['users_role'] ?? '')) === 'admin') $is_admin = true;
|
||||
}
|
||||
}
|
||||
if ($menu_db_opened) {
|
||||
if (function_exists('billing_maybe_close_db')) {
|
||||
billing_maybe_close_db($menu_db);
|
||||
} else {
|
||||
@mysqli_close($menu_db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<link rel="stylesheet" href="<?php echo billing_nav_escape($nav_prefix . 'css/header.css'); ?>">
|
||||
|
||||
<!-- site wrapper for scoping styles -->
|
||||
<div id="gsw-site">
|
||||
|
||||
<div class="gsw-header">
|
||||
<div class="gsw-header-top">
|
||||
<div class="gsw-header-left">
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'index.php'); ?>" class="gsw-logo-link">
|
||||
<img src="<?php echo billing_nav_escape($nav_prefix . 'images/logo-sm.png'); ?>" alt="GameServers.World" class="gsw-logo">
|
||||
<span class="gsw-site-name">GameServers.World</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="gsw-header-right">
|
||||
<!-- Always show the user-info area (may be empty for guests) and an auth button -->
|
||||
<?php
|
||||
// Build a safe absolute return_to under this site so auth redirects stay within this module
|
||||
$current = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
$return_to_param = $current;
|
||||
?>
|
||||
<?php if ($is_logged_in): ?>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'my_account.php'); ?>" class="gsw-user-info">Welcome, <?php echo $username; ?></a>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'logout.php?return_to=' . urlencode($return_to_param)); ?>" class="gsw-header-btn">Logout</a>
|
||||
<?php else: ?>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'login.php?return_to=' . urlencode($return_to_param)); ?>" class="gsw-header-btn">Login</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gsw-header-bottom">
|
||||
<nav class="gsw-header-nav">
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'index.php'); ?>" class="gsw-nav-link">Home</a>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'serverlist.php'); ?>" class="gsw-nav-link">Game Servers</a>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'docs.php'); ?>" class="gsw-nav-link">Documentation</a>
|
||||
<?php if ($is_logged_in): ?>
|
||||
<!-- My Account as a regular nav link, not a prominent button -->
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'my_account.php'); ?>" class="gsw-nav-link">My Account</a>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'cart.php'); ?>" class="gsw-nav-link">Cart
|
||||
<?php
|
||||
$cart_count = 0;
|
||||
if (file_exists(__DIR__ . '/cart_helper.php')) {
|
||||
include_once __DIR__ . '/cart_helper.php';
|
||||
if (function_exists('get_cart_count')) $cart_count = (int) get_cart_count();
|
||||
}
|
||||
if ($cart_count > 0) echo ' <span class="cart-badge">' . intval($cart_count) . '</span>';
|
||||
?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ($is_logged_in && $is_admin): ?>
|
||||
<a href="<?php echo billing_nav_escape($nav_prefix . 'admin.php'); ?>" class="gsw-nav-link">Admin</a>
|
||||
<?php endif; ?>
|
||||
<a href="http://panel.iaregamer.com" class="gsw-nav-link" target="_blank">Control Panel</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
115
Website/includes/panel_bridge.php
Normal file
115
Website/includes/panel_bridge.php
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
/**
|
||||
* Panel bridge helpers for the storefront.
|
||||
* Provides access to the native OGPDatabase layer, settings, and XML parsers
|
||||
* without duplicating the panel bootstrap logic in each script.
|
||||
*/
|
||||
|
||||
if (!function_exists('billing_panel_bootstrap')) {
|
||||
/**
|
||||
* Initialize the panel runtime and return shared context.
|
||||
*
|
||||
* @return array{db:OGPDatabase|null, settings:array, table_prefix:string}|null
|
||||
*/
|
||||
function billing_panel_bootstrap()
|
||||
{
|
||||
static $context = null;
|
||||
if ($context !== null) {
|
||||
return $context;
|
||||
}
|
||||
|
||||
$root = realpath(__DIR__ . '/../../');
|
||||
if ($root === false) {
|
||||
error_log('billing_panel_bootstrap: unable to resolve project root');
|
||||
return null;
|
||||
}
|
||||
|
||||
// When storefront runs from modules/billing/_website, $root points to modules/.
|
||||
// Adjust path so panel includes resolve from the repository root, not modules/.
|
||||
if (is_dir($root . '/modules') && is_dir($root . '/includes')) {
|
||||
// already at repo root
|
||||
} elseif (is_dir(dirname($root) . '/includes')) {
|
||||
$root = dirname($root);
|
||||
}
|
||||
|
||||
static $includeInjected = false;
|
||||
if (!$includeInjected) {
|
||||
set_include_path($root . PATH_SEPARATOR . get_include_path());
|
||||
// Change CWD to the panel root so that SERVER_CONFIG_LOCATION and other
|
||||
// relative paths (e.g. XML_SCHEMA) resolve correctly when billing endpoints
|
||||
// run outside of home.php (e.g. api/capture_order.php, PayPal webhooks).
|
||||
chdir($root);
|
||||
$includeInjected = true;
|
||||
}
|
||||
|
||||
// Define panel constants if they are not already defined (panel runtime does this for us).
|
||||
if (!defined('INCLUDES')) {
|
||||
define('INCLUDES', 'includes/');
|
||||
}
|
||||
if (!defined('MODULES')) {
|
||||
define('MODULES', 'modules/');
|
||||
}
|
||||
|
||||
// Load panel helpers that provisioning logic depends on.
|
||||
require_once $root . '/includes/functions.php';
|
||||
require_once $root . '/includes/helpers.php';
|
||||
require_once $root . '/includes/lib_remote.php';
|
||||
require_once $root . '/modules/config_games/server_config_parser.php';
|
||||
|
||||
// Load panel configuration (db credentials, prefix, etc.)
|
||||
$configFile = $root . '/includes/config.inc.php';
|
||||
if (!file_exists($configFile)) {
|
||||
error_log('billing_panel_bootstrap: missing config file ' . $configFile);
|
||||
return null;
|
||||
}
|
||||
require $configFile;
|
||||
|
||||
// Ensure required variables exist before attempting to connect.
|
||||
if (!isset($db_type, $db_host, $db_user, $db_pass, $db_name, $table_prefix)) {
|
||||
error_log('billing_panel_bootstrap: config variables not initialized');
|
||||
return null;
|
||||
}
|
||||
|
||||
$panelDb = createDatabaseConnection($db_type, $db_host, $db_user, $db_pass, $db_name, $table_prefix, isset($db_port) ? $db_port : NULL);
|
||||
if (!($panelDb instanceof OGPDatabase)) {
|
||||
error_log('billing_panel_bootstrap: failed to connect to panel database');
|
||||
return null;
|
||||
}
|
||||
|
||||
$settings = $panelDb->getSettings();
|
||||
|
||||
$context = [
|
||||
'db' => $panelDb,
|
||||
'settings' => is_array($settings) ? $settings : [],
|
||||
'table_prefix' => $table_prefix,
|
||||
];
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('billing_get_panel_db')) {
|
||||
/**
|
||||
* Convenience wrapper to fetch the shared OGPDatabase handle.
|
||||
*
|
||||
* @return OGPDatabase|null
|
||||
*/
|
||||
function billing_get_panel_db()
|
||||
{
|
||||
$ctx = billing_panel_bootstrap();
|
||||
return $ctx['db'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('billing_get_panel_settings')) {
|
||||
/**
|
||||
* Convenience wrapper to fetch panel settings (time zone, steam creds, etc.).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function billing_get_panel_settings()
|
||||
{
|
||||
$ctx = billing_panel_bootstrap();
|
||||
return $ctx['settings'] ?? [];
|
||||
}
|
||||
}
|
||||
223
Website/includes/payment_processor.php
Normal file
223
Website/includes/payment_processor.php
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
/**
|
||||
* Standalone Payment Processing Helper (module-local)
|
||||
*
|
||||
* 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);
|
||||
*/
|
||||
|
||||
// Load panel config first, falling back to a module-local copy if needed.
|
||||
require_once __DIR__ . '/config_loader.php';
|
||||
|
||||
// Variables from config.inc.php (helps IDEs understand scope)
|
||||
/** @var string $db_host Database host */
|
||||
/** @var string $db_user Database user */
|
||||
/** @var string $db_pass Database password */
|
||||
/** @var string $db_name Database name */
|
||||
/** @var string $table_prefix Table prefix for database tables */
|
||||
|
||||
// Normalize table prefix variable: many files use $table_prefix (lowercase)
|
||||
if (!isset($TABLE_PREFIX) && isset($table_prefix)) {
|
||||
$TABLE_PREFIX = $table_prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a mysqli connection using the site's config values.
|
||||
* Returns mysqli or false on failure.
|
||||
*/
|
||||
function payment_db_connect() {
|
||||
global $db_host, $db_user, $db_pass, $db_name, $db_port;
|
||||
|
||||
if (empty($db_host) || empty($db_user) || empty($db_name)) {
|
||||
error_log('[payment_processor] DB globals not available from includes/config.inc.php');
|
||||
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 ((array)$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);
|
||||
}
|
||||
|
||||
// If invoice has a coupon, increment usage count
|
||||
$coupon_id = intval($inv['coupon_id'] ?? 0);
|
||||
if ($coupon_id > 0) {
|
||||
$upd_coupon = "UPDATE `" . $TABLE_PREFIX . "billing_coupons` SET current_uses = current_uses + 1 WHERE coupon_id = ?";
|
||||
if ($stmt = mysqli_prepare($db, $upd_coupon)) {
|
||||
mysqli_stmt_bind_param($stmt, 'i', $coupon_id);
|
||||
mysqli_stmt_execute($stmt);
|
||||
mysqli_stmt_close($stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// If this invoice already has an order -> treat as renewal
|
||||
if ($order_id > 0) {
|
||||
// compute months
|
||||
$months = (stripos($duration, 'year') !== false) ? ($qty * 12) : $qty;
|
||||
// fetch current end_date
|
||||
$get = "SELECT end_date FROM `" . $TABLE_PREFIX . "billing_orders` WHERE order_id = ? LIMIT 1";
|
||||
if ($stmt = mysqli_prepare($db, $get)) {
|
||||
mysqli_stmt_bind_param($stmt, 'i', $order_id);
|
||||
mysqli_stmt_execute($stmt);
|
||||
$res = mysqli_stmt_get_result($stmt);
|
||||
if ($res && $row = mysqli_fetch_assoc($res)) {
|
||||
$current_end = $row['end_date'] ?? 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);
|
||||
if ($months > 0) $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='Active', payment_txid = ?, paid_ts = ? WHERE order_id = ?";
|
||||
if ($u = mysqli_prepare($db, $update)) {
|
||||
mysqli_stmt_bind_param($u, 'sssi', $new_end, $esc_txid, $now, $order_id);
|
||||
mysqli_stmt_execute($u);
|
||||
mysqli_stmt_close($u);
|
||||
$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', 'Active', '%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 {
|
||||
error_log('[payment_processor] Failed to insert order: ' . mysqli_error($db));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
32
Website/includes/session_bridge.php
Normal file
32
Website/includes/session_bridge.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/**
|
||||
* Session bridge to keep panel + storefront logins in sync.
|
||||
* Always call this before rendering billing pages.
|
||||
*/
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_name('opengamepanel_web');
|
||||
session_start();
|
||||
}
|
||||
|
||||
// If the panel session is populated, mirror into website-specific keys.
|
||||
if (!empty($_SESSION['user_id']) && empty($_SESSION['website_user_id'])) {
|
||||
$_SESSION['website_user_id'] = (int)$_SESSION['user_id'];
|
||||
if (!empty($_SESSION['users_login'])) {
|
||||
$_SESSION['website_username'] = $_SESSION['users_login'];
|
||||
}
|
||||
if (!empty($_SESSION['users_group'])) {
|
||||
$_SESSION['website_user_role'] = $_SESSION['users_group'];
|
||||
}
|
||||
}
|
||||
|
||||
// If the website session is populated but the panel keys are missing, mirror back.
|
||||
if (!empty($_SESSION['website_user_id']) && empty($_SESSION['user_id'])) {
|
||||
$_SESSION['user_id'] = (int)$_SESSION['website_user_id'];
|
||||
if (!empty($_SESSION['website_username'])) {
|
||||
$_SESSION['users_login'] = $_SESSION['website_username'];
|
||||
}
|
||||
if (!empty($_SESSION['website_user_role'])) {
|
||||
$_SESSION['users_group'] = $_SESSION['website_user_role'];
|
||||
}
|
||||
}
|
||||
6
Website/includes/top.php
Normal file
6
Website/includes/top.php
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
// top.php — reserved placeholder
|
||||
// The site header (logo + title) has been moved into includes/menu.php
|
||||
// Keep this file present so pages that include it continue to work.
|
||||
// If you need to re-enable a separate top strip later, add markup here.
|
||||
?>
|
||||
Loading…
Add table
Add a link
Reference in a new issue