Last active
June 30, 2026 04:47
-
-
Save thuongtin/8d30df225f73f0cd103c5cda2eee021c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (async function generateTeamLink() { | |
| // ============================================================ | |
| // CẤU HÌNH (có thể chỉnh tay khi cần) | |
| // ============================================================ | |
| const CONFIG = { | |
| workspaceName: "workspace", // Tên workspace | |
| priceInterval: "month", // "month" hoặc "year" | |
| seatQuantity: 2, // Số ghế (Team tối thiểu 2) | |
| // Ép thủ công nếu không muốn tự nhận biết. Để null = tự động. | |
| forceCurrency: null, // ví dụ "AUD" | |
| forceCountry: null, // ví dụ "AU" | |
| // Selector block giá trong pricing modal (chứa dòng "USD per seat..."). | |
| // Để null sẽ tự quét toàn trang. | |
| currencySelector: '[data-testid="team-pricing-column-cost"]' | |
| }; | |
| // ============================================================ | |
| // BẢNG QUY ĐỔI: TIỀN TỆ -> MÃ QUỐC GIA | |
| // (EUR mặc định "IE" vì dùng chung nhiều nước — đổi forceCountry nếu cần) | |
| // ============================================================ | |
| const CURRENCY_TO_COUNTRY = { | |
| USD: "US", AUD: "AU", GBP: "GB", EUR: "IE", CAD: "CA", | |
| NZD: "NZ", JPY: "JP", CNY: "CN", HKD: "HK", SGD: "SG", | |
| INR: "IN", BRL: "BR", MXN: "MX", CHF: "CH", SEK: "SE", | |
| NOK: "NO", DKK: "DK", PLN: "PL", ZAR: "ZA", AED: "AE", | |
| SAR: "SA", TRY: "TR", KRW: "KR", THB: "TH", IDR: "ID", | |
| MYR: "MY", PHP: "PH", VND: "VN", TWD: "TW", ILS: "IL" | |
| }; | |
| // ============================================================ | |
| // BẢNG KÝ HIỆU -> TIỀN TỆ (thứ tự quan trọng: cụ thể trước, "$" chung sau cùng) | |
| // ============================================================ | |
| const SYMBOL_TO_CURRENCY = [ | |
| { re: /A\$/, cur: "AUD" }, | |
| { re: /US\$/, cur: "USD" }, | |
| { re: /C\$|CA\$/, cur: "CAD" }, | |
| { re: /NZ\$/, cur: "NZD" }, | |
| { re: /HK\$/, cur: "HKD" }, | |
| { re: /S\$/, cur: "SGD" }, | |
| { re: /R\$/, cur: "BRL" }, | |
| { re: /£/, cur: "GBP" }, | |
| { re: /€/, cur: "EUR" }, | |
| { re: /₩/, cur: "KRW" }, | |
| { re: /₫/, cur: "VND" }, | |
| { re: /₹/, cur: "INR" }, | |
| { re: /¥/, cur: "JPY" }, // mặc định JPY (¥ có thể là CNY) | |
| { re: /\$/, cur: "USD" } // fallback chung cho "$" | |
| ]; | |
| // Quét mã ISO trực tiếp trên trang (ưu tiên cao nhất) | |
| const ISO_RE = /\b(USD|AUD|GBP|EUR|CAD|NZD|JPY|CNY|HKD|SGD|INR|BRL|MXN|CHF|SEK|NOK|DKK|PLN|ZAR|AED|SAR|TRY|KRW|THB|IDR|MYR|PHP|VND|TWD|ILS)\b/; | |
| // ---- Lấy mã giảm giá từ URL hiện tại (?promoCode=...) ---- | |
| function getCouponFromURL() { | |
| try { | |
| return new URL(location.href).searchParams.get("promoCode") || null; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| // ---- Tự nhận biết tiền tệ từ trang ---- | |
| function detectCurrency() { | |
| if (CONFIG.forceCurrency) return CONFIG.forceCurrency; | |
| let text = ""; | |
| if (CONFIG.currencySelector) { | |
| const el = document.querySelector(CONFIG.currencySelector); | |
| text = el ? el.textContent : ""; | |
| } | |
| if (!text) text = document.body ? document.body.innerText : ""; | |
| // 1) Ưu tiên mã ISO (USD, AUD, ...) | |
| const iso = text.match(ISO_RE); | |
| if (iso) return iso[1]; | |
| // 2) Rồi tới ký hiệu tiền tệ | |
| for (const { re, cur } of SYMBOL_TO_CURRENCY) { | |
| if (re.test(text)) return cur; | |
| } | |
| return null; | |
| } | |
| // ============================================================ | |
| // BẮT ĐẦU | |
| // ============================================================ | |
| console.log("⏳ Đang lấy Session Token..."); | |
| let accessToken; | |
| try { | |
| const s = await fetch("/api/auth/session").then(r => r.json()); | |
| accessToken = s?.accessToken; | |
| if (!accessToken) throw new Error("accessToken rỗng"); | |
| } catch (e) { | |
| console.error("❌ Lấy Token thất bại:", e.message); | |
| return; | |
| } | |
| console.log("✅ Lấy Token thành công"); | |
| // ---- Mã giảm giá ---- | |
| const coupon = getCouponFromURL(); | |
| if (coupon) { | |
| console.log("🎟️ Mã giảm giá (lấy từ URL):", coupon); | |
| } else { | |
| console.warn("⚠️ Không thấy promoCode trên URL. Hãy mở trang dạng https://chatgpt.com/?promoCode=XXXX"); | |
| } | |
| // ---- Tiền tệ + quốc gia ---- | |
| const currency = detectCurrency(); | |
| if (!currency) { | |
| console.error("❌ Không nhận biết được tiền tệ. Hãy đặt CONFIG.forceCurrency hoặc CONFIG.currencySelector."); | |
| return; | |
| } | |
| const country = CONFIG.forceCountry || CURRENCY_TO_COUNTRY[currency]; | |
| if (!country) { | |
| console.error(`❌ Chưa có mã quốc gia cho tiền tệ ${currency}. Bổ sung vào CURRENCY_TO_COUNTRY hoặc đặt CONFIG.forceCountry.`); | |
| return; | |
| } | |
| console.log(`🌐 Nhận biết: tiền tệ ${currency} → quốc gia ${country}`); | |
| // ---- Payload ---- | |
| const payload = { | |
| plan_name: "chatgptteamplan", | |
| team_plan_data: { | |
| workspace_name: CONFIG.workspaceName, | |
| price_interval: CONFIG.priceInterval, | |
| seat_quantity: CONFIG.seatQuantity | |
| }, | |
| billing_details: { country, currency }, | |
| cancel_url: coupon ? `https://chatgpt.com/?promoCode=${coupon}` : "https://chatgpt.com/", | |
| promo_code: coupon || undefined, // bỏ qua nếu không có | |
| checkout_ui_mode: "hosted" | |
| }; | |
| console.log(`⏳ Đang yêu cầu link Stripe (${country}/${currency})...`); | |
| try { | |
| const resp = await fetch( | |
| "https://chatgpt.com/backend-api/payments/checkout", | |
| { | |
| method: "POST", | |
| headers: { | |
| Authorization: `Bearer ${accessToken}`, | |
| "Content-Type": "application/json" | |
| }, | |
| body: JSON.stringify(payload) | |
| } | |
| ); | |
| const data = await resp.json(); | |
| if (!resp.ok) { | |
| console.error(`❌ Yêu cầu thất bại HTTP ${resp.status}`); | |
| console.log("📋 Chi tiết phản hồi:", data); | |
| return; | |
| } | |
| const hostedUrl = data?.url || data?.stripe_hosted_url || data?.checkout_url; | |
| if (!hostedUrl) { | |
| console.warn("⚠️ Không tìm thấy link, phản hồi gốc:", data); | |
| return; | |
| } | |
| console.log("─".repeat(60)); | |
| console.log("✅ Tạo link ChatGPT Team thành công!"); | |
| console.log("📋 Checkout Session ID :", data.checkout_session_id); | |
| console.log(`📌 Gói : ChatGPT Team (${country}/${currency})`); | |
| console.log("💺 Số ghế :", payload.team_plan_data.seat_quantity); | |
| console.log("🎟️ Mã giảm giá :", coupon || "(không có)"); | |
| console.log("🔗 Link Stripe:"); | |
| console.log(hostedUrl); | |
| console.log("─".repeat(60)); | |
| } catch (e) { | |
| console.error("❌ Lỗi mạng:", e.message); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment