修复: 使用页内请求重定向

This commit is contained in:
moux1024 2025-09-25 17:24:39 +08:00
parent ef428058c1
commit e2eda2ddb8
4 changed files with 87 additions and 142 deletions

View File

@ -2,38 +2,75 @@
import React from "react";
import { TailwindSpinner } from "@/components/common/GlobalLoad";
import { useSearchParams } from "next/navigation";
import { createCheckoutSession, buyTokens } from "@/lib/stripe";
export default function PayRedirectPage() {
const [status, setStatus] = React.useState<string>("Waiting for secure checkout redirect...");
const [status, setStatus] = React.useState<string>("Preparing checkout...");
const [error, setError] = React.useState<string>("");
const searchParams = useSearchParams();
React.useEffect(() => {
const handleMessage = (event: MessageEvent) => {
try {
if (event.origin !== window.location.origin) return;
const data = event.data || {};
if (data?.type === "redirect-to-payment" && typeof data?.url === "string") {
setStatus("Redirecting to Stripe Checkout...");
window.location.href = data.url as string;
} else if (data?.type === "redirect-error") {
setError(typeof data?.message === "string" ? data.message : "Failed to create payment. Please close this page and try again.");
}
} catch {
setError("An error occurred while processing redirect info. Please close this page and try again.");
}
};
window.addEventListener("message", handleMessage);
const timeoutId = window.setTimeout(() => {
const type = (searchParams.get("type") || "").toLowerCase();
if (!type) {
setStatus("");
setError("No redirect instruction received. It may be a network issue or the page was blocked. Please go back and try again.");
}, 15000);
setError("Missing payment type.");
return;
}
return () => {
window.removeEventListener("message", handleMessage);
window.clearTimeout(timeoutId);
const redirectTo = async () => {
try {
if (type === "token") {
const amountStr = searchParams.get("amount");
const pkg = searchParams.get("pkg") || "basic";
const amount = Number(amountStr);
if (!amount || amount <= 0) {
throw new Error("Invalid token amount");
}
setStatus("Creating token purchase session...");
const resp = await buyTokens({ token_amount: amount, package_type: pkg });
if (resp?.successful && resp?.data?.checkout_url) {
setStatus("Redirecting to Stripe Checkout...");
window.location.href = resp.data.checkout_url as string;
return;
}
throw new Error(resp?.message || "Failed to create token checkout session");
}
if (type === "subscription") {
const plan = searchParams.get("plan");
const billing = (searchParams.get("billing") || "month") as "month" | "year";
if (!plan) {
throw new Error("Missing plan name");
}
const currentUser = JSON.parse(localStorage.getItem("currentUser") || "{}");
if (!currentUser?.id) {
throw new Error("Not logged in. Please sign in and try again.");
}
setStatus("Creating subscription session...");
const result = await createCheckoutSession({
user_id: String(currentUser.id),
plan_name: plan,
billing_cycle: billing,
});
if (result?.successful && result?.data?.checkout_url) {
setStatus("Redirecting to Stripe Checkout...");
window.location.href = result.data.checkout_url as string;
return;
}
throw new Error(result?.message || "Failed to create subscription session");
}
throw new Error("Unsupported payment type");
} catch (e: unknown) {
setStatus("");
setError(e instanceof Error ? e.message : "Failed to prepare checkout.");
}
};
}, []);
redirectTo();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams]);
return (
<div data-alt="pay-redirect-page" className="min-h-screen w-full flex items-center justify-center bg-black text-white">

View File

@ -85,53 +85,17 @@ function HomeModule5() {
const handleSubscribe = async (planName: string) => {
setLoadingPlan(planName);
// 先同步打开同域重定向页,避免拦截
const redirectWindow = window.open('/pay-redirect', '_blank');
if (!redirectWindow) {
setLoadingPlan(null);
throw new Error('Unable to open redirect window, please check popup settings');
}
try {
const { createCheckoutSession } = await import("@/lib/stripe");
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
if (!User.id) {
throw new Error("Unable to obtain user ID, please log in again");
}
const result = await createCheckoutSession({
user_id: String(User.id),
plan_name: planName,
billing_cycle: billingType,
});
if (!result.successful || !result.data?.checkout_url) {
throw new Error("create checkout session failed");
}
// 改为直接携带参数打开 pay-redirect由其内部完成创建与跳转
const url = `/pay-redirect?type=subscription&plan=${encodeURIComponent(planName)}&billing=${encodeURIComponent(billingType)}`;
const win = window.open(url, '_blank');
// 通知当前窗口等待支付显示loading模态框
window.postMessage({
type: 'waiting-payment',
paymentType: 'subscription',
}, '*');
// 通过 postMessage 通知新页面执行重定向
redirectWindow.postMessage({
type: 'redirect-to-payment',
url: result.data.checkout_url,
}, window.location.origin);
} catch (error) {
// 通知新页错误信息
try {
redirectWindow.postMessage({
type: 'redirect-error',
message: 'Failed to create checkout session',
}, window.location.origin);
} catch {}
if (!win) {
setLoadingPlan(null);
throw new Error("create checkout session failed, please try again later");
throw new Error('Unable to open redirect window, please check popup settings');
}
};
return (

View File

@ -88,7 +88,7 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
}
};
// 处理Token购买同域新页 + postMessage 方式重定向
// 处理Token购买改为携带参数打开 pay-redirect
const handleBuyTokens = async (tokenAmount: number) => {
if (!currentUser?.id) {
console.error("用户未登录");
@ -99,52 +99,14 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
console.error("Token数量必须大于0");
return;
}
// 先同步打开同域新页面,避免被拦截
const redirectWindow = window.open("/pay-redirect", "_blank");
if (!redirectWindow) {
console.error("无法打开支付重定向页面,可能被浏览器拦截");
return;
}
setIsBuyingTokens(true);
try {
const response = await buyTokens({
token_amount: tokenAmount,
package_type: "basic"
});
if (response.successful && response.data?.checkout_url) {
// 通知当前窗口等待支付标识为Token购买
// 直接打开带参数的 pay-redirect新窗口内自行创建会话并跳转
const url = `/pay-redirect?type=token&amount=${encodeURIComponent(tokenAmount)}&pkg=basic`;
window.open(url, "_blank");
// 通知当前窗口等待支付显示loading模态框
window.postMessage({
type: "waiting-payment",
paymentType: "token"
}, "*");
sessionStorage.setItem('session_id', response.data.session_id);
// 通过 postMessage 通知新页面进行重定向
redirectWindow.postMessage({
type: "redirect-to-payment",
url: response.data.checkout_url
}, window.location.origin);
} else {
console.error("创建Token购买失败:", response.message);
// 通知新页显示错误
redirectWindow.postMessage({
type: "redirect-error",
message: response.message || "创建支付失败"
}, window.location.origin);
}
} catch (error: unknown) {
console.error("Token购买失败:", error);
try {
redirectWindow.postMessage({
type: "redirect-error",
message: "网络或服务异常,请关闭此页重试"
}, window.location.origin);
} catch {}
} finally {
setIsBuyingTokens(false);
}
type: 'waiting-payment',
paymentType: 'subscription',
}, '*');
};
// 处理自定义金额购买

View File

@ -1249,34 +1249,16 @@ function HomeModule5() {
const handleSubscribe = async (planName: string) => {
localStorage.setItem("callBackUrl", pathname);
try {
// 使用新的Checkout Session方案更简单
const { createCheckoutSession, redirectToCheckout } = await import(
"@/lib/stripe"
);
// 从localStorage获取当前用户信息
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
if (!User.id) {
throw new Error("无法获取用户ID请重新登录");
}
// 1. 创建Checkout Session
const result = await createCheckoutSession({
user_id: String(User.id),
plan_name: planName,
billing_cycle: billingType,
});
if (!result.successful || !result.data) {
throw new Error("create checkout session failed");
}
setShowCallbackModal(true);
window.open(result.data.checkout_url, "_blank");
} catch (error) {
throw new Error("create checkout session failed, please try again later");
// 改为直接携带参数打开 pay-redirect由其内部完成创建与跳转
const url = `/pay-redirect?type=subscription&plan=${encodeURIComponent(planName)}&billing=${encodeURIComponent(billingType)}`;
const win = window.open(url, "_blank");
// 通知当前窗口等待支付显示loading模态框
window.postMessage({
type: 'waiting-payment',
paymentType: 'subscription',
}, '*');
if (!win) {
throw new Error("Unable to open redirect window, please check popup settings");
}
};
return (