Merge pull request #129 from GameServerPanel/copilot/cleanup-checkout-fixes
This commit is contained in:
commit
4e4df11e06
6 changed files with 158 additions and 37 deletions
|
|
@ -55,8 +55,6 @@ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||||||
<a class="gsw-btn" href="admin_payments.php">Transaction Log</a>
|
<a class="gsw-btn" href="admin_payments.php">Transaction Log</a>
|
||||||
<a class="gsw-btn" href="admin_coupons.php">Manage Coupons</a>
|
<a class="gsw-btn" href="admin_coupons.php">Manage Coupons</a>
|
||||||
<a class="gsw-btn" href="admin_config.php">Edit Site Config</a>
|
<a class="gsw-btn" href="admin_config.php">Edit Site Config</a>
|
||||||
<a class="gsw-btn" href="admin_xml_editor.php">XML Config Editor</a>
|
|
||||||
<a class="gsw-btn" href="docs/xml_notes.php">XML Config Guide</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
|
||||||
|
|
@ -160,29 +160,120 @@ $coupons_result = mysqli_query($db, "SELECT * FROM {$table_prefix}billing_coupon
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="css/header.css">
|
<link rel="stylesheet" href="css/header.css">
|
||||||
<style>
|
<style>
|
||||||
.coupon-form { background: #f5f5f5; padding: 20px; margin: 20px 0; border-radius: 5px; }
|
/* Coupon admin — dark-theme overrides */
|
||||||
|
.coupon-form {
|
||||||
|
background: rgba(0,0,0,0.35);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
.form-group { margin-bottom: 15px; }
|
.form-group { margin-bottom: 15px; }
|
||||||
.form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
|
.form-group label {
|
||||||
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 8px; box-sizing: border-box; }
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #e8e8e8;
|
||||||
|
}
|
||||||
|
.form-group input,
|
||||||
|
.form-group select,
|
||||||
|
.form-group textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 9px 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #11141f;
|
||||||
|
color: #f0f0f0;
|
||||||
|
border: 1px solid rgba(255,255,255,0.18);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 0.97rem;
|
||||||
|
}
|
||||||
|
.form-group input::placeholder,
|
||||||
|
.form-group textarea::placeholder {
|
||||||
|
color: rgba(255,255,255,0.4);
|
||||||
|
}
|
||||||
|
.form-group input:focus,
|
||||||
|
.form-group select:focus,
|
||||||
|
.form-group textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 2px rgba(102,126,234,0.25);
|
||||||
|
}
|
||||||
.form-group textarea { min-height: 60px; }
|
.form-group textarea { min-height: 60px; }
|
||||||
.game-checkboxes { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background: white; }
|
.game-checkboxes {
|
||||||
.game-checkboxes label { display: block; margin: 5px 0; font-weight: normal; }
|
max-height: 200px;
|
||||||
.coupon-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
overflow-y: auto;
|
||||||
.coupon-table th, .coupon-table td { border: 1px solid #ddd; padding: 10px; text-align: left; }
|
border: 1px solid rgba(255,255,255,0.15);
|
||||||
.coupon-table th { background: #4CAF50; color: white; }
|
padding: 10px;
|
||||||
.coupon-table tr:nth-child(even) { background: #f9f9f9; }
|
background: rgba(0,0,0,0.4);
|
||||||
.btn { padding: 8px 16px; margin: 2px; cursor: pointer; border: none; border-radius: 3px; }
|
border-radius: 5px;
|
||||||
.btn-primary { background: #4CAF50; color: white; }
|
}
|
||||||
.btn-warning { background: #ff9800; color: white; }
|
.game-checkboxes label {
|
||||||
.btn-danger { background: #f44336; color: white; }
|
display: block;
|
||||||
.status { padding: 10px; margin: 10px 0; border-radius: 3px; }
|
margin: 5px 0;
|
||||||
.status.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
font-weight: normal;
|
||||||
.status.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
color: #d0d0d0;
|
||||||
.badge { padding: 3px 8px; border-radius: 3px; font-size: 0.85em; }
|
cursor: pointer;
|
||||||
.badge-active { background: #28a745; color: white; }
|
}
|
||||||
.badge-inactive { background: #6c757d; color: white; }
|
.game-checkboxes input[type="checkbox"] {
|
||||||
.badge-onetime { background: #17a2b8; color: white; }
|
width: auto;
|
||||||
.badge-permanent { background: #ffc107; color: black; }
|
margin-right: 6px;
|
||||||
|
background: #11141f;
|
||||||
|
border: 1px solid rgba(255,255,255,0.25);
|
||||||
|
}
|
||||||
|
.coupon-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.coupon-table th,
|
||||||
|
.coupon-table td {
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
padding: 10px 12px;
|
||||||
|
text-align: left;
|
||||||
|
color: #e8e8e8;
|
||||||
|
}
|
||||||
|
.coupon-table th { background: rgba(76,175,80,0.25); color: #fff; }
|
||||||
|
.coupon-table tr:nth-child(even) td { background: rgba(255,255,255,0.03); }
|
||||||
|
.coupon-table tr:hover td { background: rgba(255,255,255,0.06); }
|
||||||
|
.btn { padding: 8px 16px; margin: 2px; cursor: pointer; border: none; border-radius: 4px; font-weight: 600; }
|
||||||
|
.btn-primary { background: linear-gradient(135deg,#667eea,#764ba2); color: #fff; }
|
||||||
|
.btn-warning { background: #ff9800; color: #fff; }
|
||||||
|
.btn-danger { background: #f44336; color: #fff; }
|
||||||
|
.status { padding: 10px 14px; margin: 10px 0; border-radius: 5px; }
|
||||||
|
.status.success { background: rgba(40,167,69,0.2); color: #8dffb0; border: 1px solid rgba(40,167,69,0.35); }
|
||||||
|
.status.error { background: rgba(220,53,69,0.2); color: #ffb3b8; border: 1px solid rgba(220,53,69,0.35); }
|
||||||
|
.badge { padding: 3px 8px; border-radius: 3px; font-size: 0.85em; font-weight: 600; }
|
||||||
|
.badge-active { background: #28a745; color: #fff; }
|
||||||
|
.badge-inactive { background: #6c757d; color: #fff; }
|
||||||
|
.badge-onetime { background: #17a2b8; color: #fff; }
|
||||||
|
.badge-permanent { background: #ffc107; color: #000; }
|
||||||
|
|
||||||
|
/* Inline select/option elements inside table rows */
|
||||||
|
.coupon-table select { background: #11141f; color: #f0f0f0; border: 1px solid rgba(255,255,255,0.18); border-radius: 4px; padding: 4px 6px; }
|
||||||
|
|
||||||
|
/* Mobile: stack table cells */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.coupon-table, .coupon-table thead, .coupon-table tbody,
|
||||||
|
.coupon-table th, .coupon-table td, .coupon-table tr { display: block; }
|
||||||
|
.coupon-table thead tr { display: none; }
|
||||||
|
.coupon-table td {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 45%;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||||
|
}
|
||||||
|
.coupon-table td::before {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
width: 40%;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255,255,255,0.55);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
content: attr(data-label);
|
||||||
|
}
|
||||||
|
.coupon-table tr { border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; margin-bottom: 12px; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
function toggleGameFilter(selectEl) {
|
function toggleGameFilter(selectEl) {
|
||||||
|
|
@ -313,35 +404,35 @@ include(__DIR__ . '/includes/menu.php');
|
||||||
?>
|
?>
|
||||||
<!-- View Row -->
|
<!-- View Row -->
|
||||||
<tr id="view-row-<?php echo $coupon['coupon_id']; ?>">
|
<tr id="view-row-<?php echo $coupon['coupon_id']; ?>">
|
||||||
<td><strong><?php echo h($coupon['code']); ?></strong></td>
|
<td data-label="Code"><strong><?php echo h($coupon['code']); ?></strong></td>
|
||||||
<td><?php echo h($coupon['name']); ?></td>
|
<td data-label="Name"><?php echo h($coupon['name']); ?></td>
|
||||||
<td><?php echo h($coupon['discount_percent']); ?>%</td>
|
<td data-label="Discount"><?php echo h($coupon['discount_percent']); ?>%</td>
|
||||||
<td>
|
<td data-label="Type">
|
||||||
<span class="badge badge-<?php echo $coupon['usage_type'] === 'permanent' ? 'permanent' : 'onetime'; ?>">
|
<span class="badge badge-<?php echo $coupon['usage_type'] === 'permanent' ? 'permanent' : 'onetime'; ?>">
|
||||||
<?php echo h(ucfirst(str_replace('_', ' ', $coupon['usage_type']))); ?>
|
<?php echo h(ucfirst(str_replace('_', ' ', $coupon['usage_type']))); ?>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td data-label="Games">
|
||||||
<?php if ($coupon['game_filter_type'] === 'all_games'): ?>
|
<?php if ($coupon['game_filter_type'] === 'all_games'): ?>
|
||||||
All Games
|
All Games
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php echo count((array)$games_filtered); ?> specific games
|
<?php echo count((array)$games_filtered); ?> specific games
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td data-label="Uses">
|
||||||
<?php if ($coupon['max_uses']): ?>
|
<?php if ($coupon['max_uses']): ?>
|
||||||
<?php echo h($coupon['current_uses']); ?> / <?php echo h($coupon['max_uses']); ?>
|
<?php echo h($coupon['current_uses']); ?> / <?php echo h($coupon['max_uses']); ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php echo h($coupon['current_uses']); ?> (unlimited)
|
<?php echo h($coupon['current_uses']); ?> (unlimited)
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?php echo $coupon['expires'] ? h($coupon['expires']) : 'Never'; ?></td>
|
<td data-label="Expires"><?php echo $coupon['expires'] ? h($coupon['expires']) : 'Never'; ?></td>
|
||||||
<td>
|
<td data-label="Status">
|
||||||
<span class="badge badge-<?php echo $coupon['is_active'] ? 'active' : 'inactive'; ?>">
|
<span class="badge badge-<?php echo $coupon['is_active'] ? 'active' : 'inactive'; ?>">
|
||||||
<?php echo $coupon['is_active'] ? 'Active' : 'Inactive'; ?>
|
<?php echo $coupon['is_active'] ? 'Active' : 'Inactive'; ?>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td data-label="Actions">
|
||||||
<button onclick="editCoupon(<?php echo $coupon['coupon_id']; ?>)" class="btn btn-warning">Edit</button>
|
<button onclick="editCoupon(<?php echo $coupon['coupon_id']; ?>)" class="btn btn-warning">Edit</button>
|
||||||
<form method="POST" style="display:inline;" onsubmit="return confirm('Delete this coupon?');">
|
<form method="POST" style="display:inline;" onsubmit="return confirm('Delete this coupon?');">
|
||||||
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
|
<input type="hidden" name="csrf" value="<?php echo h($csrf); ?>">
|
||||||
|
|
|
||||||
|
|
@ -509,7 +509,7 @@ $siteBase = $protocol . $host;
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" href="images/logo-sm.png" type="image/png">
|
<link rel="icon" href="images/logo-sm.png" type="image/png">
|
||||||
<link rel="apple-touch-icon" href="images/logo-sm.png">
|
<link rel="apple-touch-icon" href="images/logo-sm.png">
|
||||||
<?php if (!$cart_empty): ?>
|
<?php if (!$cart_empty && !empty($client_id)): ?>
|
||||||
<script src="https://www.paypal.com/sdk/js?client-id=<?php echo htmlspecialchars($client_id); ?>¤cy=USD&intent=capture"></script>
|
<script src="https://www.paypal.com/sdk/js?client-id=<?php echo htmlspecialchars($client_id); ?>¤cy=USD&intent=capture"></script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -643,11 +643,30 @@ $siteBase = $protocol . $host;
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="checkout-section">
|
<div class="checkout-section">
|
||||||
<h3>Checkout with PayPal</h3>
|
<h3>Checkout with PayPal</h3>
|
||||||
|
<?php if (empty($client_id)): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<strong>Checkout Unavailable:</strong> PayPal has not been configured for this site.
|
||||||
|
Please contact the site administrator or try again later.
|
||||||
|
<?php
|
||||||
|
// Admin hint: only show config link if the current user is an admin
|
||||||
|
$cart_user_id_check = intval($_SESSION['website_user_id'] ?? 0);
|
||||||
|
$cart_is_admin = false;
|
||||||
|
if ($cart_user_id_check > 0 && $db) {
|
||||||
|
$ar = mysqli_query($db, "SELECT users_role FROM {$table_prefix}users WHERE user_id = " . $cart_user_id_check . " LIMIT 1");
|
||||||
|
if ($ar && ($arow = mysqli_fetch_assoc($ar))) {
|
||||||
|
$cart_is_admin = strtolower($arow['users_role'] ?? '') === 'admin';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($cart_is_admin):
|
||||||
|
?>
|
||||||
|
<br><small><em>Admin: set <code>$paypal_client_id</code> in <a href="/admin_config.php" style="color:inherit;text-decoration:underline;">Site Config</a>.</em></small>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
<p>Click the button below to complete your purchase securely through PayPal.</p>
|
<p>Click the button below to complete your purchase securely through PayPal.</p>
|
||||||
|
|
||||||
<div id="paypal-button-container"></div>
|
<div id="paypal-button-container"></div>
|
||||||
<div id="status-message" class="status-message"></div>
|
<div id="status-message" class="status-message"></div>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a href="/order.php" class="btn btn-secondary">Continue Shopping</a>
|
<a href="/order.php" class="btn btn-secondary">Continue Shopping</a>
|
||||||
<a href="/my_account.php" class="btn btn-secondary">My Account</a>
|
<a href="/my_account.php" class="btn btn-secondary">My Account</a>
|
||||||
|
|
@ -665,7 +684,7 @@ $siteBase = $protocol . $host;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php if ($final_amount > 0.00): ?>
|
<?php if ($final_amount > 0.00 && !empty($client_id)): ?>
|
||||||
<script>
|
<script>
|
||||||
paypal.Buttons({
|
paypal.Buttons({
|
||||||
createOrder: function(data, actions) {
|
createOrder: function(data, actions) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ $nav_prefix = '';
|
||||||
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||||
if (is_string($scriptName) && $scriptName !== '') {
|
if (is_string($scriptName) && $scriptName !== '') {
|
||||||
if (preg_match('#/modules/billing/(.*)$#', $scriptName, $match)) {
|
if (preg_match('#/modules/billing/(.*)$#', $scriptName, $match)) {
|
||||||
|
// Panel-embedded or non-root deployment: depth relative to modules/billing/
|
||||||
$subPath = $match[1];
|
$subPath = $match[1];
|
||||||
if ($subPath !== '') {
|
if ($subPath !== '') {
|
||||||
$depth = substr_count($subPath, '/');
|
$depth = substr_count($subPath, '/');
|
||||||
|
|
@ -23,6 +24,17 @@ if (is_string($scriptName) && $scriptName !== '') {
|
||||||
$nav_prefix = str_repeat('../', $depth);
|
$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 ?: '';
|
$nav_prefix = $nav_prefix ?: '';
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Last Updated at 5:10pm on 2026-05-04
|
Last Updated at 3:18pm on 2026-05-06
|
||||||
|
|
|
||||||
|
|
@ -550,6 +550,7 @@ function exec_ogp_module() {
|
||||||
$game_cfgs = $db->getGameCfgs();
|
$game_cfgs = $db->getGameCfgs();
|
||||||
echo "<h2>".get_lang('game_config_setup')."</h2>\n
|
echo "<h2>".get_lang('game_config_setup')."</h2>\n
|
||||||
<p>".get_lang_f("modify_configs_info",SERVER_CONFIG_LOCATION)."</p>\n
|
<p>".get_lang_f("modify_configs_info",SERVER_CONFIG_LOCATION)."</p>\n
|
||||||
|
<p><a href='https://gameservers.world/docs/xml_notes.php' target='_blank' rel='noopener noreferrer' class='xml-jump-link'>📖 XML Config Reference Guide</a></p>\n
|
||||||
<form action='?m=config_games' method='post'>\n
|
<form action='?m=config_games' method='post'>\n
|
||||||
<p><input id='reset_old_configs' type='checkbox' name='clear_old' value='yes' /><label for='reset_old_configs'>".get_lang('reset_old_configs')."</label></p>\n
|
<p><input id='reset_old_configs' type='checkbox' name='clear_old' value='yes' /><label for='reset_old_configs'>".get_lang('reset_old_configs')."</label></p>\n
|
||||||
<p class='note'>".get_lang('note').": ".get_lang('config_reset_warning')."</p>\n
|
<p class='note'>".get_lang('note').": ".get_lang('config_reset_warning')."</p>\n
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue