forked from 77media/video-flow
修复 使用新窗口重定向到支付页,防止拦截
This commit is contained in:
parent
d4bb1e7713
commit
7fefdc1b0d
52
app/pay-redirect/page.tsx
Normal file
52
app/pay-redirect/page.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function PayRedirectPage() {
|
||||||
|
const [status, setStatus] = React.useState<string>("等待安全收银台跳转...");
|
||||||
|
const [error, setError] = React.useState<string>("");
|
||||||
|
|
||||||
|
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("即将跳转到 Stripe 收银台...");
|
||||||
|
window.location.href = data.url as string;
|
||||||
|
} else if (data?.type === "redirect-error") {
|
||||||
|
setError(typeof data?.message === "string" ? data.message : "创建支付失败,请关闭此页重试");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setError("处理跳转信息时发生错误,请关闭此页重试");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", handleMessage);
|
||||||
|
// 超时兜底:若 15 秒内未收到跳转指令,提示用户
|
||||||
|
const timeoutId = window.setTimeout(() => {
|
||||||
|
setStatus("");
|
||||||
|
setError("未收到跳转指令,可能网络异常或页面被拦截。请返回重试。");
|
||||||
|
}, 15000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("message", handleMessage);
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-alt="pay-redirect-page" className="min-h-screen w-full flex items-center justify-center bg-black text-white">
|
||||||
|
<div data-alt="pay-redirect-card" className="max-w-md w-full p-6 rounded-xl border border-white/10 bg-white/5 backdrop-blur">
|
||||||
|
<h1 className="text-lg font-semibold mb-2">正在准备跳转</h1>
|
||||||
|
{status && <p className="text-sm text-white/80 mb-2">{status}</p>}
|
||||||
|
{error && <p className="text-sm text-red-300">{error}</p>}
|
||||||
|
{!error && (
|
||||||
|
<p className="text-xs text-white/60 mt-2">如长时间未跳转,请返回原页重试或检查网络。</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -84,39 +84,53 @@ function HomeModule5() {
|
|||||||
}, [plans, billingType]);
|
}, [plans, billingType]);
|
||||||
|
|
||||||
const handleSubscribe = async (planName: string) => {
|
const handleSubscribe = async (planName: string) => {
|
||||||
setLoadingPlan(planName); // 设置加载状态
|
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 {
|
try {
|
||||||
const { createCheckoutSession, redirectToCheckout } = await import(
|
const { createCheckoutSession } = await import("@/lib/stripe");
|
||||||
"@/lib/stripe"
|
|
||||||
);
|
|
||||||
|
|
||||||
// 从localStorage获取当前用户信息
|
|
||||||
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
const User = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
||||||
|
|
||||||
if (!User.id) {
|
if (!User.id) {
|
||||||
throw new Error("Unable to obtain user ID, please log in again");
|
throw new Error("Unable to obtain user ID, please log in again");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 创建Checkout Session
|
|
||||||
const result = await createCheckoutSession({
|
const result = await createCheckoutSession({
|
||||||
user_id: String(User.id),
|
user_id: String(User.id),
|
||||||
plan_name: planName,
|
plan_name: planName,
|
||||||
billing_cycle: billingType,
|
billing_cycle: billingType,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.successful || !result.data) {
|
if (!result.successful || !result.data?.checkout_url) {
|
||||||
throw new Error("create checkout session failed");
|
throw new Error("create checkout session failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知当前窗口等待支付(显示loading模态框)
|
// 通知当前窗口等待支付(显示loading模态框)
|
||||||
window.postMessage({
|
window.postMessage({
|
||||||
type: "waiting-payment",
|
type: 'waiting-payment',
|
||||||
paymentType: "subscription"
|
paymentType: 'subscription',
|
||||||
}, "*");
|
}, '*');
|
||||||
// 在新标签页中打开Stripe支付页面,保持当前定价页面不变
|
|
||||||
window.open(result.data.checkout_url, '_blank');
|
// 通过 postMessage 通知新页面执行重定向
|
||||||
|
redirectWindow.postMessage({
|
||||||
|
type: 'redirect-to-payment',
|
||||||
|
url: result.data.checkout_url,
|
||||||
|
}, window.location.origin);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setLoadingPlan(null); // 出错时清除加载状态
|
// 通知新页错误信息
|
||||||
|
try {
|
||||||
|
redirectWindow.postMessage({
|
||||||
|
type: 'redirect-error',
|
||||||
|
message: 'Failed to create checkout session',
|
||||||
|
}, window.location.origin);
|
||||||
|
} catch {}
|
||||||
|
setLoadingPlan(null);
|
||||||
throw new Error("create checkout session failed, please try again later");
|
throw new Error("create checkout session failed, please try again later");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -76,7 +76,7 @@ export default function CallbackModal({
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// pending状态,继续等待
|
// pending状态,继续等待
|
||||||
setTimeout(fetchPaymentStatus, 2000)
|
setTimeout(fetchPaymentStatus, 5000)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || 'Failed to get payment status')
|
throw new Error(response.message || 'Failed to get payment status')
|
||||||
|
|||||||
@ -81,7 +81,7 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理Token购买
|
// 处理Token购买(同域新页 + postMessage 方式重定向)
|
||||||
const handleBuyTokens = async (tokenAmount: number) => {
|
const handleBuyTokens = async (tokenAmount: number) => {
|
||||||
if (!currentUser?.id) {
|
if (!currentUser?.id) {
|
||||||
console.error("用户未登录");
|
console.error("用户未登录");
|
||||||
@ -93,6 +93,13 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 先同步打开同域新页面,避免被拦截
|
||||||
|
const redirectWindow = window.open("/pay-redirect", "_blank");
|
||||||
|
if (!redirectWindow) {
|
||||||
|
console.error("无法打开支付重定向页面,可能被浏览器拦截");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsBuyingTokens(true);
|
setIsBuyingTokens(true);
|
||||||
try {
|
try {
|
||||||
const response = await buyTokens({
|
const response = await buyTokens({
|
||||||
@ -102,17 +109,32 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe
|
|||||||
|
|
||||||
if (response.successful && response.data?.checkout_url) {
|
if (response.successful && response.data?.checkout_url) {
|
||||||
// 通知当前窗口等待支付,标识为Token购买
|
// 通知当前窗口等待支付,标识为Token购买
|
||||||
window.postMessage({
|
window.postMessage({
|
||||||
type: "waiting-payment",
|
type: "waiting-payment",
|
||||||
paymentType: "token"
|
paymentType: "token"
|
||||||
}, "*");
|
}, "*");
|
||||||
// 在新标签页中打开Stripe支付页面
|
|
||||||
window.open(response.data.checkout_url, '_blank');
|
// 通过 postMessage 通知新页面进行重定向
|
||||||
|
redirectWindow.postMessage({
|
||||||
|
type: "redirect-to-payment",
|
||||||
|
url: response.data.checkout_url
|
||||||
|
}, window.location.origin);
|
||||||
} else {
|
} else {
|
||||||
console.error("创建Token购买失败:", response.message);
|
console.error("创建Token购买失败:", response.message);
|
||||||
|
// 通知新页显示错误
|
||||||
|
redirectWindow.postMessage({
|
||||||
|
type: "redirect-error",
|
||||||
|
message: response.message || "创建支付失败"
|
||||||
|
}, window.location.origin);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
console.error("Token购买失败:", error);
|
console.error("Token购买失败:", error);
|
||||||
|
try {
|
||||||
|
redirectWindow.postMessage({
|
||||||
|
type: "redirect-error",
|
||||||
|
message: "网络或服务异常,请关闭此页重试"
|
||||||
|
}, window.location.origin);
|
||||||
|
} catch {}
|
||||||
} finally {
|
} finally {
|
||||||
setIsBuyingTokens(false);
|
setIsBuyingTokens(false);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user