Fix login and order
This commit is contained in:
parent
dbecad8606
commit
484a36ce11
22 changed files with 399 additions and 520 deletions
|
|
@ -63,24 +63,4 @@ $install_queries[1] = array(
|
|||
KEY `idx_logger_home` (`home_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
|
||||
|
||||
$install_queries[2] = array(
|
||||
"CREATE TABLE IF NOT EXISTS `".OGP_DB_PREFIX."sso_tokens`
|
||||
(
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`token_hash` CHAR(64) NOT NULL,
|
||||
`user_id` INT(11) NOT NULL,
|
||||
`source` VARCHAR(32) NOT NULL,
|
||||
`destination` VARCHAR(32) NOT NULL,
|
||||
`created_at` DATETIME NOT NULL,
|
||||
`expires_at` DATETIME NOT NULL,
|
||||
`used_at` DATETIME DEFAULT NULL,
|
||||
`nonce` VARCHAR(64) NOT NULL,
|
||||
`originating_ip` VARCHAR(64) DEFAULT NULL,
|
||||
`user_agent_hash` CHAR(64) DEFAULT NULL,
|
||||
`return_path` VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_sso_token_hash` (`token_hash`),
|
||||
KEY `idx_sso_user_destination` (`user_id`, `destination`),
|
||||
KEY `idx_sso_expires` (`expires_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ function exec_ogp_module()
|
|||
$projectRequestUrl = htmlspecialchars(gsp_project_request_url(), ENT_QUOTES, 'UTF-8');
|
||||
$discordInviteUrl = htmlspecialchars(gsp_discord_invite_url(), ENT_QUOTES, 'UTF-8');
|
||||
$serverStatusUrl = htmlspecialchars(gsp_server_status_url(), ENT_QUOTES, 'UTF-8');
|
||||
$orderAnotherUrl = htmlspecialchars(gsp_panel_to_website_sso_url('serverlist.php'), ENT_QUOTES, 'UTF-8');
|
||||
$orderAnotherUrl = htmlspecialchars(gsp_website_url('serverlist.php'), ENT_QUOTES, 'UTF-8');
|
||||
$theme = isset($settings['theme']) ? $settings['theme'] : 'SimpleBootstrap';
|
||||
$themeBase = 'themes/' . htmlspecialchars($theme, ENT_QUOTES, 'UTF-8') . '/images/icons/';
|
||||
$cards = array(
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ This module is the public Gameservers.World sales and documentation website.
|
|||
Panel/modules/website/
|
||||
index.php
|
||||
serverlist.php
|
||||
cart.php
|
||||
docs.php
|
||||
login.php
|
||||
pricing.php
|
||||
|
|
@ -34,6 +35,7 @@ Panel/modules/website/
|
|||
pages/
|
||||
home.php
|
||||
game_servers.php
|
||||
cart.php
|
||||
documentation.php
|
||||
pricing.php
|
||||
locations.php
|
||||
|
|
@ -72,22 +74,19 @@ Effects:
|
|||
- `serverlist.php` shows a clean fallback message instead of a fatal include error
|
||||
- shared navigation never crashes because billing config is missing
|
||||
|
||||
## Shared Accounts and SSO
|
||||
## Shared Accounts
|
||||
|
||||
The Panel user table is the identity source for the website. Website login checks
|
||||
the same `users` table and legacy Panel password hash format instead of creating
|
||||
a second password database.
|
||||
|
||||
The website and Panel run on different parent domains, so PHP session cookies are
|
||||
not shared. Authenticated navigation uses short-lived one-time SSO tokens stored
|
||||
in `OGP_DB_PREFIXsso_tokens`:
|
||||
not shared. SSO is deferred in the current implementation. Users can use the same
|
||||
username and password on both sites, but the website and Panel keep separate
|
||||
sessions. Control Panel and staff links point directly to the configured Panel URL.
|
||||
|
||||
- website to Panel: `Panel/modules/website/sso.php` creates a token and redirects to `Panel/sso.php`
|
||||
- Panel to website: `Panel/sso.php` creates a token and redirects to `Panel/modules/website/sso.php`
|
||||
- tokens are random, stored only as SHA-256 hashes, expire in 30-60 seconds, and are marked used after one successful validation
|
||||
- passwords, password hashes, permanent API keys, and PHP session IDs are never placed in URLs
|
||||
|
||||
Production SSO requires HTTPS. Localhost is allowed only for development testing.
|
||||
`sso.php` endpoints are compatibility redirects only. They do not create tokens or
|
||||
sessions.
|
||||
|
||||
## Ordering
|
||||
|
||||
|
|
@ -96,15 +95,16 @@ entry point:
|
|||
|
||||
- `order.php?service_id=...`
|
||||
|
||||
That page validates `service_id` server-side against enabled catalog rows before
|
||||
continuing. Logged-out customers are sent through `login.php` and returned to the
|
||||
same service after successful login. The removed `billing/order.php` route is
|
||||
obsolete and should not be used for customer-facing links.
|
||||
That page validates `service_id` server-side against enabled catalog rows, then
|
||||
lets anonymous visitors configure slots and location before adding the service to
|
||||
the session cart. Login or registration is required only when the customer proceeds
|
||||
to checkout. The removed `billing/order.php` route is obsolete and should not be
|
||||
used for customer-facing links.
|
||||
|
||||
The website owns catalog display, login-return intent, pricing display, and
|
||||
checkout entry. Payment approval and final server provisioning remain server-side
|
||||
responsibilities; browser requests must not call private provisioning methods
|
||||
directly.
|
||||
The website owns catalog display, cart storage, checkout intent, pricing display,
|
||||
and customer confirmation. Payment approval and final server provisioning remain
|
||||
server-side responsibilities; browser requests must not call private provisioning
|
||||
methods directly.
|
||||
|
||||
## Documentation source
|
||||
|
||||
|
|
|
|||
58
Panel/modules/website/cart.php
Normal file
58
Panel/modules/website/cart.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/includes/bootstrap.php';
|
||||
|
||||
$message = '';
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = (string)($_POST['action'] ?? '');
|
||||
if ($action === 'remove') {
|
||||
website_cart_remove((string)($_POST['cart_key'] ?? ''));
|
||||
header('Location: ' . website_cart_url(), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'checkout') {
|
||||
if (website_cart_count() === 0) {
|
||||
$error = 'Your cart is empty.';
|
||||
} elseif (!website_is_logged_in()) {
|
||||
$_SESSION['website_checkout_intent'] = true;
|
||||
$_SESSION['website_login_return'] = 'cart.php?checkout=1';
|
||||
header('Location: ' . website_login_url('cart.php?checkout=1'), true, 302);
|
||||
exit;
|
||||
} else {
|
||||
website_log_activity('Website checkout requested', (int)($_SESSION['website_user_id'] ?? 0), 'checkout_requested');
|
||||
$message = 'Checkout is ready for account validation, but the payment gateway is not connected in this repository checkout. Please contact support to complete this order.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['checkout']) && (string)$_GET['checkout'] === '1') {
|
||||
if (website_cart_count() === 0) {
|
||||
$error = 'Your cart is empty.';
|
||||
} elseif (!website_is_logged_in()) {
|
||||
$_SESSION['website_checkout_intent'] = true;
|
||||
$_SESSION['website_login_return'] = 'cart.php?checkout=1';
|
||||
header('Location: ' . website_login_url('cart.php?checkout=1'), true, 302);
|
||||
exit;
|
||||
} else {
|
||||
$message = 'You are logged in and your cart is preserved. Payment checkout still needs the active payment runtime before public orders can be completed.';
|
||||
}
|
||||
}
|
||||
|
||||
website_render(
|
||||
'cart.php',
|
||||
[
|
||||
'activePage' => 'cart',
|
||||
'pageTitle' => 'Cart - Gameservers.World',
|
||||
'metaDescription' => 'Review your Gameservers.World server cart before checkout.',
|
||||
'canonicalPath' => 'cart.php',
|
||||
'items' => website_cart_items(),
|
||||
'cartTotal' => website_cart_total(),
|
||||
'message' => $message,
|
||||
'error' => $error,
|
||||
]
|
||||
);
|
||||
|
|
@ -19,7 +19,6 @@ return [
|
|||
// Active panel URL. Do not point the public site at /panel/ unless that route is real.
|
||||
'panel_url' => 'https://panel.iaregamer.com/',
|
||||
'login_url' => 'https://panel.iaregamer.com/',
|
||||
'panel_sso_url' => 'https://panel.iaregamer.com/sso.php',
|
||||
|
||||
'company' => [
|
||||
'name' => 'Runlevel Systems',
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/paths.php';
|
||||
if (is_readable(WEBSITE_PANEL_INCLUDE_DIR . '/sso.php')) {
|
||||
require_once WEBSITE_PANEL_INCLUDE_DIR . '/sso.php';
|
||||
}
|
||||
|
||||
if (defined('GSP_WEBSITE_BOOTSTRAPPED')) {
|
||||
return;
|
||||
|
|
@ -44,7 +41,6 @@ $websiteDefaults = [
|
|||
'billing_base_url' => '/billing',
|
||||
'panel_url' => 'https://panel.iaregamer.com/',
|
||||
'login_url' => 'https://panel.iaregamer.com/',
|
||||
'panel_sso_url' => 'https://panel.iaregamer.com/sso.php',
|
||||
'company' => [
|
||||
'name' => 'Runlevel Systems',
|
||||
'url' => 'https://runlevelsystems.com/',
|
||||
|
|
@ -476,6 +472,9 @@ function website_authenticate_user(string $login, string $password): ?array
|
|||
if (!$user || !website_verify_panel_password($user, $password)) {
|
||||
return null;
|
||||
}
|
||||
if ((string)($user['users_role'] ?? '') === 'banned') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
|
@ -543,7 +542,7 @@ function website_log_activity(string $message, int $userId = 0, string $eventTyp
|
|||
}
|
||||
|
||||
$safeTable = $db->real_escape_string($table);
|
||||
$ip = substr((function_exists('gsp_sso_client_ip') ? gsp_sso_client_ip() : (string)($_SERVER['REMOTE_ADDR'] ?? '')), 0, 255);
|
||||
$ip = substr((string)($_SERVER['REMOTE_ADDR'] ?? ''), 0, 255);
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO `{$safeTable}` (`date`, `user_id`, `ip`, `message`, `source_type`, `category`, `event_type`, `severity`)
|
||||
VALUES (FROM_UNIXTIME(UNIX_TIMESTAMP(), '%d-%m-%Y %H:%i:%s'), ?, ?, ?, 'website', 'authentication', ?, 'info')"
|
||||
|
|
@ -560,15 +559,16 @@ function website_log_activity(string $message, int $userId = 0, string $eventTyp
|
|||
|
||||
function website_safe_return_path(string $returnPath, string $default = 'index.php'): string
|
||||
{
|
||||
if (function_exists('gsp_sso_safe_return_path')) {
|
||||
return gsp_sso_safe_return_path($returnPath, $default);
|
||||
}
|
||||
|
||||
if ($returnPath === '' || preg_match('#^[a-z][a-z0-9+.-]*://#i', $returnPath) === 1 || str_starts_with($returnPath, '//')) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return ltrim($returnPath, '/');
|
||||
$returnPath = ltrim($returnPath, '/');
|
||||
if (str_contains($returnPath, "\0") || str_starts_with($returnPath, '../') || str_contains($returnPath, '/../')) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $returnPath;
|
||||
}
|
||||
|
||||
function website_login_url(string $returnPath = ''): string
|
||||
|
|
@ -580,15 +580,9 @@ function website_login_url(string $returnPath = ''): string
|
|||
return website_url($path);
|
||||
}
|
||||
|
||||
function website_panel_sso_url(string $returnPath = 'home.php?m=dashboard&p=dashboard'): string
|
||||
{
|
||||
$path = 'sso.php?destination=panel&return=' . rawurlencode(website_safe_return_path($returnPath, 'home.php?m=dashboard&p=dashboard'));
|
||||
return website_url($path);
|
||||
}
|
||||
|
||||
function website_control_panel_url(string $returnPath = 'home.php?m=dashboard&p=dashboard'): string
|
||||
{
|
||||
return website_is_logged_in() ? website_panel_sso_url($returnPath) : website_login_url('panel');
|
||||
return panel_url(website_safe_return_path($returnPath, 'home.php?m=dashboard&p=dashboard'));
|
||||
}
|
||||
|
||||
function website_order_url(int|string $serviceId): string
|
||||
|
|
@ -596,6 +590,21 @@ function website_order_url(int|string $serviceId): string
|
|||
return website_url('order.php?service_id=' . rawurlencode((string)$serviceId));
|
||||
}
|
||||
|
||||
function website_cart_url(): string
|
||||
{
|
||||
return website_url('cart.php');
|
||||
}
|
||||
|
||||
function website_checkout_url(): string
|
||||
{
|
||||
return website_url('cart.php?checkout=1');
|
||||
}
|
||||
|
||||
function website_register_url(string $returnPath = 'cart.php'): string
|
||||
{
|
||||
return panel_url('index.php?m=register');
|
||||
}
|
||||
|
||||
function website_fetch_service_by_id(int $serviceId): ?array
|
||||
{
|
||||
$db = website_db();
|
||||
|
|
@ -638,6 +647,96 @@ function website_fetch_service_by_id(int $serviceId): ?array
|
|||
return $service;
|
||||
}
|
||||
|
||||
function website_service_name(array $service): string
|
||||
{
|
||||
$name = trim((string)($service['cfg_game_name'] ?? ''));
|
||||
if ($name === '') {
|
||||
$name = trim((string)($service['service_name'] ?? ''));
|
||||
}
|
||||
return $name === '' ? 'Game Server' : $name;
|
||||
}
|
||||
|
||||
function website_service_min_slots(array $service): int
|
||||
{
|
||||
foreach (['min_slots', 'minimum_slots', 'slots_min'] as $column) {
|
||||
if (isset($service[$column]) && (int)$service[$column] > 0) {
|
||||
return (int)$service[$column];
|
||||
}
|
||||
}
|
||||
|
||||
$pricing = website_config('pricing', []);
|
||||
return max(1, (int)($pricing['standard_min_slots'] ?? 16));
|
||||
}
|
||||
|
||||
function website_service_max_slots(array $service): int
|
||||
{
|
||||
foreach (['max_slots', 'maximum_slots', 'slots_max', 'max_players'] as $column) {
|
||||
if (isset($service[$column]) && (int)$service[$column] > 0) {
|
||||
return (int)$service[$column];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function website_service_locations(array $service): array
|
||||
{
|
||||
$raw = trim((string)($service['remote_server_id'] ?? ''));
|
||||
if ($raw === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$locations = [];
|
||||
foreach (preg_split('/\s*,\s*/', $raw) ?: [] as $remoteServerId) {
|
||||
$remoteServerId = trim($remoteServerId);
|
||||
if ($remoteServerId === '' || !ctype_digit($remoteServerId)) {
|
||||
continue;
|
||||
}
|
||||
$locations[$remoteServerId] = 'Location ' . $remoteServerId;
|
||||
}
|
||||
|
||||
return $locations;
|
||||
}
|
||||
|
||||
function website_cart_items(): array
|
||||
{
|
||||
website_start_session();
|
||||
return is_array($_SESSION['website_cart'] ?? null) ? $_SESSION['website_cart'] : [];
|
||||
}
|
||||
|
||||
function website_cart_count(): int
|
||||
{
|
||||
return count(website_cart_items());
|
||||
}
|
||||
|
||||
function website_cart_add(array $item): void
|
||||
{
|
||||
website_start_session();
|
||||
if (!isset($_SESSION['website_cart']) || !is_array($_SESSION['website_cart'])) {
|
||||
$_SESSION['website_cart'] = [];
|
||||
}
|
||||
|
||||
$key = bin2hex(random_bytes(8));
|
||||
$_SESSION['website_cart'][$key] = $item;
|
||||
}
|
||||
|
||||
function website_cart_remove(string $key): void
|
||||
{
|
||||
website_start_session();
|
||||
if (isset($_SESSION['website_cart'][$key])) {
|
||||
unset($_SESSION['website_cart'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
function website_cart_total(): float
|
||||
{
|
||||
$total = 0.0;
|
||||
foreach (website_cart_items() as $item) {
|
||||
$total += (float)($item['monthly_total'] ?? 0);
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
function website_billing_docs_root(): ?string
|
||||
{
|
||||
if (is_dir(WEBSITE_BILLING_DOCS_DIR)) {
|
||||
|
|
|
|||
|
|
@ -40,17 +40,20 @@ $currentUser = website_current_user();
|
|||
<li><a href="<?= website_escape(website_url('account.php')) ?>">My Account</a></li>
|
||||
<li><a href="<?= website_escape(website_url('serverlist.php')) ?>">Order a Server</a></li>
|
||||
<li><a href="<?= website_escape(website_control_panel_url()) ?>">Control Panel</a></li>
|
||||
<li><a href="<?= website_escape(website_panel_sso_url('home.php?m=gamemanager&p=game_monitor')) ?>">My Servers</a></li>
|
||||
<li><a href="<?= website_escape(panel_url('home.php?m=gamemanager&p=game_monitor')) ?>">My Servers</a></li>
|
||||
<li><a href="<?= website_escape(website_cart_url()) ?>">Cart</a></li>
|
||||
<li><a href="<?= website_escape(website_url('logout.php')) ?>">Log Out</a></li>
|
||||
<?php else: ?>
|
||||
<li><a href="<?= website_escape(website_login_url()) ?>">Account Login</a></li>
|
||||
<li><a href="<?= website_escape(website_register_url()) ?>">Create Account</a></li>
|
||||
<li><a href="<?= website_escape(website_url('serverlist.php')) ?>">Order a Server</a></li>
|
||||
<li><a href="<?= website_escape(website_control_panel_url()) ?>">Control Panel</a></li>
|
||||
<li><a href="<?= website_escape(website_cart_url()) ?>">Cart</a></li>
|
||||
<?php endif; ?>
|
||||
<li><a href="<?= website_escape(website_url('docs.php')) ?>">Server Guides</a></li>
|
||||
<li><a href="<?= website_escape(website_custom_project_url()) ?>">Request Custom Work</a></li>
|
||||
<?php if ($currentUser && website_current_user_is_staff()): ?>
|
||||
<li><a href="<?= website_escape(website_panel_sso_url('home.php?m=administration&p=watch_logger')) ?>">Staff Tools</a></li>
|
||||
<li><a href="<?= website_escape(panel_url('home.php?m=administration&p=watch_logger')) ?>">Staff Tools</a></li>
|
||||
<?php endif; ?>
|
||||
<?php if ($discordUrl !== ''): ?>
|
||||
<li><a href="<?= website_escape($discordUrl) ?>" target="_blank" rel="noopener noreferrer">Discord</a></li>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
$activePage = $activePage ?? '';
|
||||
$currentUser = website_current_user();
|
||||
$cartCount = website_cart_count();
|
||||
$navLinks = [
|
||||
['key' => 'home', 'label' => 'Home', 'href' => website_url('index.php')],
|
||||
['key' => 'servers', 'label' => 'Game Servers', 'href' => website_url('serverlist.php')],
|
||||
|
|
@ -11,6 +13,15 @@ $navLinks = [
|
|||
['key' => 'locations', 'label' => 'Locations', 'href' => website_url('locations.php')],
|
||||
['key' => 'support', 'label' => 'Support', 'href' => website_url('support.php')],
|
||||
];
|
||||
if ($currentUser) {
|
||||
$navLinks[] = ['key' => 'account', 'label' => 'My Account', 'href' => website_url('account.php')];
|
||||
$navLinks[] = ['key' => 'orders', 'label' => 'My Orders', 'href' => website_url('cart.php')];
|
||||
$navLinks[] = ['key' => 'servers_panel', 'label' => 'My Servers', 'href' => panel_url('home.php?m=gamemanager&p=game_monitor')];
|
||||
} else {
|
||||
$navLinks[] = ['key' => 'account', 'label' => 'Login', 'href' => website_login_url()];
|
||||
$navLinks[] = ['key' => 'register', 'label' => 'Create Account', 'href' => website_register_url()];
|
||||
}
|
||||
$navLinks[] = ['key' => 'cart', 'label' => 'Cart' . ($cartCount > 0 ? ' (' . $cartCount . ')' : ''), 'href' => website_cart_url()];
|
||||
?>
|
||||
<header class="site-header">
|
||||
<div class="container header-shell">
|
||||
|
|
@ -40,6 +51,9 @@ $navLinks = [
|
|||
<div class="header-actions" data-header-actions>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_custom_project_url()) ?>">Custom Projects</a>
|
||||
<a class="button button-primary" href="<?= website_escape(website_control_panel_url()) ?>">Control Panel</a>
|
||||
<?php if ($currentUser): ?>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_url('logout.php')) ?>">Logout</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ $returnPath = website_safe_return_path((string)($_GET['return'] ?? $_POST['retur
|
|||
|
||||
if (website_is_logged_in()) {
|
||||
if ($returnPath === 'panel') {
|
||||
header('Location: ' . website_panel_sso_url(), true, 302);
|
||||
header('Location: ' . website_control_panel_url(), true, 302);
|
||||
exit;
|
||||
}
|
||||
header('Location: ' . website_url($returnPath), true, 302);
|
||||
|
|
@ -27,7 +27,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
unset($_SESSION['website_login_return']);
|
||||
|
||||
if ($returnPath === 'panel') {
|
||||
header('Location: ' . website_panel_sso_url(), true, 302);
|
||||
header('Location: ' . website_control_panel_url(), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ require_once __DIR__ . '/includes/bootstrap.php';
|
|||
|
||||
$serviceId = filter_input(INPUT_GET, 'service_id', FILTER_VALIDATE_INT);
|
||||
$service = $serviceId ? website_fetch_service_by_id((int)$serviceId) : null;
|
||||
$error = '';
|
||||
|
||||
if (!$service) {
|
||||
website_log_activity('Invalid or unavailable service_id requested from website order flow', (int)($_SESSION['website_user_id'] ?? 0), 'invalid_service');
|
||||
|
|
@ -23,14 +24,45 @@ if (!$service) {
|
|||
exit;
|
||||
}
|
||||
|
||||
if (!website_is_logged_in()) {
|
||||
$_SESSION['website_pending_order_service_id'] = (int)$service['service_id'];
|
||||
$_SESSION['website_login_return'] = 'order.php?service_id=' . (int)$service['service_id'];
|
||||
header('Location: ' . website_login_url('order.php?service_id=' . (int)$service['service_id']), true, 302);
|
||||
exit;
|
||||
$locations = website_service_locations($service);
|
||||
$minSlots = website_service_min_slots($service);
|
||||
$maxSlots = website_service_max_slots($service);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$slots = filter_input(INPUT_POST, 'slots', FILTER_VALIDATE_INT);
|
||||
$locationId = trim((string)($_POST['location_id'] ?? ''));
|
||||
$durationMonths = filter_input(INPUT_POST, 'duration_months', FILTER_VALIDATE_INT);
|
||||
|
||||
if ($slots === false || $slots === null || $slots < $minSlots) {
|
||||
$error = 'Select at least ' . $minSlots . ' slots for this service.';
|
||||
} elseif ($maxSlots > 0 && $slots > $maxSlots) {
|
||||
$error = 'This service supports a maximum of ' . $maxSlots . ' slots.';
|
||||
} elseif (empty($locations) || !array_key_exists($locationId, $locations)) {
|
||||
$error = 'Select an available location for this service.';
|
||||
} elseif (!in_array((int)$durationMonths, [1, 3, 6, 12], true)) {
|
||||
$error = 'Select a valid billing duration.';
|
||||
} else {
|
||||
$priceMonthly = (float)($service['price_monthly'] ?? 0);
|
||||
$monthlyTotal = $priceMonthly > 0 ? $priceMonthly : 0.0;
|
||||
website_cart_add([
|
||||
'service_id' => (int)$service['service_id'],
|
||||
'service_name' => website_service_name($service),
|
||||
'slots' => (int)$slots,
|
||||
'location_id' => $locationId,
|
||||
'location_name' => $locations[$locationId],
|
||||
'duration_months' => (int)$durationMonths,
|
||||
'price_monthly' => $priceMonthly,
|
||||
'monthly_total' => $monthlyTotal,
|
||||
'added_at' => time(),
|
||||
]);
|
||||
|
||||
website_log_activity('Website cart item added for service_id ' . (int)$service['service_id'], (int)($_SESSION['website_user_id'] ?? 0), 'cart_item_added');
|
||||
header('Location: ' . website_cart_url(), true, 302);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
website_log_activity('Website order flow started for service_id ' . (int)$service['service_id'], (int)$_SESSION['website_user_id'], 'order_started');
|
||||
website_log_activity('Website order flow opened for service_id ' . (int)$service['service_id'], (int)($_SESSION['website_user_id'] ?? 0), 'order_started');
|
||||
website_render(
|
||||
'order.php',
|
||||
[
|
||||
|
|
@ -39,6 +71,9 @@ website_render(
|
|||
'metaDescription' => 'Configure your Gameservers.World game server order.',
|
||||
'canonicalPath' => 'order.php',
|
||||
'service' => $service,
|
||||
'error' => null,
|
||||
'error' => $error,
|
||||
'locations' => $locations,
|
||||
'minSlots' => $minSlots,
|
||||
'maxSlots' => $maxSlots,
|
||||
]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ $isStaff = website_current_user_is_staff();
|
|||
<h3>My Servers</h3>
|
||||
<p>Open the GSP control panel to manage assigned game servers, files, logs, updates, backups, and server state.</p>
|
||||
<div class="card-actions">
|
||||
<a class="button button-primary" href="<?= website_escape(website_panel_sso_url('home.php?m=gamemanager&p=game_monitor')) ?>">Open My Servers</a>
|
||||
<a class="button button-primary" href="<?= website_escape(panel_url('home.php?m=gamemanager&p=game_monitor')) ?>">Open My Servers</a>
|
||||
</div>
|
||||
</article>
|
||||
<article class="summary-card">
|
||||
|
|
@ -42,7 +42,7 @@ $isStaff = website_current_user_is_staff();
|
|||
<h3>Staff Access</h3>
|
||||
<p>Staff links are shown only after account role checks. Server-side authorization still applies inside the Panel.</p>
|
||||
<div class="card-actions">
|
||||
<a class="button button-secondary" href="<?= website_escape(website_panel_sso_url('home.php?m=administration&p=watch_logger')) ?>">Panel Administration</a>
|
||||
<a class="button button-secondary" href="<?= website_escape(panel_url('home.php?m=administration&p=watch_logger')) ?>">Panel Administration</a>
|
||||
</div>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
66
Panel/modules/website/pages/cart.php
Normal file
66
Panel/modules/website/pages/cart.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
?>
|
||||
<section class="page-heading">
|
||||
<div class="container">
|
||||
<h1>Cart</h1>
|
||||
<p>Review selected server packages. You can configure and add servers before logging in; login or account creation is required when you proceed to checkout.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<?php if ($error !== ''): ?>
|
||||
<div class="alert warning"><?= website_escape($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($message !== ''): ?>
|
||||
<div class="alert info"><?= website_escape($message) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($items)): ?>
|
||||
<div class="empty-state">
|
||||
<h2>Your cart is empty</h2>
|
||||
<p>Browse the catalog to choose a game server, slots, and location.</p>
|
||||
<div class="card-actions">
|
||||
<a class="button button-primary" href="<?= website_escape(website_url('serverlist.php')) ?>">View Game Servers</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="summary-grid">
|
||||
<?php foreach ($items as $key => $item): ?>
|
||||
<article class="summary-card">
|
||||
<h3><?= website_escape((string)($item['service_name'] ?? 'Game Server')) ?></h3>
|
||||
<p>Service ID: <?= website_escape((string)($item['service_id'] ?? '')) ?></p>
|
||||
<p>Slots: <?= website_escape((string)($item['slots'] ?? '')) ?></p>
|
||||
<p>Location: <?= website_escape((string)($item['location_name'] ?? '')) ?></p>
|
||||
<p>Billing duration: <?= website_escape((string)($item['duration_months'] ?? 1)) ?> month(s)</p>
|
||||
<p><strong><?= ((float)($item['monthly_total'] ?? 0) > 0) ? '$' . website_escape(number_format((float)$item['monthly_total'], 2)) . ' / month' : 'Contact for pricing' ?></strong></p>
|
||||
<form method="post" action="<?= website_escape(website_cart_url()) ?>">
|
||||
<input type="hidden" name="action" value="remove">
|
||||
<input type="hidden" name="cart_key" value="<?= website_escape((string)$key) ?>">
|
||||
<button class="button button-secondary" type="submit">Remove</button>
|
||||
</form>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="summary-card" style="margin-top: 18px;">
|
||||
<h3>Checkout</h3>
|
||||
<p>Estimated monthly total: <strong><?= $cartTotal > 0 ? '$' . website_escape(number_format((float)$cartTotal, 2)) : 'Contact for pricing' ?></strong></p>
|
||||
<p class="muted">Prices and service availability are revalidated server-side before payment or provisioning. Adding an item to the cart does not create a running server.</p>
|
||||
<div class="card-actions">
|
||||
<form method="post" action="<?= website_escape(website_cart_url()) ?>">
|
||||
<input type="hidden" name="action" value="checkout">
|
||||
<button class="button button-primary" type="submit">Proceed to Checkout</button>
|
||||
</form>
|
||||
<?php if (!website_is_logged_in()): ?>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_login_url('cart.php?checkout=1')) ?>">Log In</a>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_register_url('cart.php')) ?>">Create Account</a>
|
||||
<?php endif; ?>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_url('serverlist.php')) ?>">Continue Shopping</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
|||
<input id="password" name="password" type="password" autocomplete="current-password" required>
|
||||
<button class="button button-primary" type="submit">Log In</button>
|
||||
</form>
|
||||
<p class="muted">After login, Control Panel and server-management links use a short-lived one-time SSO handoff. Passwords and PHP session IDs are never passed between domains.</p>
|
||||
<p class="muted">Gameservers.World and the GSP Panel use the same account credentials, but they keep separate secure sessions. You may be asked to log in separately when opening the Panel.</p>
|
||||
<p class="muted">Need an account? <a href="<?= website_escape(website_register_url($returnPath)) ?>">Create one through the GSP Panel registration page.</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -25,18 +25,20 @@ endif;
|
|||
$serviceName = trim((string)($service['cfg_game_name'] ?? $service['service_name'] ?? 'Game Server'));
|
||||
$description = trim((string)($service['description'] ?? ''));
|
||||
$price = (float)($service['price_monthly'] ?? 0);
|
||||
$minSlots = (int)($service['min_slots'] ?? $service['minimum_slots'] ?? website_config('pricing', [])['standard_min_slots'] ?? 16);
|
||||
$maxSlots = (int)($service['max_slots'] ?? $service['maximum_slots'] ?? 0);
|
||||
$selectedSlots = max((int)$minSlots, (int)($_POST['slots'] ?? $minSlots));
|
||||
?>
|
||||
<section class="page-heading">
|
||||
<div class="container">
|
||||
<h1>Configure <?= website_escape($serviceName) ?></h1>
|
||||
<p><?= website_escape($description !== '' ? $description : 'Review this server plan before checkout. Pricing and provisioning are validated server-side from the active catalog.') ?></p>
|
||||
<p><?= website_escape($description !== '' ? $description : 'Configure this server package before adding it to your cart. Login is only required when you proceed to checkout.') ?></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<?php if ($error !== ''): ?>
|
||||
<div class="alert warning"><?= website_escape($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="summary-grid">
|
||||
<article class="summary-card">
|
||||
<h3>Plan</h3>
|
||||
|
|
@ -52,16 +54,35 @@ $maxSlots = (int)($service['max_slots'] ?? $service['maximum_slots'] ?? 0);
|
|||
</article>
|
||||
<article class="summary-card">
|
||||
<h3>Checkout boundary</h3>
|
||||
<p>This website validates the catalog service and keeps your shared account identity. Payment and final provisioning must complete server-side before the Panel creates a running game server.</p>
|
||||
<p>You can add this server to your cart before logging in. Payment and final provisioning must complete server-side before the Panel creates a running game server.</p>
|
||||
<p class="muted">The legacy <code>billing/order.php</code> route is no longer used.</p>
|
||||
</article>
|
||||
</div>
|
||||
<div class="alert info" style="margin-top: 18px;">
|
||||
Checkout/payment handlers are not present in this repository checkout. Contact support to complete this order or connect the active payment module before enabling public checkout.
|
||||
</div>
|
||||
<div class="card-actions" style="margin-top: 18px;">
|
||||
<a class="button button-primary" href="<?= website_escape(website_url('support.php')) ?>">Contact Support</a>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_url('serverlist.php')) ?>">Back to Catalog</a>
|
||||
</div>
|
||||
|
||||
<form class="website-form summary-card" method="post" action="<?= website_escape(website_order_url((int)$service['service_id'])) ?>" style="margin-top: 18px;">
|
||||
<h3>Server configuration</h3>
|
||||
<label for="slots">Slots</label>
|
||||
<input id="slots" name="slots" type="number" min="<?= website_escape((string)$minSlots) ?>"<?= $maxSlots > 0 ? ' max="' . website_escape((string)$maxSlots) . '"' : '' ?> value="<?= website_escape((string)$selectedSlots) ?>" required>
|
||||
|
||||
<label for="location_id">Location</label>
|
||||
<select id="location_id" name="location_id" required>
|
||||
<?php foreach ($locations as $locationId => $locationName): ?>
|
||||
<option value="<?= website_escape((string)$locationId) ?>"<?= (string)($_POST['location_id'] ?? '') === (string)$locationId ? ' selected' : '' ?>><?= website_escape($locationName) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<label for="duration_months">Billing duration</label>
|
||||
<select id="duration_months" name="duration_months" required>
|
||||
<?php foreach ([1, 3, 6, 12] as $duration): ?>
|
||||
<option value="<?= website_escape((string)$duration) ?>"<?= (int)($_POST['duration_months'] ?? 1) === $duration ? ' selected' : '' ?>><?= website_escape((string)$duration) ?> month<?= $duration === 1 ? '' : 's' ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<p class="muted">The website stores this selection in your cart and revalidates service, slots, location, and price during checkout.</p>
|
||||
<div class="card-actions">
|
||||
<button class="button button-primary" type="submit">Add to Cart</button>
|
||||
<a class="button button-secondary" href="<?= website_escape(website_url('serverlist.php')) ?>">Back to Catalog</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -4,78 +4,15 @@ declare(strict_types=1);
|
|||
|
||||
require_once __DIR__ . '/includes/bootstrap.php';
|
||||
|
||||
$db = website_db();
|
||||
$prefix = website_table_prefix();
|
||||
$destination = trim((string)($_GET['destination'] ?? 'panel'));
|
||||
$returnPath = website_safe_return_path((string)($_GET['return'] ?? ''), 'home.php?m=dashboard&p=dashboard');
|
||||
|
||||
if (!$db instanceof mysqli || $prefix === '') {
|
||||
http_response_code(503);
|
||||
echo 'SSO is temporarily unavailable.';
|
||||
website_log_activity('Deprecated website SSO endpoint redirected to direct login/navigation', (int)($_SESSION['website_user_id'] ?? 0), 'sso_deprecated_redirect');
|
||||
|
||||
if ($destination === 'panel') {
|
||||
header('Location: ' . panel_url($returnPath), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
$token = trim((string)($_GET['token'] ?? ''));
|
||||
if ($token !== '') {
|
||||
if (!function_exists('gsp_sso_validate_token')) {
|
||||
http_response_code(503);
|
||||
echo 'SSO is not configured.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$validation = gsp_sso_validate_token($db, $prefix, $token, 'website');
|
||||
if (empty($validation['success'])) {
|
||||
website_log_activity('Website SSO failed: ' . (string)$validation['error'], 0, 'sso_failure');
|
||||
http_response_code(403);
|
||||
echo website_escape((string)$validation['error']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = website_panel_user_by_id((int)$validation['user_id']);
|
||||
if (!$user) {
|
||||
http_response_code(403);
|
||||
echo 'SSO user is no longer available.';
|
||||
exit;
|
||||
}
|
||||
|
||||
website_set_user_session($user);
|
||||
website_log_activity('Website SSO login succeeded for ' . (string)$user['users_login'], (int)$user['user_id'], 'sso_success');
|
||||
$returnPath = website_safe_return_path((string)$validation['return_path'], 'index.php');
|
||||
header('Location: ' . website_url($returnPath), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
$destination = trim((string)($_GET['destination'] ?? ''));
|
||||
if ($destination !== 'panel') {
|
||||
http_response_code(400);
|
||||
echo 'Invalid SSO destination.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = website_current_user();
|
||||
if (!$user) {
|
||||
$_SESSION['website_login_return'] = 'panel';
|
||||
header('Location: ' . website_login_url('panel'), true, 302);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!function_exists('gsp_sso_create_token')) {
|
||||
http_response_code(503);
|
||||
echo 'SSO is not configured.';
|
||||
exit;
|
||||
}
|
||||
|
||||
$returnPath = website_safe_return_path((string)($_GET['return'] ?? 'home.php?m=dashboard&p=dashboard'), 'home.php?m=dashboard&p=dashboard');
|
||||
$newToken = gsp_sso_create_token($db, $prefix, (int)$user['user_id'], 'website', 'panel', $returnPath, 60);
|
||||
if ($newToken === null) {
|
||||
http_response_code(403);
|
||||
echo 'SSO requires HTTPS.';
|
||||
exit;
|
||||
}
|
||||
|
||||
website_log_activity('Website-to-panel SSO token created', (int)$user['user_id'], 'sso_token_created');
|
||||
$panelSsoUrl = trim((string)website_config('panel_sso_url', ''));
|
||||
if ($panelSsoUrl === '') {
|
||||
$panelSsoUrl = panel_url('sso.php');
|
||||
}
|
||||
|
||||
header('Location: ' . $panelSsoUrl . '?token=' . rawurlencode($newToken), true, 302);
|
||||
header('Location: ' . website_url('index.php'), true, 302);
|
||||
exit;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue