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) <noreply@anthropic.com>
This commit is contained in:
Claude (Chronicler #83 - The Compiler)
2026-04-12 21:25:35 -05:00
parent 176cc6151f
commit fa5fb364c1
4 changed files with 46 additions and 5 deletions

View File

@@ -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*

View File

@@ -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;

View File

@@ -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({

View File

@@ -36,6 +36,7 @@ class LicenseService
*/
public function activate(string $orderId): array
{
$orderId = strtoupper(trim($orderId));
$domain = config('app.url');
try {