/** * 支付状态轮询 * - 手机 H5:传入 orderId(点击支付后由 state 传入),直接轮询,不依赖 sessionStorage/cookie * - PC/微信:不传 orderId,使用 cookie 轮询(兼容旧逻辑) */ import { useEffect, useRef, useState } from "react"; const COOKIE_NAME = "nomadvip_pay_order"; const POLL_INTERVAL = 1000; const MAX_POLLS = 160; // ~5 分钟 const MAX_AGE_MS = 15 * 60 * 1000; function getCookie(name: string): string | null { if (typeof document === "undefined") return null; const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`)); return match ? decodeURIComponent(match[1]) : null; } function clearCookie(name: string): void { if (typeof document === "undefined") return; document.cookie = `${name}=; path=/; max-age=0`; } function parsePayOrderCookie(raw: string | null): [string | null, boolean] { if (!raw?.trim()) return [null, false]; const parts = raw.split("|"); const orderId = parts[0]?.trim() || raw.trim(); const ts = parts[1] ? parseInt(parts[1], 10) : NaN; if (!orderId) return [null, false]; if (Number.isNaN(ts)) return [orderId, false]; if (Date.now() - ts > MAX_AGE_MS) return [orderId, false]; return [orderId, true]; } export function usePayStatusPoll( onPaid: (orderId?: string) => void, onTimeout?: () => void, /** 手机 H5:点击支付后传入的 orderId,直接轮询,不依赖 sessionStorage/cookie */ orderIdFromState?: string | null ): boolean { const [polling, setPolling] = useState(false); const onPaidRef = useRef(onPaid); const onTimeoutRef = useRef(onTimeout); onPaidRef.current = onPaid; onTimeoutRef.current = onTimeout; useEffect(() => { const orderId = orderIdFromState !== undefined ? (orderIdFromState?.trim() || null) : (() => { const raw = getCookie(COOKIE_NAME); const [oid, valid] = parsePayOrderCookie(raw); return valid ? oid : null; })(); if (!orderId) { if (orderIdFromState === undefined && getCookie(COOKIE_NAME)) clearCookie(COOKIE_NAME); return; } setPolling(true); let count = 0; const timer = setInterval(async () => { count++; if (count > MAX_POLLS) { clearInterval(timer); clearCookie(COOKIE_NAME); setPolling(false); onTimeoutRef.current?.(); return; } try { const res = await fetch(`/api/pay/status?order_id=${encodeURIComponent(orderId)}`); const data = await res.json(); if (data?.ok && data?.paid) { clearInterval(timer); clearCookie(COOKIE_NAME); setPolling(false); onPaidRef.current(orderId); } } catch { // 继续轮询 } }, POLL_INTERVAL); return () => clearInterval(timer); }, [orderIdFromState]); return polling; }