fix: thank you page 401 error
- Add public /checkout/order/{id} endpoint with order_key validation
- Update checkout redirect to include order_key parameter
- Update ThankYou page to use new public endpoint with key
- Support both guest (via key) and logged-in (via customer_id) access
This commit is contained in:
@@ -243,7 +243,7 @@ export default function Checkout() {
|
||||
});
|
||||
|
||||
toast.success('Order placed successfully!');
|
||||
navigate(`/order-received/${data.order_id}`);
|
||||
navigate(`/order-received/${data.order_id}?key=${data.order_key}`);
|
||||
} else {
|
||||
throw new Error(data.error || 'Failed to create order');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import { useParams, Link, useSearchParams } from 'react-router-dom';
|
||||
import { useThankYouSettings } from '@/hooks/useAppearanceSettings';
|
||||
import Container from '@/components/Layout/Container';
|
||||
import { CheckCircle, ShoppingBag, Package, Truck } from 'lucide-react';
|
||||
@@ -9,17 +9,22 @@ import { apiClient } from '@/lib/api/client';
|
||||
|
||||
export default function ThankYou() {
|
||||
const { orderId } = useParams<{ orderId: string }>();
|
||||
const [searchParams] = useSearchParams();
|
||||
const orderKey = searchParams.get('key');
|
||||
const { template, headerVisibility, footerVisibility, backgroundColor, customMessage, elements, isLoading: settingsLoading } = useThankYouSettings();
|
||||
const [order, setOrder] = useState<any>(null);
|
||||
const [relatedProducts, setRelatedProducts] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchOrderData = async () => {
|
||||
if (!orderId) return;
|
||||
|
||||
try {
|
||||
const orderData = await apiClient.get(`/orders/${orderId}`) as any;
|
||||
// Use public order endpoint with key validation
|
||||
const keyParam = orderKey ? `?key=${orderKey}` : '';
|
||||
const orderData = await apiClient.get(`/checkout/order/${orderId}${keyParam}`) as any;
|
||||
setOrder(orderData);
|
||||
|
||||
// Fetch related products from first order item
|
||||
@@ -30,15 +35,16 @@ export default function ThankYou() {
|
||||
setRelatedProducts(productData.related_products.slice(0, 4));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch order data:', error);
|
||||
} catch (err: any) {
|
||||
console.error('Failed to fetch order data:', err);
|
||||
setError(err.message || 'Failed to load order');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchOrderData();
|
||||
}, [orderId]);
|
||||
}, [orderId, orderKey]);
|
||||
|
||||
if (loading || settingsLoading || !order) {
|
||||
return (
|
||||
|
||||
@@ -32,6 +32,18 @@ class CheckoutController {
|
||||
'callback' => [ new self(), 'get_fields' ],
|
||||
'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ],
|
||||
]);
|
||||
// Public order view endpoint for thank you page
|
||||
register_rest_route($namespace, '/checkout/order/(?P<id>\d+)', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [ new self(), 'get_order' ],
|
||||
'permission_callback' => '__return_true', // Public, validated via order_key
|
||||
'args' => [
|
||||
'key' => [
|
||||
'type' => 'string',
|
||||
'required' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,6 +145,69 @@ class CheckoutController {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Public order view endpoint for thank you page
|
||||
* Validates access via order_key (for guests) or logged-in customer ID
|
||||
* GET /checkout/order/{id}?key=wc_order_xxx
|
||||
*/
|
||||
public function get_order(WP_REST_Request $r): array {
|
||||
$order_id = absint($r['id']);
|
||||
$order_key = sanitize_text_field($r->get_param('key') ?? '');
|
||||
|
||||
if (!$order_id) {
|
||||
return ['error' => __('Invalid order ID', 'woonoow')];
|
||||
}
|
||||
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
return ['error' => __('Order not found', 'woonoow')];
|
||||
}
|
||||
|
||||
// Validate access: order_key must match OR user must be logged in and own the order
|
||||
$valid_key = $order_key && hash_equals($order->get_order_key(), $order_key);
|
||||
$valid_owner = is_user_logged_in() && get_current_user_id() === $order->get_customer_id();
|
||||
|
||||
if (!$valid_key && !$valid_owner) {
|
||||
return ['error' => __('Unauthorized access to order', 'woonoow')];
|
||||
}
|
||||
|
||||
// Build order items
|
||||
$items = [];
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
$items[] = [
|
||||
'id' => $item->get_id(),
|
||||
'product_id' => $product ? $product->get_id() : 0,
|
||||
'name' => $item->get_name(),
|
||||
'qty' => (int) $item->get_quantity(),
|
||||
'price' => (float) $item->get_total() / max(1, $item->get_quantity()),
|
||||
'total' => (float) $item->get_total(),
|
||||
'image' => $product ? wp_get_attachment_image_url($product->get_image_id(), 'thumbnail') : null,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'id' => $order->get_id(),
|
||||
'number' => $order->get_order_number(),
|
||||
'status' => $order->get_status(),
|
||||
'subtotal' => (float) $order->get_subtotal(),
|
||||
'shipping_total' => (float) $order->get_shipping_total(),
|
||||
'tax_total' => (float) $order->get_total_tax(),
|
||||
'total' => (float) $order->get_total(),
|
||||
'currency' => $order->get_currency(),
|
||||
'currency_symbol' => get_woocommerce_currency_symbol($order->get_currency()),
|
||||
'payment_method' => $order->get_payment_method_title(),
|
||||
'billing' => [
|
||||
'first_name' => $order->get_billing_first_name(),
|
||||
'last_name' => $order->get_billing_last_name(),
|
||||
'email' => $order->get_billing_email(),
|
||||
'phone' => $order->get_billing_phone(),
|
||||
],
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit an order:
|
||||
* {
|
||||
|
||||
Reference in New Issue
Block a user