From 3066d9c75c1494b8d74ffe5828b1661a91ea7289 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 2 May 2026 12:23:23 +0000 Subject: [PATCH] fix(billing): address code review issues - ALTER TABLE syntax, null period handling, type detection Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/e8da2cb7-dbf1-4296-b25d-766f8e099581 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com> --- modules/billing/cart.php | 6 ++--- modules/billing/classes/BillingRepository.php | 11 +++++++++- modules/billing/classes/BillingService.php | 11 +++++++++- modules/billing/includes/config.inc.php | 1 + modules/billing/module.php | 22 +++++++++---------- modules/billing/webhook.php | 8 +++---- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/modules/billing/cart.php b/modules/billing/cart.php index b7adb6f5..d84c5ca0 100644 --- a/modules/billing/cart.php +++ b/modules/billing/cart.php @@ -250,9 +250,9 @@ if ($applied_coupon && $coupon_discount_percent > 0) { $final_amount = $total_amount - $discount_amount; -// PayPal configuration -$sandbox = true; -$client_id = 'AfvY_C2zA_hTHxHq7TIhtOeub4xBdySYrt_Hjj3d_WYQwjWI9NfOAVOTeResx2rgZ_nP5tOoxQSAHw8c'; +// PayPal configuration (from config) +$sandbox = $paypal_sandbox ?? true; +$client_id = $paypal_client_id ?? ''; // Prepare PayPal items $paypal_items = []; diff --git a/modules/billing/classes/BillingRepository.php b/modules/billing/classes/BillingRepository.php index f969f3db..fc7de877 100644 --- a/modules/billing/classes/BillingRepository.php +++ b/modules/billing/classes/BillingRepository.php @@ -200,7 +200,16 @@ class BillingRepository if (array_key_exists($col, $data)) { $set[] = "`{$col}` = ?"; $params[] = $data[$col]; - $types .= is_int($data[$col]) ? 'i' : 's'; + $val = $data[$col]; + if ($val === null) { + $types .= 's'; // NULL binds safely as string in mysqli + } elseif (is_int($val)) { + $types .= 'i'; + } elseif (is_float($val)) { + $types .= 'd'; + } else { + $types .= 's'; + } } } if (empty($set)) return false; diff --git a/modules/billing/classes/BillingService.php b/modules/billing/classes/BillingService.php index 78faae4d..eeee8aac 100644 --- a/modules/billing/classes/BillingService.php +++ b/modules/billing/classes/BillingService.php @@ -171,7 +171,16 @@ class BillingService // If current expiry is in the future, extend from it; otherwise reset from period_end $currentExpiry = $home['billing_expires_at'] ?? null; if ($currentExpiry && strtotime($currentExpiry) > time()) { - $currentPeriodSecs = strtotime($invoiceRow['period_end'] ?? $now) - strtotime($invoiceRow['period_start'] ?? $now); + // Calculate the period length from the invoice; fall back to rate_type if dates are missing + $periodStart = $invoiceRow['period_start'] ?? null; + $periodEndVal = $invoiceRow['period_end'] ?? null; + 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); + } $newExpiry = date('Y-m-d H:i:s', strtotime($currentExpiry) + max(86400, $currentPeriodSecs)); } else { $newExpiry = $periodEnd; diff --git a/modules/billing/includes/config.inc.php b/modules/billing/includes/config.inc.php index fac3bb5d..ec877dcb 100644 --- a/modules/billing/includes/config.inc.php +++ b/modules/billing/includes/config.inc.php @@ -34,4 +34,5 @@ $SITE_DATA_DIR = realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data'; $paypal_sandbox = true; // Set to false for live payments $paypal_client_id = ''; // Your PayPal Client ID $paypal_client_secret = ''; // Your PayPal Client Secret +$paypal_webhook_id = ''; // Your PayPal Webhook ID (for webhook signature verification) ?> diff --git a/modules/billing/module.php b/modules/billing/module.php index c2aa5f80..291a063d 100644 --- a/modules/billing/module.php +++ b/modules/billing/module.php @@ -125,18 +125,18 @@ $install_queries[0] = array( ); // Version 2: New columns on billing_invoices, transaction log table, service-to-node mapping +// Each ALTER TABLE is a separate statement because ADD COLUMN IF NOT EXISTS requires MySQL 8.0+. +// The module manager only runs these once (on db_version bump 1->2), so they do not need IF NOT EXISTS. $install_queries[1] = array( - // Add new columns to billing_invoices (IF NOT EXISTS for idempotence) - "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` - ADD COLUMN IF NOT EXISTS `home_id` INT(11) NOT NULL DEFAULT 0 AFTER `service_id`, - ADD COLUMN IF NOT EXISTS `rate_type` ENUM('daily','monthly','yearly') NOT NULL DEFAULT 'monthly' AFTER `invoice_duration`, - ADD COLUMN IF NOT EXISTS `rate_per_player` DECIMAL(15,4) NOT NULL DEFAULT 0 AFTER `rate_type`, - ADD COLUMN IF NOT EXISTS `players` INT(11) NOT NULL DEFAULT 0 AFTER `rate_per_player`, - ADD COLUMN IF NOT EXISTS `period_start` DATETIME NULL AFTER `players`, - ADD COLUMN IF NOT EXISTS `period_end` DATETIME NULL AFTER `period_start`, - ADD COLUMN IF NOT EXISTS `subtotal` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `period_end`, - ADD COLUMN IF NOT EXISTS `total_due` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `subtotal`, - ADD COLUMN IF NOT EXISTS `payment_status` ENUM('unpaid','paid','cancelled','refunded') NOT NULL DEFAULT 'unpaid' AFTER `total_due`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `home_id` INT(11) NOT NULL DEFAULT 0 AFTER `service_id`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `rate_type` ENUM('daily','monthly','yearly') NOT NULL DEFAULT 'monthly' AFTER `invoice_duration`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `rate_per_player` DECIMAL(15,4) NOT NULL DEFAULT 0 AFTER `rate_type`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `players` INT(11) NOT NULL DEFAULT 0 AFTER `rate_per_player`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `period_start` DATETIME NULL AFTER `players`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `period_end` DATETIME NULL AFTER `period_start`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `subtotal` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `period_end`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `total_due` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `subtotal`", + "ALTER TABLE `".OGP_DB_PREFIX."billing_invoices` ADD COLUMN `payment_status` ENUM('unpaid','paid','cancelled','refunded') NOT NULL DEFAULT 'unpaid' AFTER `total_due`", // Payment transaction log — immutable audit trail "CREATE TABLE IF NOT EXISTS `".OGP_DB_PREFIX."billing_transactions` ( diff --git a/modules/billing/webhook.php b/modules/billing/webhook.php index 0691dd3b..bed7d136 100644 --- a/modules/billing/webhook.php +++ b/modules/billing/webhook.php @@ -3,10 +3,10 @@ require_once(__DIR__ . '/includes/config_loader.php'); if (is_file(__DIR__ . '/includes/log.php')) require_once(__DIR__ . '/includes/log.php'); $config = [ - 'sandbox' => true, - 'client_id' => 'AfvY_C2zA_hTHxHq7TIhtOeub4xBdySYrt_Hjj3d_WYQwjWI9NfOAVOTeResx2rgZ_nP5tOoxQSAHw8c', - 'client_secret' => 'EJ216np9cAj9n7KSddez3fLVxGe-zi4oKKKl1YGqPp88XIikr4Qzbxh0XW2as-V6LgdX-upjtQAg9dC0', - 'webhook_id' => '6N620673281740730', + 'sandbox' => $paypal_sandbox ?? true, + 'client_id' => $paypal_client_id ?? '', + 'client_secret' => $paypal_client_secret ?? '', + 'webhook_id' => $paypal_webhook_id ?? '', 'data_dir' => rtrim( (defined('SITE_DATA_DIR') ? SITE_DATA_DIR : '') ?: ($SITE_DATA_DIR ?? ''), DIRECTORY_SEPARATOR