// ==UserScript==
// @name GPT Payment Link Helper
// @namespace https://chatgpt.com/
// @version 0.1.3
// @description 在 ChatGPT 页面一键获取支付链接,支持直接跳转或复制链接。
// @match https://chatgpt.com/*
// @match https://chat.openai.com/*
// @grant GM_setClipboard
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
try {
run();
} catch (error) {
console.error('[GPT Payment Link Helper] fatal error:', error);
}
function run() {
const host = window.location.hostname;
const isAllowed =
host === 'chatgpt.com' ||
host === 'chat.openai.com' ||
host.endsWith('.chatgpt.com') ||
host.endsWith('.openai.com');
if (!isAllowed) {
return;
}
window.__gptPaymentHelperInjected = true;
console.info('[GPT Payment Link Helper] script injected on', window.location.href);
const BUTTON_ID = 'gpt-payment-link-helper-button';
const PANEL_ID = 'gpt-payment-link-helper-panel';
const STYLE_ID = 'gpt-payment-link-helper-style';
let currentCheckoutLink = '';
const payload = {
plan_name: 'chatgptteamplan',
team_plan_data: {
workspace_name: 'Sam Altman',
price_interval: 'month',
seat_quantity: 5
},
billing_details: {
country: 'FR',
currency: 'EUR'
},
promo_campaign: {
promo_campaign_id: 'team-1-month-free',
is_coupon_from_query_param: true
},
checkout_ui_mode: 'custom'
};
function injectStyles() {
if (document.getElementById(STYLE_ID)) {
return;
}
const style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = `
#${BUTTON_ID} {
position: fixed;
right: 20px;
bottom: 20px;
z-index: 2147483647;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 132px;
height: 44px;
padding: 0 18px;
border: 0;
border-radius: 999px;
background: linear-gradient(135deg, #10a37f 0%, #0f8f6f 100%);
color: #ffffff;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.01em;
box-shadow: 0 14px 32px rgba(16, 163, 127, 0.32);
cursor: pointer;
}
#${BUTTON_ID}:disabled {
opacity: 0.7;
cursor: wait;
}
#${PANEL_ID} {
position: fixed;
right: 20px;
bottom: 74px;
z-index: 2147483647;
width: min(360px, calc(100vw - 24px));
padding: 14px;
border: 1px solid rgba(15, 23, 42, 0.12);
border-radius: 18px;
background: rgba(255, 255, 255, 0.96);
color: #0f172a;
box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);
backdrop-filter: blur(14px);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
#${PANEL_ID}[hidden] {
display: none;
}
#${PANEL_ID} .gpt-payment-title {
margin: 0 0 8px;
font-size: 14px;
font-weight: 800;
}
#${PANEL_ID} .gpt-payment-desc {
margin: 0;
color: #475569;
font-size: 12px;
line-height: 1.6;
}
#${PANEL_ID} .gpt-payment-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 14px;
}
#${PANEL_ID} .gpt-payment-action {
flex: 1 1 140px;
min-height: 38px;
padding: 0 12px;
border: 0;
border-radius: 12px;
background: #0f172a;
color: #ffffff;
font-size: 13px;
font-weight: 700;
cursor: pointer;
}
#${PANEL_ID} .gpt-payment-action.secondary {
background: #e2e8f0;
color: #0f172a;
}
#${PANEL_ID} .gpt-payment-action:disabled {
opacity: 0.65;
cursor: not-allowed;
}
#${PANEL_ID} .gpt-payment-status {
margin-top: 12px;
min-height: 20px;
color: #0f172a;
font-size: 12px;
line-height: 1.6;
word-break: break-word;
}
@media (max-width: 640px) {
#${BUTTON_ID} {
right: 12px;
bottom: 12px;
min-width: 118px;
height: 40px;
padding: 0 14px;
font-size: 13px;
}
#${PANEL_ID} {
right: 12px;
bottom: 60px;
width: calc(100vw - 24px);
}
}
`;
(document.head || document.documentElement).appendChild(style);
}
function ensurePanel() {
let panel = document.getElementById(PANEL_ID);
if (panel) {
return panel;
}
if (!document.body) {
return null;
}
panel = document.createElement('div');
panel.id = PANEL_ID;
panel.hidden = true;
panel.innerHTML = `
<div class="gpt-payment-title">GPT Payment Link Helper</div>
<p class="gpt-payment-desc">点击下方按钮后先生成支付链接,再选择直接跳转或复制链接。</p>
<div class="gpt-payment-actions">
<button type="button" class="gpt-payment-action" data-action="open">直接跳转</button>
<button type="button" class="gpt-payment-action secondary" data-action="copy">复制链接</button>
</div>
<div class="gpt-payment-status" data-role="status">请先点击“获取支付链接”。</div>
`;
document.body.appendChild(panel);
return panel;
}
function ensureButton() {
let button = document.getElementById(BUTTON_ID);
if (button) {
return button;
}
if (!document.body) {
return null;
}
button = document.createElement('button');
button.id = BUTTON_ID;
button.type = 'button';
button.textContent = '获取支付链接';
button.style.cssText = 'position:fixed !important;right:20px !important;bottom:20px !important;z-index:2147483647 !important;display:inline-flex !important;align-items:center !important;justify-content:center !important;min-width:132px !important;height:44px !important;padding:0 18px !important;border:0 !important;border-radius:999px !important;background:linear-gradient(135deg,#10a37f 0%,#0f8f6f 100%) !important;color:#fff !important;font-size:14px !important;font-weight:700 !important;letter-spacing:.01em !important;box-shadow:0 14px 32px rgba(16,163,127,.32) !important;cursor:pointer !important;visibility:visible !important;opacity:1 !important;pointer-events:auto !important;';
button.addEventListener('click', () => {
void handleGenerateLink();
});
document.body.appendChild(button);
console.info('[GPT Payment Link Helper] button mounted');
return button;
}
function setStatus(message, isError) {
const panel = ensurePanel();
const status = panel?.querySelector('[data-role="status"]');
if (!status) {
return;
}
status.textContent = message || '';
status.style.color = isError ? '#b91c1c' : '#0f172a';
}
function setActionsDisabled(disabled) {
const panel = ensurePanel();
const actions = panel?.querySelectorAll('.gpt-payment-action');
actions?.forEach((action) => {
action.disabled = disabled;
});
}
function setButtonLoading(loading) {
const button = ensureButton();
if (!button) {
return;
}
button.disabled = loading;
button.textContent = loading ? '生成中...' : '获取支付链接';
}
async function copyText(value) {
if (typeof GM_setClipboard === 'function') {
GM_setClipboard(value);
return true;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(value);
return true;
}
return false;
}
async function createCheckoutLink() {
const sessionResponse = await fetch('/api/auth/session');
const sessionData = await sessionResponse.json();
if (!sessionData.accessToken) {
throw new Error('请先登录 ChatGPT。');
}
const checkoutResponse = await fetch('https://chatgpt.com/backend-api/payments/checkout', {
method: 'POST',
headers: {
Authorization: `Bearer ${sessionData.accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const checkoutData = await checkoutResponse.json();
if (!checkoutData.checkout_session_id) {
throw new Error(checkoutData.detail || JSON.stringify(checkoutData));
}
return `https://chatgpt.com/checkout/openai_ie/${checkoutData.checkout_session_id}`;
}
async function handleGenerateLink() {
const panel = ensurePanel();
if (panel) {
panel.hidden = false;
}
currentCheckoutLink = '';
setButtonLoading(true);
setActionsDisabled(true);
setStatus('正在生成支付链接...', false);
try {
const link = await createCheckoutLink();
currentCheckoutLink = link;
setStatus(`支付链接已生成:${link}`, false);
setActionsDisabled(false);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
setStatus(`生成失败:${message}`, true);
} finally {
setButtonLoading(false);
}
}
async function handleAction(mode) {
if (!currentCheckoutLink) {
setStatus('请先点击“获取支付链接”。', true);
return;
}
if (mode === 'copy') {
try {
await copyText(currentCheckoutLink);
setStatus(`支付链接已复制:${currentCheckoutLink}`, false);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
setStatus(`复制失败:${message}`, true);
}
return;
}
setStatus(`即将跳转:${currentCheckoutLink}`, false);
window.location.href = currentCheckoutLink;
}
function bindPanelActions() {
const panel = ensurePanel();
if (!panel) {
return;
}
const openButton = panel.querySelector('[data-action="open"]');
const copyButton = panel.querySelector('[data-action="copy"]');
if (openButton && !openButton.dataset.bound) {
openButton.dataset.bound = 'true';
openButton.addEventListener('click', () => {
void handleAction('open');
});
}
if (copyButton && !copyButton.dataset.bound) {
copyButton.dataset.bound = 'true';
copyButton.addEventListener('click', () => {
void handleAction('copy');
});
}
setActionsDisabled(!currentCheckoutLink);
}
function mount() {
if (!document.documentElement) {
return false;
}
injectStyles();
if (!document.body) {
return false;
}
ensureButton();
ensurePanel();
bindPanelActions();
return true;
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', mount, { once: true });
}
if (!mount()) {
const timer = window.setInterval(() => {
try {
if (mount()) {
window.clearInterval(timer);
}
} catch (error) {
console.error('[GPT Payment Link Helper] mount retry failed:', error);
}
}, 400);
window.setTimeout(() => {
window.clearInterval(timer);
}, 10000);
}
const observer = new MutationObserver(() => {
try {
mount();
} catch (error) {
console.error('[GPT Payment Link Helper] mutation mount failed:', error);
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
})();
加载评论中...