引用这位大佬的代码:https://www.nodeseek.com/post-734017-1
(async function generatePlusHostedLink() {
console.log("⏳ [plus-link] 正在获取 Session Token...");
// ── 1. 获取当前登录的 Access Token ──────────────────────────────────────
let accessToken;
try {
const session = await fetch("/api/auth/session", { credentials: "include" }).then((r) => r.json());
accessToken = session?.accessToken;
if (!accessToken) throw new Error("accessToken 为空");
} catch (e) {
console.error("❌ [plus-link] 获取 Token 失败,请确保已登录 ChatGPT:", e.message);
return;
}
console.log("✅ [plus-link] Token 获取成功");
// ── 2. 构造请求 Payload ──────────────────────────────────────────────────
const payload = {
plan_name: "chatgptplusplan",
billing_details: {
country: "JP", // ← 根据需要改:US/JP/KR/DE/ID 等
currency: "JPY", // ← 对应币种:USD/JPY/KRW/EUR/IDR 等
},
cancel_url: "https://chatgpt.com/#pricing",
// 🔥 首月免费试用优惠(如不需要可删除整个 promo_campaign 块)
promo_campaign: {
promo_campaign_id: "plus-1-month-free",
is_coupon_from_query_param: false,
},
checkout_ui_mode: "hosted",
};
// ── 3. 发送请求 ──────────────────────────────────────────────────────────
console.log("⏳ [plus-link] 正在向 OpenAI 请求支付信息...");
let data;
try {
const response = await fetch(
"https://chatgpt.com/backend-api/payments/checkout",
{
method: "POST",
credentials: "include",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
}
);
data = await response.json();
if (!response.ok) {
console.error("❌ [plus-link] 请求失败,HTTP", response.status);
console.error(data);
return;
}
} catch (e) {
console.error("❌ [plus-link] 网络请求异常:", e.message);
return;
}
// ── 4. 处理结果并执行跳转 (核心修复) ──────────────────────────────────────
const hostedUrl = data?.url || data?.stripe_hosted_url || data?.checkout_url;
if (hostedUrl) {
// 方案 A:成功拿到了直接的 URL
console.log("─".repeat(60));
console.log("✅ [plus-link] 成功生成 Hosted 直接链接!");
console.log("🔗 请点击下方链接支付:");
console.log(hostedUrl);
console.log("─".repeat(60));
// 如果想要自动跳转可以取消下面这行的注释
// window.location.href = hostedUrl;
} else if (data.checkout_session_id && data.publishable_key) {
// 方案 B:OpenAI 强制走了 Custom 模式,利用 session_id 触发原生 Stripe 跳转
console.log("⚠️ [plus-link] 接口返回了 Custom 模式,未能拿到长链接。");
console.log("%c✅ 正在利用 session_id 通过 Stripe.js 进行原生跳转...", "color:#10a37f;font-size:14px");
// 动态加载官方 Stripe.js
if (!window.Stripe) {
await new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = "https://js.stripe.com/v3/";
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
const stripe = window.Stripe(data.publishable_key);
console.log("%c🚀 正在拉起 Stripe 收银台,请稍候...", "color:orange;font-size:16px");
// 触发跳转
stripe.redirectToCheckout({ sessionId: data.checkout_session_id });
} else {
// 方案 C:未知异常格式
console.error("❌ [plus-link] 无法解析支付信息,响应格式异常:", data);
}
})();
