From 40fb3640355c6b08416f10d9b4ed315b19067471 Mon Sep 17 00:00:00 2001 From: dwindown Date: Thu, 6 Nov 2025 14:05:18 +0700 Subject: [PATCH] fix: Route priority issue - /order was matched by /{id} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: POST /payments/gateways/order → 404 'gateway_not_found' Root Cause: WordPress REST API matches routes in registration order. The /gateways/order route was registered AFTER /gateways/{id}. So /gateways/order was being matched by /gateways/{id} where id='order'. Then get_gateway('order') returned 'gateway_not_found'. Solution: Register specific routes BEFORE dynamic routes: 1. /gateways (list) 2. /gateways/order (specific - NEW POSITION) 3. /gateways/{id} (dynamic) 4. /gateways/{id}/toggle (dynamic with action) Route Priority Rules: ✅ Specific routes first ✅ Dynamic routes last ✅ More specific before less specific Before: /gateways → OK /gateways/{id} → Matches everything including 'order' /gateways/{id}/toggle → OK (more specific than {id}) /gateways/order → Never reached! After: /gateways → OK /gateways/order → Matches 'order' specifically /gateways/{id} → Matches other IDs /gateways/{id}/toggle → OK Result: ✅ /gateways/order now works correctly ✅ Sorting saves to database ✅ No more 'gateway_not_found' error Files Modified: - PaymentsController.php: Moved /order route before /{id} routes --- admin-spa/src/routes/Settings/Payments.tsx | 20 +++++++-- includes/Api/PaymentsController.php | 51 +++++++++++----------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/admin-spa/src/routes/Settings/Payments.tsx b/admin-spa/src/routes/Settings/Payments.tsx index a393f60..d8d8196 100644 --- a/admin-spa/src/routes/Settings/Payments.tsx +++ b/admin-spa/src/routes/Settings/Payments.tsx @@ -117,18 +117,26 @@ export default function PaymentsPage() { // Initialize order from saved order or gateways React.useEffect(() => { if (gateways.length > 0 && manualOrder.length === 0 && onlineOrder.length === 0) { + console.log('Initializing gateway order...'); + console.log('All gateways:', gateways.map((g: PaymentGateway) => ({ id: g.id, type: g.type }))); + console.log('Saved order:', savedOrder); + // Use saved order if available, otherwise use gateway order if (savedOrder.manual.length > 0) { + console.log('Using saved manual order:', savedOrder.manual); setManualOrder(savedOrder.manual); } else { const manual = gateways.filter((g: PaymentGateway) => g.type === 'manual').map((g: PaymentGateway) => g.id); + console.log('Using gateway manual order:', manual); setManualOrder(manual); } if (savedOrder.online.length > 0) { + console.log('Using saved online order:', savedOrder.online); setOnlineOrder(savedOrder.online); } else { const online = gateways.filter((g: PaymentGateway) => g.type === 'provider' || g.type === 'other').map((g: PaymentGateway) => g.id); + console.log('Using gateway online order:', online); setOnlineOrder(online); } } @@ -189,14 +197,16 @@ export default function PaymentsPage() { // Save order to backend try { + console.log('Saving manual order:', newOrder); await api.post('/payments/gateways/order', { category: 'manual', order: newOrder, }); toast.success('Payment methods reordered'); - } catch (error) { + } catch (error: any) { console.error('Failed to save order:', error); - toast.error('Failed to save order'); + console.error('Error response:', error.response?.data); + toast.error(error.response?.data?.message || 'Failed to save order'); // Revert order on error setManualOrder(manualOrder); } @@ -215,14 +225,16 @@ export default function PaymentsPage() { // Save order to backend try { + console.log('Saving online order:', newOrder); await api.post('/payments/gateways/order', { category: 'online', order: newOrder, }); toast.success('Payment methods reordered'); - } catch (error) { + } catch (error: any) { console.error('Failed to save order:', error); - toast.error('Failed to save order'); + console.error('Error response:', error.response?.data); + toast.error(error.response?.data?.message || 'Failed to save order'); // Revert order on error setOnlineOrder(onlineOrder); } diff --git a/includes/Api/PaymentsController.php b/includes/Api/PaymentsController.php index a4a507c..cf10564 100644 --- a/includes/Api/PaymentsController.php +++ b/includes/Api/PaymentsController.php @@ -42,6 +42,32 @@ class PaymentsController extends WP_REST_Controller { 'schema' => [$this, 'get_gateways_schema'], ]); + // POST /woonoow/v1/payments/gateways/order + // IMPORTANT: Register this BEFORE the dynamic {id} routes to avoid conflicts + register_rest_route($this->namespace, '/' . $this->rest_base . '/gateways/order', [ + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => [$this, 'save_gateway_order'], + 'permission_callback' => [$this, 'check_permission'], + 'args' => [ + 'category' => [ + 'description' => 'Gateway category (manual or online)', + 'type' => 'string', + 'required' => true, + 'enum' => ['manual', 'online'], + ], + 'order' => [ + 'description' => 'Array of gateway IDs in desired order', + 'type' => 'array', + 'required' => true, + 'items' => [ + 'type' => 'string', + ], + ], + ], + ], + ]); + // GET /woonoow/v1/payments/gateways/{id} register_rest_route($this->namespace, '/' . $this->rest_base . '/gateways/(?P[a-zA-Z0-9_-]+)', [ [ @@ -94,31 +120,6 @@ class PaymentsController extends WP_REST_Controller { ], ], ]); - - // POST /woonoow/v1/payments/gateways/order - register_rest_route($this->namespace, '/' . $this->rest_base . '/gateways/order', [ - [ - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => [$this, 'save_gateway_order'], - 'permission_callback' => [$this, 'check_permission'], - 'args' => [ - 'category' => [ - 'description' => 'Gateway category (manual or online)', - 'type' => 'string', - 'required' => true, - 'enum' => ['manual', 'online'], - ], - 'order' => [ - 'description' => 'Array of gateway IDs in desired order', - 'type' => 'array', - 'required' => true, - 'items' => [ - 'type' => 'string', - ], - ], - ], - ], - ]); } /**