From d4cc237ccdb3fc67808ef7fee92697a00b17b737 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 27 Nov 2025 10:34:59 +0800 Subject: [PATCH] dasdasd --- browser-automation-ts/analyze-advanced.js | 120 ++++++++++++++++++ browser-automation-ts/analyze-clustering.js | 90 +++++++++++++ browser-automation-ts/analyze-markov.js | 93 ++++++++++++++ browser-automation-ts/analyze-patterns.js | 61 +++++++++ .../configs/sites/windsurf.yaml | 34 ++--- .../docs/ANTI-FRAUD-STRATEGY.md | 0 .../docs/STRIPE-ANTI-FRAUD.md | 0 .../test-bin-distribution.mjs | 47 +++++++ browser-automation-ts/test-card-simple.js | 23 ++++ browser-automation-ts/test-card-validity.mjs | 90 +++++++++++++ browser-automation-ts/test-gen.mjs | 74 +++++++++++ 11 files changed, 615 insertions(+), 17 deletions(-) create mode 100644 browser-automation-ts/analyze-advanced.js create mode 100644 browser-automation-ts/analyze-clustering.js create mode 100644 browser-automation-ts/analyze-markov.js create mode 100644 browser-automation-ts/analyze-patterns.js create mode 100644 browser-automation-ts/docs/ANTI-FRAUD-STRATEGY.md create mode 100644 browser-automation-ts/docs/STRIPE-ANTI-FRAUD.md create mode 100644 browser-automation-ts/test-bin-distribution.mjs create mode 100644 browser-automation-ts/test-card-simple.js create mode 100644 browser-automation-ts/test-card-validity.mjs create mode 100644 browser-automation-ts/test-gen.mjs diff --git a/browser-automation-ts/analyze-advanced.js b/browser-automation-ts/analyze-advanced.js new file mode 100644 index 0000000..65e1716 --- /dev/null +++ b/browser-automation-ts/analyze-advanced.js @@ -0,0 +1,120 @@ +/** + * 高级分析:找出"成功模式"而不是"成功BIN" + * 目标:在保持BIN多样性的同时,提高成功率 + */ + +// 70张成功卡号 +const fullCards = [ + // 原始16张 + '6228367545055812', '6228367548774419', '6228367546781457', '6228367542738949', + '6228367542602400', '6228367548575105', '6228367546496080', '6228367540057649', + '6228367549574719', '6228367548435128', '6228367542797374', '6228367545956423', + '6228367547237848', '6228367540385107', '6228367544252006', '6228367547562054', + // 今天54张 + '6228367541130577', '6228367540744030', '6228367549888788', '6228367549131205', + '6228367541450744', '6228367547238010', '6228367547300364', '6228367540814288', + '6228367546042579', '6228367546361755', '6228367542443235', '6228367543564435', + '6228367548400627', '6228367544445204', '6228367542653734', '6228367549976732', + '6228367540810302', '6228367540707201', '6228367545237808', '6228367544322734', + '6228367541880148', '6228367549130520', '6228367547863197', '6228367541210049', + '6228367549031561', '6228367542464926', '6228367542487000', '6228367545452860', + '6228367548491592', '6228367545022853', '6228367545864858', '6228367544742832', + '6228367540023658', '6228367547416988', '6228367547093159', '6228367549198576', + '6228367548160064', '6228367546223252', '6228367544873785', '6228367541299976', + '6228367542940032', '6228367546998937', '6228367545800241', '6228367543770784', + '6228367545976843', '6228367547542551', '6228367543917914', '6228367545657930', + '6228367586381796', '6228367544322809', '6228367549131254', '6228367543917146', + '6228367546998903', '6228367545864460' +]; + +console.log('=== 高级分析:成功模式识别 ===\n'); + +// 分析1:BIN的第10-12位(extension)的规律 +console.log('1. BIN Extension(第10-12位)分析:\n'); +const extensions = {}; +fullCards.forEach(card => { + const ext = card.slice(9, 12); // 第10-12位 + extensions[ext] = (extensions[ext] || 0) + 1; +}); + +const sortedExt = Object.entries(extensions) + .sort((a, b) => b[1] - a[1]) + .slice(0, 10); + +sortedExt.forEach(([ext, count]) => { + const percent = (count / 70 * 100).toFixed(1); + console.log(` ${ext}: ${count}次 (${percent}%)`); +}); + +// 分析2:完整BIN(13位)的第13位规律 +console.log('\n2. BIN最后一位(第13位)分析:\n'); +const lastDigits = {}; +fullCards.forEach(card => { + const last = card[12]; // 第13位 + lastDigits[last] = (lastDigits[last] || 0) + 1; +}); + +for (let i = 0; i <= 9; i++) { + const count = lastDigits[i] || 0; + const percent = (count / 70 * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%)`); +} + +// 分析3:后3位数字的"和"的规律 +console.log('\n3. 后3位数字之和的分布:\n'); +const sums = {}; +fullCards.forEach(card => { + const last3 = card.slice(-4, -1); + const sum = parseInt(last3[0]) + parseInt(last3[1]) + parseInt(last3[2]); + sums[sum] = (sums[sum] || 0) + 1; +}); + +const sortedSums = Object.entries(sums) + .sort((a, b) => b[1] - a[1]) + .slice(0, 10); + +sortedSums.forEach(([sum, count]) => { + const percent = (count / 70 * 100).toFixed(1); + console.log(` 和=${sum}: ${count}次 (${percent}%)`); +}); + +// 分析4:后3位数字的"奇偶性"规律 +console.log('\n4. 后3位奇偶性分析:\n'); +const patterns = { + '偶偶偶': 0, '偶偶奇': 0, '偶奇偶': 0, '偶奇奇': 0, + '奇偶偶': 0, '奇偶奇': 0, '奇奇偶': 0, '奇奇奇': 0 +}; + +fullCards.forEach(card => { + const last3 = card.slice(-4, -1); + const pattern = last3.split('').map(d => parseInt(d) % 2 === 0 ? '偶' : '奇').join(''); + patterns[pattern]++; +}); + +Object.entries(patterns) + .sort((a, b) => b[1] - a[1]) + .forEach(([pattern, count]) => { + const percent = (count / 70 * 100).toFixed(1); + console.log(` ${pattern}: ${count}次 (${percent}%)`); + }); + +// 分析5:Luhn校验位的分布 +console.log('\n5. Luhn校验位(最后一位)分布:\n'); +const checkDigits = {}; +fullCards.forEach(card => { + const check = card[15]; // 最后一位 + checkDigits[check] = (checkDigits[check] || 0) + 1; +}); + +for (let i = 0; i <= 9; i++) { + const count = checkDigits[i] || 0; + const percent = (count / 70 * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%)`); +} + +console.log('\n=== 关键发现 ===\n'); +console.log('基于以上分析,可以优化生成策略:'); +console.log('1. 优先选择高频的BIN Extension'); +console.log('2. 后3位数字之和倾向于某些值'); +console.log('3. 奇偶性组合有明显偏好'); +console.log('4. 结合马尔可夫链,在保持BIN多样性的同时提高成功率'); diff --git a/browser-automation-ts/analyze-clustering.js b/browser-automation-ts/analyze-clustering.js new file mode 100644 index 0000000..38c72d9 --- /dev/null +++ b/browser-automation-ts/analyze-clustering.js @@ -0,0 +1,90 @@ +/** + * 聚类分析 - 找出成功案例的"家族"模式 + */ + +// 70张成功卡号 +const fullCards = [ + // 原始16张 + '6228367545055812', '6228367548774419', '6228367546781457', '6228367542738949', + '6228367542602400', '6228367548575105', '6228367546496080', '6228367540057649', + '6228367549574719', '6228367548435128', '6228367542797374', '6228367545956423', + '6228367547237848', '6228367540385107', '6228367544252006', '6228367547562054', + // 今天54张 + '6228367541130577', '6228367540744030', '6228367549888788', '6228367549131205', + '6228367541450744', '6228367547238010', '6228367547300364', '6228367540814288', + '6228367546042579', '6228367546361755', '6228367542443235', '6228367543564435', + '6228367548400627', '6228367544445204', '6228367542653734', '6228367549976732', + '6228367540810302', '6228367540707201', '6228367545237808', '6228367544322734', + '6228367541880148', '6228367549130520', '6228367547863197', '6228367541210049', + '6228367549031561', '6228367542464926', '6228367542487000', '6228367545452860', + '6228367548491592', '6228367545022853', '6228367545864858', '6228367544742832', + '6228367540023658', '6228367547416988', '6228367547093159', '6228367549198576', + '6228367548160064', '6228367546223252', '6228367544873785', '6228367541299976', + '6228367542940032', '6228367546998937', '6228367545800241', '6228367543770784', + '6228367545976843', '6228367547542551', '6228367543917914', '6228367545657930', + '6228367586381796', '6228367544322809', '6228367549131254', '6228367543917146', + '6228367546998903', '6228367545864460' +]; + +console.log('=== 聚类分析:找出成功案例的家族 ===\n'); + +// 提取BIN(13位前缀) +const binGroups = {}; +fullCards.forEach(card => { + const bin = card.slice(0, 13); + if (!binGroups[bin]) { + binGroups[bin] = []; + } + binGroups[bin].push(card); +}); + +// 按数量排序 +const sortedBins = Object.entries(binGroups) + .sort((a, b) => b[1].length - a[1].length); + +console.log('=== BIN家族分布(前10个) ===\n'); +sortedBins.slice(0, 10).forEach(([bin, cards], i) => { + console.log(`${i + 1}. BIN: ${bin} (${cards.length}张)`); + cards.forEach(card => { + const last3 = card.slice(-4, -1); + console.log(` ${card} -> 后3位: ${last3}`); + }); + console.log(); +}); + +// 分析每个BIN家族的后3位模式 +console.log('=== 每个BIN家族的后3位规律 ===\n'); +sortedBins.slice(0, 10).forEach(([bin, cards]) => { + if (cards.length < 2) return; + + console.log(`BIN: ${bin} (${cards.length}张)`); + + const last3s = cards.map(c => c.slice(-4, -1)); + + // 统计每位的分布 + const pos1 = {}, pos2 = {}; + last3s.forEach(l3 => { + pos1[l3[0]] = (pos1[l3[0]] || 0) + 1; + pos2[l3[1]] = (pos2[l3[1]] || 0) + 1; + }); + + console.log(' 百位分布:', Object.entries(pos1).sort((a, b) => b[1] - a[1]).map(([d, c]) => `${d}:${c}`).join(', ')); + console.log(' 十位分布:', Object.entries(pos2).sort((a, b) => b[1] - a[1]).map(([d, c]) => `${d}:${c}`).join(', ')); + console.log(); +}); + +// 找出"热门BIN"(成功率高的BIN) +console.log('=== 热门BIN推荐(成功案例>=2张)===\n'); +const hotBins = sortedBins.filter(([_, cards]) => cards.length >= 2); +console.log(`共找到 ${hotBins.length} 个热门BIN\n`); + +hotBins.forEach(([bin, cards]) => { + const last3s = cards.map(c => c.slice(-4, -1)); + console.log(`${bin}: ${last3s.join(', ')}`); +}); + +// 生成配置建议 +console.log('\n=== 优化建议 ===\n'); +console.log(`1. 优先使用热门BIN(${hotBins.length}个),它们的成功率更高`); +console.log(`2. 对于热门BIN,可以直接从已知成功的后3位变异生成`); +console.log(`3. 冷门BIN(只有1张成功)可以降低权重或跳过`); diff --git a/browser-automation-ts/analyze-markov.js b/browser-automation-ts/analyze-markov.js new file mode 100644 index 0000000..ec053e2 --- /dev/null +++ b/browser-automation-ts/analyze-markov.js @@ -0,0 +1,93 @@ +/** + * 马尔可夫链分析 - 分析位置之间的转移概率 + */ + +// 70张成功卡号 +const fullCards = [ + // 原始16张 + '6228367545055812', '6228367548774419', '6228367546781457', '6228367542738949', + '6228367542602400', '6228367548575105', '6228367546496080', '6228367540057649', + '6228367549574719', '6228367548435128', '6228367542797374', '6228367545956423', + '6228367547237848', '6228367540385107', '6228367544252006', '6228367547562054', + // 今天54张 + '6228367541130577', '6228367540744030', '6228367549888788', '6228367549131205', + '6228367541450744', '6228367547238010', '6228367547300364', '6228367540814288', + '6228367546042579', '6228367546361755', '6228367542443235', '6228367543564435', + '6228367548400627', '6228367544445204', '6228367542653734', '6228367549976732', + '6228367540810302', '6228367540707201', '6228367545237808', '6228367544322734', + '6228367541880148', '6228367549130520', '6228367547863197', '6228367541210049', + '6228367549031561', '6228367542464926', '6228367542487000', '6228367545452860', + '6228367548491592', '6228367545022853', '6228367545864858', '6228367544742832', + '6228367540023658', '6228367547416988', '6228367547093159', '6228367549198576', + '6228367548160064', '6228367546223252', '6228367544873785', '6228367541299976', + '6228367542940032', '6228367546998937', '6228367545800241', '6228367543770784', + '6228367545976843', '6228367547542551', '6228367543917914', '6228367545657930', + '6228367586381796', '6228367544322809', '6228367549131254', '6228367543917146', + '6228367546998903', '6228367545864460' +]; + +// 提取后3位(不含校验位) +const patterns = fullCards.map(card => card.slice(-4, -1)); + +console.log('=== 马尔可夫链分析:位置转移概率 ===\n'); + +// 统计转移概率:百位 -> 十位 +const transitions = {}; + +patterns.forEach(p => { + const d1 = p[0]; // 百位 + const d2 = p[1]; // 十位 + + if (!transitions[d1]) { + transitions[d1] = {}; + } + transitions[d1][d2] = (transitions[d1][d2] || 0) + 1; +}); + +// 打印转移概率矩阵 +console.log('百位 -> 十位 转移概率:\n'); +for (let i = 0; i <= 9; i++) { + if (!transitions[i]) continue; + + const total = Object.values(transitions[i]).reduce((sum, count) => sum + count, 0); + console.log(`百位=${i} (${total}次):`); + + const sorted = Object.entries(transitions[i]) + .sort((a, b) => b[1] - a[1]) + .slice(0, 5); // 只显示前5个最高概率 + + sorted.forEach(([digit, count]) => { + const prob = (count / total * 100).toFixed(1); + console.log(` -> ${digit}: ${count}次 (${prob}%)`); + }); + console.log(); +} + +// 生成转移概率配置 +console.log('\n=== 生成马尔可夫转移配置 ===\n'); +console.log('const markovTransitions = {'); +for (let i = 0; i <= 9; i++) { + if (!transitions[i]) { + console.log(` '${i}': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // 无数据,均匀分布`); + continue; + } + + const weights = Array.from({length: 10}, (_, j) => transitions[i][j] || 0); + console.log(` '${i}': [${weights.join(', ')}],`); +} +console.log('};'); + +// 分析是否有明显的模式 +console.log('\n=== 关键发现 ===\n'); +for (let i = 0; i <= 9; i++) { + if (!transitions[i]) continue; + + const total = Object.values(transitions[i]).reduce((sum, count) => sum + count, 0); + const max = Math.max(...Object.values(transitions[i])); + const maxDigit = Object.entries(transitions[i]).find(([_, count]) => count === max)[0]; + const maxProb = (max / total * 100).toFixed(1); + + if (maxProb > 25) { + console.log(`⚠️ 百位=${i} 时,十位=${maxDigit} 的概率高达 ${maxProb}%`); + } +} diff --git a/browser-automation-ts/analyze-patterns.js b/browser-automation-ts/analyze-patterns.js new file mode 100644 index 0000000..20d8258 --- /dev/null +++ b/browser-automation-ts/analyze-patterns.js @@ -0,0 +1,61 @@ +// 分析成功卡号的规律(完整16位卡号) +const fullCards = [ + // 原始16张 + '6228367545055812', '6228367548774419', '6228367546781457', '6228367542738949', + '6228367542602400', '6228367548575105', '6228367546496080', '6228367540057649', + '6228367549574719', '6228367548435128', '6228367542797374', '6228367545956423', + '6228367547237848', '6228367540385107', '6228367544252006', '6228367547562054', + // 今天54张 + '6228367541130577', '6228367540744030', '6228367549888788', '6228367549131205', + '6228367541450744', '6228367547238010', '6228367547300364', '6228367540814288', + '6228367546042579', '6228367546361755', '6228367542443235', '6228367543564435', + '6228367548400627', '6228367544445204', '6228367542653734', '6228367549976732', + '6228367540810302', '6228367540707201', '6228367545237808', '6228367544322734', + '6228367541880148', '6228367549130520', '6228367547863197', '6228367541210049', + '6228367549031561', '6228367542464926', '6228367542487000', '6228367545452860', + '6228367548491592', '6228367545022853', '6228367545864858', '6228367544742832', + '6228367540023658', '6228367547416988', '6228367547093159', '6228367549198576', + '6228367548160064', '6228367546223252', '6228367544873785', '6228367541299976', + '6228367542940032', '6228367546998937', '6228367545800241', '6228367543770784', + '6228367545976843', '6228367547542551', '6228367543917914', '6228367545657930', + '6228367586381796', '6228367544322809', '6228367549131254', '6228367543917146', + '6228367546998903', '6228367545864460' +]; + +// 提取后3位(不含校验位) +const patterns = fullCards.map(card => card.slice(-4, -1)); + +// 统计每个位置的数字频率 +const pos1 = {}; // 百位 +const pos2 = {}; // 十位 + +patterns.forEach(p => { + const d1 = p[0]; + const d2 = p[1]; + + pos1[d1] = (pos1[d1] || 0) + 1; + pos2[d2] = (pos2[d2] || 0) + 1; +}); + +console.log('=== 70张成功卡号的数字分布规律 ===\n'); + +console.log('位置1(千位)频率:'); +for (let i = 0; i <= 9; i++) { + const count = pos1[i] || 0; + const percent = (count / patterns.length * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%)`); +} + +console.log('\n位置2(十位)频率:'); +for (let i = 0; i <= 9; i++) { + const count = pos2[i] || 0; + const percent = (count / patterns.length * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%)`); +} + +// 生成权重数组(用于config.ts)- 只需要2位 +console.log('\n=== 生成权重配置(后2位,不含校验位)===\n'); +console.log('const positionWeights = ['); +console.log(' [' + Array.from({length: 10}, (_, i) => pos1[i] || 0).join(', ') + '], // 百位'); +console.log(' [' + Array.from({length: 10}, (_, i) => pos2[i] || 0).join(', ') + '] // 十位'); +console.log('];'); diff --git a/browser-automation-ts/configs/sites/windsurf.yaml b/browser-automation-ts/configs/sites/windsurf.yaml index 5e0bd12..8d89f14 100644 --- a/browser-automation-ts/configs/sites/windsurf.yaml +++ b/browser-automation-ts/configs/sites/windsurf.yaml @@ -1,27 +1,27 @@ # Windsurf 注册自动化配置 site: name: Windsurf - url: https://windsurf.com/refer?referral_code=55424ec434 + url: https://windsurf.com/account/register # 工作流定义 workflow: # ==================== 步骤 0: 处理邀请链接 ==================== - - action: navigate - name: "打开邀请链接" - url: "https://windsurf.com/refer?referral_code=55424ec434" - options: - waitUntil: 'networkidle2' - timeout: 30000 - - - action: click - name: "点击接受邀请" - selector: - - text: 'Sign up to accept referral' - selector: 'button' - - css: 'button.bg-sk-aqua' - - css: 'button:has-text("Sign up to accept referral")' - timeout: 30000 - waitForNavigation: true +# - action: navigate +# name: "打开邀请链接" +# url: "https://windsurf.com/refer?referral_code=55424ec434" +# options: +# waitUntil: 'networkidle2' +# timeout: 30000 +# +# - action: click +# name: "点击接受邀请" +# selector: +# - text: 'Sign up to accept referral' +# selector: 'button' +# - css: 'button.bg-sk-aqua' +# - css: 'button:has-text("Sign up to accept referral")' +# timeout: 30000 +# waitForNavigation: true # 验证跳转到注册页面(带referral_code) - action: verify diff --git a/browser-automation-ts/docs/ANTI-FRAUD-STRATEGY.md b/browser-automation-ts/docs/ANTI-FRAUD-STRATEGY.md new file mode 100644 index 0000000..e69de29 diff --git a/browser-automation-ts/docs/STRIPE-ANTI-FRAUD.md b/browser-automation-ts/docs/STRIPE-ANTI-FRAUD.md new file mode 100644 index 0000000..e69de29 diff --git a/browser-automation-ts/test-bin-distribution.mjs b/browser-automation-ts/test-bin-distribution.mjs new file mode 100644 index 0000000..2d13c97 --- /dev/null +++ b/browser-automation-ts/test-bin-distribution.mjs @@ -0,0 +1,47 @@ +/** + * 测试BIN分布 + */ +import pkg from './src/tools/card/CardGeneratorTool.js'; +const { CardGeneratorTool } = pkg; + +async function test() { + console.log('生成1000张卡,分析BIN分布...\n'); + + const cardGen = new CardGeneratorTool(); + await cardGen.initialize({}); + + const cards = await cardGen.generateBatch(1000, 'unionpay'); + + // 统计BIN分布 + const binCounts = {}; + cards.forEach(card => { + const bin = card.number.slice(0, 13); + binCounts[bin] = (binCounts[bin] || 0) + 1; + }); + + // 排序 + const sorted = Object.entries(binCounts) + .sort((a, b) => b[1] - a[1]); + + console.log('=== BIN分布(前10个) ===\n'); + sorted.slice(0, 10).forEach(([bin, count], i) => { + const percent = (count / 1000 * 100).toFixed(1); + const isHot = ['6228367549131', '6228367544322', '6228367545864', '6228367546998', '6228367543917'].includes(bin); + const mark = isHot ? '🔥' : ' '; + console.log(`${i + 1}. ${mark} ${bin}: ${count}次 (${percent}%)`); + }); + + // 统计热门BIN的总占比 + const hotBins = ['6228367549131', '6228367544322', '6228367545864', '6228367546998', '6228367543917']; + const hotCount = hotBins.reduce((sum, bin) => sum + (binCounts[bin] || 0), 0); + const hotPercent = (hotCount / 1000 * 100).toFixed(1); + + console.log(`\n=== 热门BIN统计 ===`); + console.log(`热门BIN总占比: ${hotCount}/1000 (${hotPercent}%)`); + console.log(`期望占比: ~${(5 * 5 / (5 * 5 + 60 * 1) * 100).toFixed(1)}% (权重5 vs 权重1)`); +} + +test().catch(err => { + console.error('错误:', err.message); + console.error(err.stack); +}); diff --git a/browser-automation-ts/test-card-simple.js b/browser-automation-ts/test-card-simple.js new file mode 100644 index 0000000..8152ee7 --- /dev/null +++ b/browser-automation-ts/test-card-simple.js @@ -0,0 +1,23 @@ +/** + * 简单测试卡号生成 + */ +const {CardGeneratorTool} = require('./src/tools/card/CardGeneratorTool'); + +async function test() { + console.log('开始测试...'); + + const cardGen = new CardGeneratorTool(); + await cardGen.initialize({}); + + console.log('生成10张测试卡...\n'); + + for (let i = 0; i < 10; i++) { + const card = await cardGen.generate('unionpay'); + console.log(`${i + 1}. ${card.number} (${card.month}/${card.year}) - ${card.issuer}-${card.cvv}`); + } +} + +test().catch(err => { + console.error('错误:', err.message); + console.error(err.stack); +}); diff --git a/browser-automation-ts/test-card-validity.mjs b/browser-automation-ts/test-card-validity.mjs new file mode 100644 index 0000000..613517a --- /dev/null +++ b/browser-automation-ts/test-card-validity.mjs @@ -0,0 +1,90 @@ +/** + * 测试生成的卡号是否有效 + */ +import pkg from './src/tools/card/CardGeneratorTool.js'; +const { CardGeneratorTool } = pkg; + +// Luhn算法验证 +function validateLuhn(cardNumber) { + let sum = 0; + let isEven = false; + + for (let i = cardNumber.length - 1; i >= 0; i--) { + let digit = parseInt(cardNumber[i]); + + if (isEven) { + digit *= 2; + if (digit > 9) digit -= 9; + } + + sum += digit; + isEven = !isEven; + } + + return sum % 10 === 0; +} + +async function test() { + console.log('生成20张卡,验证有效性...\n'); + + const cardGen = new CardGeneratorTool(); + await cardGen.initialize({}); + + const cards = await cardGen.generateBatch(20, 'unionpay'); + + let luhnPass = 0; + let luhnFail = 0; + + console.log('=== 卡号验证 ===\n'); + cards.forEach((card, i) => { + const isValid = validateLuhn(card.number); + const status = isValid ? '✅' : '❌'; + + if (isValid) luhnPass++; + else luhnFail++; + + console.log(`${i + 1}. ${status} ${card.number}`); + console.log(` 有效期: ${card.month}/${card.year}`); + console.log(` CVV: ${card.cvv}`); + console.log(` 发卡行: ${card.issuer} (${card.country})`); + console.log(); + }); + + console.log('=== 验证结果 ==='); + console.log(`Luhn校验通过: ${luhnPass}/20`); + console.log(`Luhn校验失败: ${luhnFail}/20`); + + // 验证有效期范围 + console.log('\n=== 有效期检查 ==='); + const months = cards.map(c => parseInt(c.month)); + const years = cards.map(c => parseInt(c.year)); + + const validMonths = months.filter(m => m >= 1 && m <= 12).length; + const validYears = years.filter(y => y >= 26 && y <= 30).length; + + console.log(`月份有效: ${validMonths}/20 (范围: 01-12)`); + console.log(`年份有效: ${validYears}/20 (范围: 26-30)`); + + // 验证CVV长度 + console.log('\n=== CVV检查 ==='); + const validCVV = cards.filter(c => c.cvv.length === 3).length; + console.log(`CVV长度正确: ${validCVV}/20 (应为3位)`); + + // 显示有效期和CVV的分布 + console.log('\n=== 有效期分布 ==='); + const expiryDist = {}; + cards.forEach(c => { + const key = `${c.month}/${c.year}`; + expiryDist[key] = (expiryDist[key] || 0) + 1; + }); + Object.entries(expiryDist) + .sort((a, b) => b[1] - a[1]) + .forEach(([expiry, count]) => { + console.log(` ${expiry}: ${count}次`); + }); +} + +test().catch(err => { + console.error('错误:', err.message); + console.error(err.stack); +}); diff --git a/browser-automation-ts/test-gen.mjs b/browser-automation-ts/test-gen.mjs new file mode 100644 index 0000000..1598bbc --- /dev/null +++ b/browser-automation-ts/test-gen.mjs @@ -0,0 +1,74 @@ +/** + * 测试卡号生成规律 + */ +import pkg from './src/tools/card/CardGeneratorTool.js'; +const { CardGeneratorTool } = pkg; + +async function test() { + console.log('开始生成1000张卡...\n'); + + const cardGen = new CardGeneratorTool(); + await cardGen.initialize({}); + + const cards = await cardGen.generateBatch(1000, 'unionpay'); + + // 统计后4位的分布 + const pos1 = {}; + const pos2 = {}; + const pos3 = {}; + + cards.forEach(card => { + const last4 = card.number.slice(-4); + const digits = last4.slice(0, 3); + + pos1[digits[0]] = (pos1[digits[0]] || 0) + 1; + pos2[digits[1]] = (pos2[digits[1]] || 0) + 1; + pos3[digits[2]] = (pos3[digits[2]] || 0) + 1; + }); + + console.log('=== 生成的1000张卡的数字分布 ===\n'); + + console.log('位置1(千位)频率:'); + for (let i = 0; i <= 9; i++) { + const count = pos1[i] || 0; + const percent = (count / 1000 * 100).toFixed(1); + const expected = [14, 9, 7, 8, 10, 2, 4, 7, 7, 2][i]; + const expectedPercent = (expected / 70 * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%) [期望: ${expectedPercent}%]`); + } + + console.log('\n位置2(百位)频率:'); + for (let i = 0; i <= 9; i++) { + const count = pos2[i] || 0; + const percent = (count / 1000 * 100).toFixed(1); + const expected = [10, 7, 10, 2, 5, 8, 2, 11, 8, 7][i]; + const expectedPercent = (expected / 70 * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%) [期望: ${expectedPercent}%]`); + } + + console.log('\n位置3(十位)频率:'); + for (let i = 0; i <= 9; i++) { + const count = pos3[i] || 0; + const percent = (count / 1000 * 100).toFixed(1); + const expected = [10, 2, 4, 11, 8, 9, 6, 6, 7, 7][i]; + const expectedPercent = (expected / 70 * 100).toFixed(1); + console.log(` ${i}: ${count}次 (${percent}%) [期望: ${expectedPercent}%]`); + } + + console.log('\n=== 前20张生成的卡号 ===\n'); + cards.slice(0, 20).forEach((card, i) => { + const last4 = card.number.slice(-4); + console.log(`${i + 1}. ${card.number} (后4位: ${last4})`); + }); + + const uniqueNumbers = new Set(cards.map(c => c.number)); + console.log(`\n=== 去重检查 ===`); + console.log(`生成: 1000张`); + console.log(`唯一: ${uniqueNumbers.size}张`); + console.log(`重复: ${1000 - uniqueNumbers.size}张`); +} + +test().catch(err => { + console.error('错误:', err.message); + console.error(err.stack); +});