diff --git a/src/app/pay/result/page.tsx b/src/app/pay/result/page.tsx index 909c1df..a573dc4 100644 --- a/src/app/pay/result/page.tsx +++ b/src/app/pay/result/page.tsx @@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation'; import { useEffect, useState, Suspense } from 'react'; import { applyLocaleToSearchParams, pickLocaleText, resolveLocale, type Locale } from '@/lib/locale'; import type { PublicOrderStatusSnapshot } from '@/lib/order/status'; +import { buildOrderStatusUrl } from '@/lib/order/status-url'; type WindowWithAlipayBridge = Window & { AlipayJSBridge?: { @@ -54,15 +55,6 @@ function closeCurrentWindow() { }, 250); } -function buildOrderStatusUrl(orderId: string, accessToken?: string | null): string { - const query = new URLSearchParams(); - if (accessToken) { - query.set('access_token', accessToken); - } - const suffix = query.toString(); - return suffix ? `/api/orders/${orderId}?${suffix}` : `/api/orders/${orderId}`; -} - function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale, hasAccessToken: boolean) { if (!order) { return locale === 'en' @@ -142,7 +134,7 @@ function ResultContent() { }, [isPopup]); useEffect(() => { - if (!outTradeNo || !accessToken) { + if (!outTradeNo || !accessToken || accessToken.length < 10) { setLoading(false); return; } diff --git a/src/components/OrderStatus.tsx b/src/components/OrderStatus.tsx index a361bc3..e2a5ab9 100644 --- a/src/components/OrderStatus.tsx +++ b/src/components/OrderStatus.tsx @@ -1,9 +1,10 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import type { Locale } from '@/lib/locale'; import type { PublicOrderStatusSnapshot } from '@/lib/order/status'; +import { buildOrderStatusUrl } from '@/lib/order/status-url'; interface OrderStatusProps { orderId: string; @@ -15,15 +16,6 @@ interface OrderStatusProps { locale?: Locale; } -function buildOrderStatusUrl(orderId: string, statusAccessToken?: string): string { - const query = new URLSearchParams(); - if (statusAccessToken) { - query.set('access_token', statusAccessToken); - } - const suffix = query.toString(); - return suffix ? `/api/orders/${orderId}?${suffix}` : `/api/orders/${orderId}`; -} - function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) { if (order.rechargeSuccess) { return locale === 'en' @@ -84,6 +76,8 @@ export default function OrderStatus({ locale = 'zh', }: OrderStatusProps) { const [currentOrder, setCurrentOrder] = useState(order); + const onStateChangeRef = useRef(onStateChange); + onStateChangeRef.current = onStateChange; useEffect(() => { setCurrentOrder(order); @@ -103,7 +97,7 @@ export default function OrderStatus({ const nextOrder = (await response.json()) as PublicOrderStatusSnapshot; if (cancelled) return; setCurrentOrder(nextOrder); - onStateChange?.(nextOrder); + onStateChangeRef.current?.(nextOrder); } catch { } }; @@ -117,7 +111,7 @@ export default function OrderStatus({ clearInterval(timer); clearTimeout(timeout); }; - }, [orderId, currentOrder.paymentSuccess, currentOrder.rechargeSuccess, onStateChange, statusAccessToken]); + }, [orderId, currentOrder.paymentSuccess, currentOrder.rechargeSuccess, statusAccessToken]); const config = getStatusConfig(currentOrder, locale); const doneLabel = locale === 'en' ? 'Done' : '完成'; diff --git a/src/components/PaymentQRCode.tsx b/src/components/PaymentQRCode.tsx index 6229e1d..28118e0 100644 --- a/src/components/PaymentQRCode.tsx +++ b/src/components/PaymentQRCode.tsx @@ -10,6 +10,7 @@ import { getPaymentIconSrc, getPaymentChannelLabel, } from '@/lib/pay-utils'; +import { buildOrderStatusUrl } from '@/lib/order/status-url'; import { TERMINAL_STATUSES } from '@/lib/constants'; interface PaymentQRCodeProps { @@ -36,15 +37,6 @@ function isVisibleOrderOutcome(data: PublicOrderStatusSnapshot): boolean { return data.paymentSuccess || TERMINAL_STATUSES.has(data.status); } -function buildOrderStatusUrl(orderId: string, statusAccessToken?: string): string { - const query = new URLSearchParams(); - if (statusAccessToken) { - query.set('access_token', statusAccessToken); - } - const suffix = query.toString(); - return suffix ? `/api/orders/${orderId}?${suffix}` : `/api/orders/${orderId}`; -} - export default function PaymentQRCode({ orderId, token, diff --git a/src/lib/order/status-url.ts b/src/lib/order/status-url.ts new file mode 100644 index 0000000..68b71ad --- /dev/null +++ b/src/lib/order/status-url.ts @@ -0,0 +1,15 @@ +/** + * Client-safe utility for building order status API URLs. + * This module must NOT import any server-only modules (config, fs, crypto, etc.). + */ + +const ACCESS_TOKEN_KEY = 'access_token'; + +export function buildOrderStatusUrl(orderId: string, accessToken?: string | null): string { + const query = new URLSearchParams(); + if (accessToken) { + query.set(ACCESS_TOKEN_KEY, accessToken); + } + const suffix = query.toString(); + return suffix ? `/api/orders/${orderId}?${suffix}` : `/api/orders/${orderId}`; +}