"use client"; /** * 订阅成功回跳页 * * 流程: * 1. 用户在 Stripe 托管页完成结账 * 2. Stripe 重定向到 `/subscription/success?session_id=xxx` * 3. 这个页面显示 "购买成功" + 调 `getCurrentUser` 刷新 user * 4. 点 "返回订阅页" 跳回 `/subscription` * * 注意:webhook 会异步触发 + 更新 user.isVip + voiceMinutes * 此页面拿到 session_id 后 轮询几次 getCurrentUser 等待 webhook 完成(避免 webhook 还没到 user 就还没更新的情况) */ import { useEffect, useRef, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { MobileShell } from "@/app/_components/core/mobile-shell"; import { authRepository } from "@/data/repositories/auth_repository"; import { ROUTES } from "@/router/routes"; export default function SubscriptionSuccessPage() { const router = useRouter(); const [status, setStatus] = useState<"polling" | "ok" | "error">("polling"); const [errorMessage, setErrorMessage] = useState(null); const hasRefreshed = useRef(false); // 轮询 getCurrentUser 等待 webhook 完成 useEffect(() => { if (hasRefreshed.current) return; hasRefreshed.current = true; let cancelled = false; const tryRefresh = async (attemptsLeft: number) => { if (cancelled) return; const r = await authRepository.getCurrentUser(); if (cancelled) return; if (r.success) { if (r.data.isVip) { setStatus("ok"); return; } } else { setErrorMessage(r.error?.message ?? "Unknown error"); } if (attemptsLeft > 0) { setTimeout(() => void tryRefresh(attemptsLeft - 1), 2000); } else { setStatus("error"); } }; void tryRefresh(5); return () => { cancelled = true; }; }, []); return (
{status === "polling" ? ( <>

Processing your subscription…

Your payment was received. We are confirming the details with our server.

) : status === "ok" ? ( <>

🎉 Subscription activated!

Welcome to VIP. Enjoy unlimited chat, free voice minutes, and more.

Back to subscription ) : ( <>

Subscription status unclear

We received your payment but could not confirm your VIP status. {errorMessage ?? "Please refresh in a few seconds."}

Back to subscription )}
); }