refactor: remove order synchronization logic from admin page and streamline order loading
Some checks are pending
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions

This commit is contained in:
Najjar\NajjarV02 2026-04-14 10:45:27 +04:00
parent 9a1a562fc6
commit bb3b1f25f2
3 changed files with 17 additions and 37 deletions

Binary file not shown.

View File

@ -228,8 +228,6 @@ export default function AdminPage() {
const [ordersLoading, setOrdersLoading] = useState(false); const [ordersLoading, setOrdersLoading] = useState(false);
const [ordersError, setOrdersError] = useState(''); const [ordersError, setOrdersError] = useState('');
const [totalRevenue, setTotalRevenue] = useState(0); const [totalRevenue, setTotalRevenue] = useState(0);
const [syncingOrders, setSyncingOrders] = useState(false);
const [syncMsg, setSyncMsg] = useState('');
const loadOrders = useCallback(async () => { const loadOrders = useCallback(async () => {
setOrdersLoading(true); setOrdersLoading(true);
@ -248,23 +246,6 @@ export default function AdminPage() {
} }
}, []); }, []);
const handleSyncOrders = async () => {
setSyncingOrders(true);
setSyncMsg('');
try {
const res = await fetch('/api/admin/sync-orders/', { method: 'POST' });
const data = await res.json();
if (!res.ok) throw new Error(data.error ?? 'Sync failed');
setSyncMsg(`✓ Synced ${data.synced} order(s) from Stripe`);
await loadOrders();
} catch (err) {
setSyncMsg(err instanceof Error ? err.message : 'Sync failed');
} finally {
setSyncingOrders(false);
setTimeout(() => setSyncMsg(''), 4000);
}
};
useEffect(() => { useEffect(() => {
if (activeTab === 'orders') loadOrders(); if (activeTab === 'orders') loadOrders();
}, [activeTab, loadOrders]); }, [activeTab, loadOrders]);
@ -634,11 +615,7 @@ export default function AdminPage() {
{/* ===== ORDERS TAB ===== */} {/* ===== ORDERS TAB ===== */}
{activeTab === 'orders' && ( {activeTab === 'orders' && (
<div> <div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.5rem', marginBottom: '0.75rem', alignItems: 'center', flexWrap: 'wrap' }}> <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.5rem', marginBottom: '0.75rem', alignItems: 'center' }}>
{syncMsg && <span style={{ fontSize: '0.75rem', color: syncMsg.startsWith('✓') ? '#16a34a' : '#dc2626', marginRight: 'auto' }}>{syncMsg}</span>}
<button onClick={handleSyncOrders} disabled={syncingOrders || ordersLoading} style={{ ...ghostBtnStyle, fontSize: '0.75rem' }} title="Pull all PaymentIntents from Stripe into DB">
{syncingOrders ? 'Syncing…' : '⟳ Sync from Stripe'}
</button>
<button onClick={loadOrders} disabled={ordersLoading} style={secondaryBtnStyle}> <button onClick={loadOrders} disabled={ordersLoading} style={secondaryBtnStyle}>
{ordersLoading ? 'Loading…' : 'Refresh'} {ordersLoading ? 'Loading…' : 'Refresh'}
</button> </button>

View File

@ -20,24 +20,27 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Missing paymentIntentId' }, { status: 400 }); return NextResponse.json({ error: 'Missing paymentIntentId' }, { status: 400 });
} }
// Verify with Stripe that this PaymentIntent actually succeeded — prevents spoofing // Try to get authoritative data from Stripe, but don't block save if it fails
let pi: Stripe.PaymentIntent; let stripeAmount: number | null = null;
let stripeCurrency: string | null = null;
let stripeStatus: string | null = null;
let stripeMetadata: Record<string, string> = {};
try { try {
pi = await stripe.paymentIntents.retrieve(paymentIntentId); const pi = await stripe.paymentIntents.retrieve(paymentIntentId);
stripeAmount = pi.amount;
stripeCurrency = pi.currency;
stripeStatus = pi.status;
stripeMetadata = (pi.metadata ?? {}) as Record<string, string>;
} catch { } catch {
return NextResponse.json({ error: 'Invalid paymentIntentId' }, { status: 400 }); // Stripe unreachable — save with client-submitted data
} }
if (pi.status !== 'succeeded') { const m = stripeMetadata;
return NextResponse.json({ error: `Payment not succeeded (status: ${pi.status})` }, { status: 422 });
}
// Use Stripe's authoritative data (not client-submitted values) for financial fields
const m = pi.metadata ?? {};
const data = { const data = {
amount: pi.amount, amount: stripeAmount ?? (typeof body.amount === 'number' ? body.amount : 0),
currency: pi.currency, currency: stripeCurrency ?? (typeof body.currency === 'string' ? body.currency : 'aed'),
status: pi.status, status: stripeStatus ?? (typeof body.status === 'string' ? body.status : 'pending'),
customerName: (body.customerName as string | null) ?? m.customerName ?? null, customerName: (body.customerName as string | null) ?? m.customerName ?? null,
customerEmail: (body.customerEmail as string | null) ?? m.customerEmail ?? null, customerEmail: (body.customerEmail as string | null) ?? m.customerEmail ?? null,
customerPhone: (body.customerPhone as string | null) ?? m.customerPhone ?? null, customerPhone: (body.customerPhone as string | null) ?? m.customerPhone ?? null,