@@ -615,24 +597,12 @@ if (!empty($services)) {
value="">
-
-
-
-
-
-
-
-
A service will only appear in the store when Enabled is checked
and at least one server is selected.
+
Price / Month ($) is the canonical billing price used by cart, checkout, and provisioning.
The Game Name and Config XML columns are sourced
from and are read-only
here. To change them, update the game XML config in the panel.
diff --git a/modules/billing/api/capture_order.php b/modules/billing/api/capture_order.php
index 1d5f6a68..a0f6b2cd 100644
--- a/modules/billing/api/capture_order.php
+++ b/modules/billing/api/capture_order.php
@@ -124,19 +124,7 @@ function cap_invoice_ids_from_custom_id(?string $customId): array {
}
function cap_get_duration_metadata(array $invoice): array {
- $duration = strtolower((string)($invoice['invoice_duration'] ?? $invoice['rate_type'] ?? 'month'));
- switch ($duration) {
- case 'day':
- case 'daily':
- return ['invoice_duration' => 'day', 'rate_type' => 'daily', 'days' => 1];
- case 'year':
- case 'yearly':
- return ['invoice_duration' => 'year', 'rate_type' => 'yearly', 'days' => 365];
- case 'month':
- case 'monthly':
- default:
- return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
- }
+ return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
}
function cap_get_end_date(array $invoice, ?string $fromDate = null): string {
diff --git a/modules/billing/checkout_free.php b/modules/billing/checkout_free.php
index 6c9b8f03..b5453fbd 100644
--- a/modules/billing/checkout_free.php
+++ b/modules/billing/checkout_free.php
@@ -111,19 +111,7 @@ require_once __DIR__ . '/classes/BillingService.php';
$repo = new BillingRepository($mysqli, $table_prefix);
$newOrderIds = [];
$duration_meta = static function (array $invoice): array {
- $duration = strtolower((string)($invoice['invoice_duration'] ?? $invoice['rate_type'] ?? 'month'));
- switch ($duration) {
- case 'day':
- case 'daily':
- return ['invoice_duration' => 'day', 'rate_type' => 'daily', 'days' => 1];
- case 'year':
- case 'yearly':
- return ['invoice_duration' => 'year', 'rate_type' => 'yearly', 'days' => 365];
- case 'month':
- case 'monthly':
- default:
- return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
- }
+ return ['invoice_duration' => 'month', 'rate_type' => 'monthly', 'days' => 31];
};
foreach ($invoices as $inv) {
diff --git a/modules/billing/classes/BillingService.php b/modules/billing/classes/BillingService.php
index eeee8aac..bca672ad 100644
--- a/modules/billing/classes/BillingService.php
+++ b/modules/billing/classes/BillingService.php
@@ -33,23 +33,9 @@ class BillingService
{
$qty = max(1, $qty);
$players = max(1, $players);
-
- switch ($rateType) {
- case 'daily':
- $basePrice = (float)($service['price_daily'] ?? 0);
- $periodDays = $qty;
- break;
- case 'yearly':
- $basePrice = (float)($service['price_year'] ?? 0);
- $periodDays = $qty * 365;
- break;
- case 'monthly':
- default:
- $rateType = 'monthly';
- $basePrice = (float)($service['price_monthly'] ?? 0);
- $periodDays = $qty * 31;
- break;
- }
+ $rateType = 'monthly';
+ $basePrice = (float)($service['price_monthly'] ?? 0);
+ $periodDays = $qty * 31;
// price_monthly etc is the per-player per-period rate
$ratePerPlayer = $basePrice;
@@ -163,9 +149,7 @@ class BillingService
$periodEnd = $invoiceRow['period_end'] ?? null;
if (!$periodEnd) {
- $rateType = $invoiceRow['rate_type'] ?? 'monthly';
- $periodMap = ['daily' => '+1 day', 'monthly' => '+31 days', 'yearly' => '+365 days'];
- $periodEnd = date('Y-m-d H:i:s', strtotime($periodMap[$rateType] ?? '+31 days'));
+ $periodEnd = date('Y-m-d H:i:s', strtotime('+31 days'));
}
// If current expiry is in the future, extend from it; otherwise reset from period_end
@@ -177,9 +161,7 @@ class BillingService
if ($periodStart && $periodEndVal) {
$currentPeriodSecs = strtotime($periodEndVal) - strtotime($periodStart);
} else {
- $rateType2 = $invoiceRow['rate_type'] ?? 'monthly';
- $periodSecMap = ['daily' => 86400, 'monthly' => 31 * 86400, 'yearly' => 365 * 86400];
- $currentPeriodSecs = $periodSecMap[$rateType2] ?? (31 * 86400);
+ $currentPeriodSecs = 31 * 86400;
}
$newExpiry = date('Y-m-d H:i:s', strtotime($currentExpiry) + max(86400, $currentPeriodSecs));
} else {
diff --git a/modules/billing/create_servers.php b/modules/billing/create_servers.php
index f304d505..46c8a8c5 100644
--- a/modules/billing/create_servers.php
+++ b/modules/billing/create_servers.php
@@ -1,6 +1,7 @@
mods->mod[0])) {
- $mod_xml = $server_xml->mods->mod[0];
- $modkey = (string)$mod_xml['key'];
- }
- if ($mod_xml === false) {
- $order_failed = true;
- $order_failure_reason = "No installable mod profile exists in XML for home #{$home_id}.";
- } else {
- $installer_name = (string)$mod_xml->installer_name;
- $resolved_mod_cfg_id = intval($selected_mod['mod_cfg_id'] ?? $resolved_mod_cfg_id);
- }
- }
- }
-
- //Get Preinstall commands from xml
- $precmd = !$order_failed ? $server_xml->pre_install : '';
-
-
- //Get Postinstall commands from xml
- $postcmd = !$order_failed ? $server_xml->post_install : '';
-
-
//Enable FTP account in remote server
if (!$order_failed && $ftp == "enabled")
{
$remote->ftp_mgr("useradd", $home_info['home_id'], $home_info['ftp_password'], $home_info['home_path']);
$db->changeFtpStatus('enabled',$home_info['home_id']);
}
-
- //Install files for this service in the remote server
- if (!$order_failed) {
- $exec_folder_path = clean_path($home_info['home_path'] . "/" . $server_xml->exe_location );
- $exec_path = clean_path($exec_folder_path . "/" . $server_xml->server_exec_name );
- }
- if (!$order_failed && (string)$server_xml->installer === "steamcmd" && !empty((string)$installer_name) )
- {
- if( preg_match("/win32/", $server_xml->game_key) OR preg_match("/win64/", $server_xml->game_key) )
- $cfg_os = "windows";
- elseif( preg_match("/linux/", $server_xml->game_key) )
- $cfg_os = "linux";
-
- // Some games like L4D2 require anonymous login
- if(!empty($mod_xml->installer_login)){
- $login = $mod_xml->installer_login;
- $pass = '';
- }else{
- $login = $settings['steam_user'];
- $pass = $settings['steam_pass'];
- }
-
- $modname = ( $installer_name == '90' and !preg_match("/(cstrike|valve)/", $modkey) ) ? $modkey : '';
- $betaname = isset($mod_xml->betaname) ? $mod_xml->betaname : '';
- $betapwd = isset($mod_xml->betapwd) ? $mod_xml->betapwd : '';
- $arch = isset($mod_xml->steam_bitness) ? $mod_xml->steam_bitness : '';
-
- $remote->steam_cmd( $home_id,$home_info['home_path'],$installer_name,$modname,
- $betaname,$betapwd,$login,$pass,$settings['steam_guard'],
- $exec_folder_path,$exec_path,$precmd,$postcmd,$cfg_os,'',$arch);
- }
- elseif (!$order_failed)
- {
- // No SteamCMD installer — run pre/post install scripts only.
- if (!empty((string)$precmd)) {
- $result = $remote->exec((string)$precmd);
- if ($result === NULL)
- $db->logger("Script-only install: pre_install script returned no output for home_id $home_id");
- }
- if (!empty((string)$postcmd)) {
- $result = $remote->exec((string)$postcmd);
- if ($result === NULL)
- $db->logger("Script-only install: post_install script returned no output for home_id $home_id");
+ if (!$order_failed) {
+ $autoInstall = gamemanager_trigger_update_install(
+ $db,
+ $home_info,
+ intval($mod_id),
+ array('settings' => $settings)
+ );
+ $mod_id = intval($autoInstall['mod_id'] ?? $mod_id);
+ if (empty($autoInstall['ok'])) {
+ $order_failed = true;
+ $order_failure_reason = "Server files have not been installed yet. " . ($autoInstall['message'] ?? 'Auto install could not be started.');
}
}
if (!$order_failed) {
@@ -529,54 +455,18 @@ function exec_ogp_module()
}
$end_date_str = date('Y-m-d H:i:s', $existing_end);
}
- elseif ($order['invoice_duration'] == "day")
+ else
{
-
- if(empty($order['end_date']) || $order['end_date'] === NULL){
- $end_date = strtotime('+'.$order['qty'].' day');
+ $qty_days = max(1, intval($order['qty'])) * 31;
+ if (empty($order['end_date']) || $order['end_date'] === NULL) {
+ $end_date = strtotime('+' . $qty_days . ' day');
+ } else {
+ $current_end = strtotime($order['end_date']);
+ if ($current_end === false) {
+ $current_end = time();
+ }
+ $end_date = strtotime('+' . $qty_days . ' day', $current_end);
}
- else{
- //this is a renewel, start from end of previous order
- $current_end = strtotime($order['end_date']);
- if ($current_end === false) {
- $current_end = time(); // fallback to now if date is invalid
- }
- $end_date = strtotime('+'.$order['qty'].' day', $current_end);
- }
-
- }
- elseif ($order['invoice_duration'] == "month")
- {
- // this is a new order
- if(empty($order['end_date']) || $order['end_date'] === NULL){
- $end_date = strtotime('+'.(intval($order['qty']) * 31).' day');
-
- }
- else{
- //this is a renewel, start from end of previous order
- $current_end = strtotime($order['end_date']);
- if ($current_end === false) {
- $current_end = time(); // fallback to now if date is invalid
- }
- $end_date = strtotime('+'.(intval($order['qty']) * 31).' day', $current_end);
- }
- }
- elseif ($order['invoice_duration'] == "year")
- {
- // this is a new order
- if(empty($order['end_date']) || $order['end_date'] === NULL){
- $end_date = strtotime('+'.$order['qty'].' year');
- }
- else{
- //this is a renewel, start from end of previous order
- $current_end = strtotime($order['end_date']);
- if ($current_end === false) {
- $current_end = time(); // fallback to now if date is invalid
- }
- $end_date = strtotime('+'.$order['qty'].' year', $current_end);
-
- }
-
}
if (!isset($end_date_str)) {
$end_date_str = date('Y-m-d H:i:s', $end_date);
diff --git a/modules/billing/payment_success.php b/modules/billing/payment_success.php
index f04de5f4..18ed9dce 100644
--- a/modules/billing/payment_success.php
+++ b/modules/billing/payment_success.php
@@ -176,7 +176,7 @@ if ($db && $user_id > 0) {
What Happens Next?
✓ Payment Confirmed: Your payment has been captured by PayPal
-
⚙️ Server Provisioning: Your game server(s) will be automatically created when you log into the panel
+
⚙️ Server Provisioning: Your game server(s) are queued for automatic install now; if a node is unavailable they remain clearly marked as pending install
📧 Email Notification: You'll receive a confirmation email with your order details
🎮 Access Your Servers: Log into the Game Server Panel to manage your new servers