From c7638f240ae92be4239d61f89914c994755b096b Mon Sep 17 00:00:00 2001 From: zhangjianjun Date: Wed, 15 Jul 2026 11:08:43 +0800 Subject: [PATCH] ud --- src/pages/PayPage.tsx | 82 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/pages/PayPage.tsx b/src/pages/PayPage.tsx index 5b536fa..adf17c6 100644 --- a/src/pages/PayPage.tsx +++ b/src/pages/PayPage.tsx @@ -10,9 +10,67 @@ const PAY_METHOD_LIST = [ const ORDER_POLLING_INTERVAL = 2000 const ORDER_POLLING_MAX_COUNT = 60 +const PENDING_PAYMENT_STORAGE_KEY = 'pending-payment-order' +const PENDING_PAYMENT_MAX_AGE = ORDER_POLLING_INTERVAL * ORDER_POLLING_MAX_COUNT type PayMethodInfo = (typeof PAY_METHOD_LIST)[number] +type PendingPayment = { + expiresAt: number + orderId: string +} + +function getPendingPayment(): PendingPayment | null { + try { + const rawPendingPayment = window.localStorage.getItem(PENDING_PAYMENT_STORAGE_KEY) + + if (!rawPendingPayment) { + return null + } + + const pendingPayment: unknown = JSON.parse(rawPendingPayment) + if ( + !pendingPayment || + typeof pendingPayment !== 'object' || + typeof (pendingPayment as PendingPayment).orderId !== 'string' || + typeof (pendingPayment as PendingPayment).expiresAt !== 'number' || + (pendingPayment as PendingPayment).expiresAt <= Date.now() + ) { + window.localStorage.removeItem(PENDING_PAYMENT_STORAGE_KEY) + return null + } + + return pendingPayment as PendingPayment + } catch { + return null + } +} + +function savePendingPayment(orderId: string) { + try { + const pendingPayment: PendingPayment = { + orderId, + expiresAt: Date.now() + PENDING_PAYMENT_MAX_AGE, + } + + window.localStorage.setItem(PENDING_PAYMENT_STORAGE_KEY, JSON.stringify(pendingPayment)) + } catch { + // The current page can still poll when browser storage is unavailable. + } +} + +function clearPendingPayment(orderId?: string) { + try { + const pendingPayment = getPendingPayment() + + if (!orderId || !pendingPayment || pendingPayment.orderId === orderId) { + window.localStorage.removeItem(PENDING_PAYMENT_STORAGE_KEY) + } + } catch { + // Ignore storage cleanup failures. + } +} + function getSupportedPayMethods(goods?: GoodsItem) { if (!goods?.pay_type) { return [] @@ -58,6 +116,7 @@ function PayPage() { const cancelPaymentFlow = useCallback(() => { paymentAttemptRef.current += 1 stopOrderPolling() + clearPendingPayment() if (isMountedRef.current) { setIsPaying(false) @@ -121,6 +180,8 @@ function PayPage() { }, []) useEffect(() => { + isMountedRef.current = true + return () => { isMountedRef.current = false stopOrderPolling() @@ -142,7 +203,11 @@ function PayPage() { const hasSelectionChanged = previousSelection.planId !== selectedPlanId || previousSelection.payType !== selectedPayType - if (isPaying && hasSelectionChanged) { + const hasCompleteSelection = Boolean( + previousSelection.planId && previousSelection.payType && selectedPlanId && selectedPayType, + ) + + if (isPaying && hasCompleteSelection && hasSelectionChanged) { cancelPaymentFlow() } @@ -173,6 +238,7 @@ function PayPage() { if (data.status === '2') { stopOrderPolling() + clearPendingPayment(orderId) navigate('/pay-result?success=true') return } @@ -182,6 +248,7 @@ function PayPage() { } stopOrderPolling() + clearPendingPayment(orderId) if (isMountedRef.current) { setIsPaying(false) } @@ -190,6 +257,7 @@ function PayPage() { if (pollingCountRef.current >= ORDER_POLLING_MAX_COUNT) { stopOrderPolling() + clearPendingPayment(orderId) if (isMountedRef.current) { setIsPaying(false) } @@ -208,6 +276,17 @@ function PayPage() { [navigate, stopOrderPolling], ) + useEffect(() => { + const pendingPayment = getPendingPayment() + + if (!pendingPayment) { + return + } + + setIsPaying(true) + startOrderPolling(pendingPayment.orderId) + }, [startOrderPolling]) + const handlePay = useCallback(async () => { if (isPaying || !selectedPlan || !selectedPayType) { return @@ -246,6 +325,7 @@ function PayPage() { throw new Error('Failed to open Alipay window') } + savePendingPayment(data.orderId) startOrderPolling(data.orderId) return }