query() and $db->resultQuery() // call str_replace("OGP_DB_PREFIX", $this->table_prefix, $query) at // the PHP level before passing the SQL to MySQL, so the placeholder is // resolved to the real prefix (e.g. 'ogp_billing_orders') even when it // appears inside a single-quoted string in the SQL text. // ----------------------------------------------------------------------- $install_queries[2] = array( // billing_orders: add discount_amount if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_orders' AND COLUMN_NAME = 'discount_amount'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_orders` ADD `discount_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 AFTER `price`"); }, // billing_invoices: add home_id if missing (needed by cron-shop.php join) function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'home_id'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `home_id` INT(11) NOT NULL DEFAULT 0 AFTER `service_id`"); }, // billing_invoices: add discount_amount if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'discount_amount'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `discount_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 AFTER `amount`"); }, // billing_invoices: add billing_status (lifecycle: Active/Invoiced/Expired) if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'billing_status'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `billing_status` VARCHAR(16) NOT NULL DEFAULT 'due' AFTER `status`"); }, // billing_invoices: add rate_type enum if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'rate_type'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `rate_type` ENUM('daily','monthly','yearly') NOT NULL DEFAULT 'monthly' AFTER `invoice_duration`"); }, // billing_invoices: add rate_per_player if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'rate_per_player'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `rate_per_player` DECIMAL(15,4) NOT NULL DEFAULT 0 AFTER `rate_type`"); }, // billing_invoices: add players if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'players'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `players` INT(11) NOT NULL DEFAULT 0 AFTER `rate_per_player`"); }, // billing_invoices: add subtotal if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'subtotal'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `subtotal` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `players`"); }, // billing_invoices: add total_due if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'total_due'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `total_due` DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER `subtotal`"); }, // billing_invoices: add payment_status enum if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'payment_status'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `payment_status` ENUM('unpaid','paid','cancelled','refunded') NOT NULL DEFAULT 'unpaid' AFTER `currency`"); }, // billing_invoices: add coupon_id if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'coupon_id'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `coupon_id` INT(11) NOT NULL DEFAULT 0 AFTER `qty`"); }, // Create billing_config table for cron-shop settings if missing "CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXbilling_config` ( `config_id` INT(11) NOT NULL AUTO_INCREMENT, `game_key` VARCHAR(100) NULL DEFAULT NULL, `enabled` TINYINT(1) NOT NULL DEFAULT 1, `grace_days` INT(11) NOT NULL DEFAULT 0, `delete_after_expired_days` INT(11) NOT NULL DEFAULT 7, `rate_type` ENUM('daily','monthly','yearly') NOT NULL DEFAULT 'monthly', `price_per_player` DECIMAL(10,4) NOT NULL DEFAULT 0.0000, PRIMARY KEY (`config_id`), KEY `game_key` (`game_key`), KEY `enabled` (`enabled`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;", // Create billing_coupons table if missing (older installs using panel.sql may not have it) "CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXbilling_coupons` ( `coupon_id` INT(11) NOT NULL AUTO_INCREMENT, `code` VARCHAR(50) NOT NULL, `name` VARCHAR(255) NOT NULL DEFAULT '', `description` TEXT NULL, `discount_percent` DECIMAL(5,2) NOT NULL DEFAULT 0.00, `usage_type` ENUM('one_time','permanent') NOT NULL DEFAULT 'one_time', `game_filter_type` ENUM('all_games','specific_games') NOT NULL DEFAULT 'all_games', `game_filter_list` TEXT NULL, `max_uses` INT(11) NULL, `current_uses` INT(11) NOT NULL DEFAULT 0, `expires` DATETIME NULL, `created_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_by` INT(11) NULL, `is_active` TINYINT(1) NOT NULL DEFAULT 1, PRIMARY KEY (`coupon_id`), UNIQUE KEY `idx_code` (`code`), KEY `idx_active_expires` (`is_active`,`expires`), KEY `idx_created_by` (`created_by`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;" ); // ----------------------------------------------------------------------- // db_version 3 — Add billing_paypal_webhook_events table for idempotent // webhook event processing. // ----------------------------------------------------------------------- $install_queries[3] = array( "CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXbilling_paypal_webhook_events` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `paypal_event_id` VARCHAR(100) NOT NULL DEFAULT '', `event_type` VARCHAR(100) NOT NULL DEFAULT '', `resource_id` VARCHAR(100) NOT NULL DEFAULT '', `order_id` VARCHAR(100) NOT NULL DEFAULT '', `capture_id` VARCHAR(100) NOT NULL DEFAULT '', `billing_order_id` INT(11) NOT NULL DEFAULT 0, `processing_status` VARCHAR(50) NOT NULL DEFAULT 'received', `raw_json` MEDIUMTEXT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `processed_at` DATETIME NULL, PRIMARY KEY (`id`), UNIQUE KEY `uidx_paypal_event_id` (`paypal_event_id`), KEY `idx_event_type` (`event_type`), KEY `idx_billing_order_id` (`billing_order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" ); // ----------------------------------------------------------------------- // db_version 4 — Add billing_paypal_errors table for checkout error logging. // ----------------------------------------------------------------------- $install_queries[4] = array( "CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXbilling_paypal_errors` ( `id` INT NOT NULL AUTO_INCREMENT, `context` VARCHAR(64) NOT NULL DEFAULT '', `error_code` VARCHAR(128) NOT NULL DEFAULT '', `message` TEXT NULL, `paypal_debug_id` VARCHAR(128) NULL, `order_id` VARCHAR(128) NULL, `capture_id` VARCHAR(128) NULL, `billing_order_id` INT NULL, `user_id` INT NULL, `raw_json` LONGTEXT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_context` (`context`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" ); // ----------------------------------------------------------------------- // db_version 5 — Preserve the unused legacy service/node mapping table by // renaming it to a *_deprecated_backup table instead of dropping it. // ----------------------------------------------------------------------- $install_queries[5] = array( function($db) { $legacy = 'OGP_DB_PREFIXbilling_service_remote_servers'; $backup = 'OGP_DB_PREFIXbilling_service_remote_servers_deprecated_backup'; $legacyCheck = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '{$legacy}'"); if (!$legacyCheck || empty($legacyCheck[0]['cnt']) || (int)$legacyCheck[0]['cnt'] === 0) return true; $backupCheck = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '{$backup}'"); if ($backupCheck && !empty($backupCheck[0]['cnt']) && (int)$backupCheck[0]['cnt'] > 0) return true; return (bool)$db->query("RENAME TABLE `{$legacy}` TO `{$backup}`"); } ); // ----------------------------------------------------------------------- // db_version 6 — Add server_os column to remote_servers for OS-aware // game/service selection in the billing storefront. // Default 'linux' preserves existing behaviour for all current installs. // ----------------------------------------------------------------------- $install_queries[6] = array( function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXremote_servers' AND COLUMN_NAME = 'server_os'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXremote_servers` ADD `server_os` ENUM('linux','windows','any') NOT NULL DEFAULT 'linux' AFTER `display_public_ip`"); } ); // ----------------------------------------------------------------------- // db_version 7 — Ensure period_start and period_end columns exist in // billing_invoices. These columns were defined in the baseline CREATE TABLE // (db_version 1) but no migration was provided for existing installations // that created the table before those columns were added, causing a fatal // "Unknown column 'period_start'" error in add_to_cart.php. // Each callable uses INFORMATION_SCHEMA so it is safe to re-run. // ----------------------------------------------------------------------- $install_queries[7] = array( // billing_invoices: add period_start if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'period_start'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `period_start` DATETIME NULL AFTER `players`"); }, // billing_invoices: add period_end if missing function($db) { $r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXbilling_invoices' AND COLUMN_NAME = 'period_end'"); if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true; return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXbilling_invoices` ADD `period_end` DATETIME NULL AFTER `period_start`"); }, ); ?>