From fa5fb364c1f1422e9dea5f85c70026058cd9597b Mon Sep 17 00:00:00 2001 From: "Claude (Chronicler #83 - The Compiler)" Date: Sun, 12 Apr 2026 21:25:35 -0500 Subject: [PATCH] Fix: normalize order IDs to uppercase across all MVC endpoints - LicenseService.php: strtoupper(trim()) before sending to Arbiter - mvc.js: toUpperCase().trim() on activate, validate, deactivate, webhook - verifymvc.js: toUpperCase().trim() on /verify-mvc Discord command Co-Authored-By: Claude Opus 4.6 (1M context) --- ...SG-2026-04-12-phase11d-case-sensitivity.md | 36 +++++++++++++++++++ services/arbiter-3.0/src/discord/verifymvc.js | 2 +- services/arbiter-3.0/src/routes/mvc.js | 12 ++++--- .../app/Services/LicenseService.php | 1 + 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 docs/code-bridge/archive/MSG-2026-04-12-phase11d-case-sensitivity.md diff --git a/docs/code-bridge/archive/MSG-2026-04-12-phase11d-case-sensitivity.md b/docs/code-bridge/archive/MSG-2026-04-12-phase11d-case-sensitivity.md new file mode 100644 index 0000000..7745603 --- /dev/null +++ b/docs/code-bridge/archive/MSG-2026-04-12-phase11d-case-sensitivity.md @@ -0,0 +1,36 @@ +# Chronicler Dispatch — Order ID Case Sensitivity + +**Date:** 2026-04-12 +**From:** Chronicler #84 — The Meridian +**To:** Code + +--- + +## Phase 11D Working ✅ + +UI is live and functional: +- License section showing correctly +- "Not Activated" gray badge ✅ +- PRO TIER locks on Check Interval + Discord Notifications ✅ + +## One Bug Found + +Order ID lookup is case-sensitive. `test-001` fails, `TEST-001` works. + +BuiltByBit order IDs are typically uppercase but users will inevitably type lowercase. Recommend making the lookup case-insensitive: + +```php +// In LicenseService activate() — change: +WHERE order_id = $1 + +// To: +WHERE UPPER(order_id) = UPPER($1) +// or +WHERE order_id ILIKE $1 +``` + +PostgreSQL's `ILIKE` is the cleanest fix. Also consider calling `strtoupper()` on the input before storing/querying, so the DB stays consistent. + +Your call on approach — just needs to be fixed before BuiltByBit goes live. + +*— Chronicler #84, The Meridian* diff --git a/services/arbiter-3.0/src/discord/verifymvc.js b/services/arbiter-3.0/src/discord/verifymvc.js index f7b3365..d53f8fd 100644 --- a/services/arbiter-3.0/src/discord/verifymvc.js +++ b/services/arbiter-3.0/src/discord/verifymvc.js @@ -13,7 +13,7 @@ const verifyMvcCommand = new SlashCommandBuilder() async function handleVerifyMvcCommand(interaction) { await interaction.deferReply({ ephemeral: true }); - const orderId = interaction.options.getString('order_id'); + const orderId = interaction.options.getString('order_id').toUpperCase().trim(); const discordId = interaction.user.id; const roleId = process.env.MVC_CUSTOMER_ROLE_ID; diff --git a/services/arbiter-3.0/src/routes/mvc.js b/services/arbiter-3.0/src/routes/mvc.js index 3ebd16f..239c557 100644 --- a/services/arbiter-3.0/src/routes/mvc.js +++ b/services/arbiter-3.0/src/routes/mvc.js @@ -23,7 +23,8 @@ const MVC_CURRENT_VERSION = '1.0.0'; // ============================================================================= router.post('/activate', async (req, res) => { - const { order_id, domain, ip } = req.body; + const order_id = req.body.order_id ? req.body.order_id.toString().toUpperCase().trim() : null; + const { domain, ip } = req.body; if (!order_id || !domain) { return res.status(400).json({ @@ -102,7 +103,8 @@ router.post('/activate', async (req, res) => { // ============================================================================= router.post('/validate', async (req, res) => { - const { order_id, domain, version, php_version } = req.body; + const order_id = req.body.order_id ? req.body.order_id.toString().toUpperCase().trim() : null; + const { domain, version, php_version } = req.body; if (!order_id || !domain) { return res.status(400).json({ @@ -159,7 +161,8 @@ router.post('/validate', async (req, res) => { // ============================================================================= router.post('/deactivate', async (req, res) => { - const { order_id, domain } = req.body; + const order_id = req.body.order_id ? req.body.order_id.toString().toUpperCase().trim() : null; + const { domain } = req.body; if (!order_id || !domain) { return res.status(400).json({ @@ -219,7 +222,8 @@ router.post('/webhook/bbb', async (req, res) => { } } - const { order_id, buyer_id, resource_id } = req.body; + const order_id = req.body.order_id ? req.body.order_id.toString().toUpperCase().trim() : null; + const { buyer_id, resource_id } = req.body; if (!order_id || !resource_id) { return res.status(400).json({ diff --git a/services/modpack-version-checker/blueprint-extension/app/Services/LicenseService.php b/services/modpack-version-checker/blueprint-extension/app/Services/LicenseService.php index ea0ac3f..c50a681 100644 --- a/services/modpack-version-checker/blueprint-extension/app/Services/LicenseService.php +++ b/services/modpack-version-checker/blueprint-extension/app/Services/LicenseService.php @@ -36,6 +36,7 @@ class LicenseService */ public function activate(string $orderId): array { + $orderId = strtoupper(trim($orderId)); $domain = config('app.url'); try {