refactor(app): tighten chat and sync boundaries
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
"use client";
|
||||
/**
|
||||
* 订阅成功回跳页
|
||||
*
|
||||
* 流程:
|
||||
* 1. 用户在支付服务托管页完成结账
|
||||
* 2. 支付服务重定向到 `/subscription/success`
|
||||
* 3. 这个页面显示 "购买成功" + 调 VIP 状态接口确认权益
|
||||
* 4. 点 "返回订阅页" 跳回 `/subscription`
|
||||
*
|
||||
* 注意:权益发放由后端支付服务链路异步完成。
|
||||
* 此页面轮询几次 VIP 状态接口等待权益完成更新。
|
||||
*/
|
||||
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 { getCurrentVipPaymentStatus } from "@/lib/payment/payment_status";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
|
||||
export default function SubscriptionSuccessPage() {
|
||||
const router = useRouter();
|
||||
const [status, setStatus] = useState<"polling" | "ok" | "error">("polling");
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const hasRefreshed = useRef(false);
|
||||
|
||||
// 轮询 VIP 状态接口等待后端权益发放完成
|
||||
useEffect(() => {
|
||||
if (hasRefreshed.current) return;
|
||||
hasRefreshed.current = true;
|
||||
|
||||
let cancelled = false;
|
||||
const tryRefresh = async (attemptsLeft: number) => {
|
||||
if (cancelled) return;
|
||||
const r = await getCurrentVipPaymentStatus();
|
||||
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 (
|
||||
<MobileShell>
|
||||
<div
|
||||
style={{
|
||||
padding: "2rem 1.5rem",
|
||||
textAlign: "center",
|
||||
minHeight: "60vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: "1rem",
|
||||
}}
|
||||
>
|
||||
{status === "polling" ? (
|
||||
<>
|
||||
<h1 style={{ fontSize: "1.5rem", fontWeight: 600 }}>
|
||||
Processing your subscription…
|
||||
</h1>
|
||||
<p style={{ color: "#666" }}>
|
||||
Your payment was received. We are confirming the details with
|
||||
our server.
|
||||
</p>
|
||||
</>
|
||||
) : status === "ok" ? (
|
||||
<>
|
||||
<h1 style={{ fontSize: "1.5rem", fontWeight: 600, color: "#1f8b3a" }}>
|
||||
🎉 Subscription activated!
|
||||
</h1>
|
||||
<p style={{ color: "#666" }}>
|
||||
Welcome to VIP. Enjoy unlimited chat and more premium benefits.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push(ROUTES.chat)}
|
||||
style={{
|
||||
padding: "0.75rem 1.5rem",
|
||||
borderRadius: "12px",
|
||||
background: "linear-gradient(135deg, #f96ADE, #f657A0)",
|
||||
color: "white",
|
||||
border: "none",
|
||||
fontSize: "1rem",
|
||||
fontWeight: 600,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Start chatting
|
||||
</button>
|
||||
<Link
|
||||
href={ROUTES.subscription}
|
||||
style={{ color: "#888", fontSize: "0.875rem" }}
|
||||
>
|
||||
Back to subscription
|
||||
</Link>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h1 style={{ fontSize: "1.5rem", fontWeight: 600, color: "#c0392b" }}>
|
||||
Subscription status unclear
|
||||
</h1>
|
||||
<p style={{ color: "#666" }}>
|
||||
We received your payment but could not confirm your VIP
|
||||
status. {errorMessage ?? "Please refresh in a few seconds."}
|
||||
</p>
|
||||
<Link
|
||||
href={ROUTES.subscription}
|
||||
style={{
|
||||
padding: "0.75rem 1.5rem",
|
||||
borderRadius: "12px",
|
||||
background: "#f0f0f0",
|
||||
color: "#333",
|
||||
textDecoration: "none",
|
||||
fontSize: "1rem",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Back to subscription
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</MobileShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user