This commit is contained in:
zhangjianjun 2026-07-15 11:08:43 +08:00
parent 17413303e4
commit c7638f240a
1 changed files with 81 additions and 1 deletions

View File

@ -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
}