Files
cozsweet-frontend-nextjs/src/app/subscription/success/page.tsx
T

142 lines
4.3 KiB
TypeScript

"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 { getPaymentRepository } from "@/data/repositories/payment_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<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 getPaymentRepository().getVipStatus();
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>
);
}