Test Cards + Tokens + 3DS + DIDs — One Desktop Tool Does It All
Generate Luhn-valid test cards with tokens, 3DS codes, biometrics, and DIDs — all from a desktop app you can run offline.
If you’ve ever tested a payment form, you’ve probably hit Namso Gen. It works. But it’s online-only, limited in features, and you’re trusting someone else’s server with your workflow. This tool runs locally, generates more data types, and gives you a proper GUI.
🧠 What Even Is a Test Card Generator?
Payment forms need testing. Real cards = real charges = real problems. Test cards = fake numbers that look valid to the form but can’t actually charge anyone.
How it works:
- Every card number follows the Luhn algorithm (a.k.a. Mod 10) — a checksum that catches typos
- The first 6-8 digits are the BIN/IIN — they identify the card network (Visa, Mastercard, etc.) and issuing bank
- Generators create numbers that pass Luhn validation but aren’t linked to real accounts
What you use them for:
- Testing checkout flows without risking real money
- Validating that your payment form rejects bad numbers
- Stress-testing payment systems with bulk data
- Learning how card validation actually works
What they DON’T do:
- Generate real card numbers with real balances
- Work for actual purchases (they’ll fail at the bank level)
- Give you anything illegal — these are test numbers, not stolen data
⚡ The Tool — Test Card Generator 2030
This is a Python + PyQt6 desktop app. You run it locally. No servers, no tracking, no rate limits.
What It Does
| Feature | What You Get |
|---|---|
| BIN input | Enter any 6-8 digit IIN, generates matching card type |
| Auto-detection | Visa, Mastercard, Amex, Discover, UnionPay, Crypto — detected from BIN |
| Correct lengths | Visa: 16 or 19, Amex: 15, Mastercard: 16 — handled automatically |
| CVV generation | 3 digits (most cards) or 4 digits (Amex) — auto-selected |
| Expiry dates | 2026+ with 1-10 year validity |
| Cardholder names | Random English names for realistic test data |
| Batch generation | Up to 200 cards at once |
| Save to file | Export everything to .txt |
Advanced Features (Checkboxes)
| Checkbox | What It Adds |
|---|---|
| Add tokens | Simulates Apple Pay / Google Pay / stablecoin tokenization |
| Add 3DS codes | 6-digit codes for 3D Secure authentication testing |
| Add biometric tokens | SHA-256 hash mimicking fingerprint/face ID tokens |
| Add dynamic CVV | Changes hourly (shows seed + explanation) |
| Add DID | Decentralized identifiers (did:ethr:..., did:key:..., did:web:...) |
Screenshots
📋 The Full Script (Copy-Paste Ready)
Requirements: Python 3.9+ and PyQt6 (pip install pyqt6)
Save this as card_generator.py and double-click to run:
import sys
import random
import hashlib
from datetime import datetime
from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit,
QPushButton, QTextEdit, QSpinBox, QFileDialog,
QMessageBox, QCheckBox
)
def luhn_checksum(number_str):
digits = [int(d) for d in number_str]
for i in range(len(digits) - 2, -1, -2):
digits[i] = sum(divmod(digits[i] * 2, 10))
total = sum(digits)
return (10 - (total % 10)) % 10
def generate_card_number(iin_prefix):
iin_str = str(iin_prefix)
if len(iin_str) < 6 or len(iin_str) > 8:
raise ValueError("IIN must be 6-8 digits")
card_type, length_range, _ = determine_card_type(iin_prefix)
if isinstance(length_range, int):
length = length_range
else:
length = random.choice(length_range)
remaining_length = length - len(iin_str) - 1
random_digits = ''.join(str(random.randint(0, 9)) for _ in range(remaining_length))
partial_number = iin_str + random_digits
checksum = luhn_checksum(partial_number + '0')
return partial_number + str(checksum), length
def generate_expiry_date():
base_year = 2026
current_year = datetime.now().year
start_year = max(current_year, base_year)
expiry_year = start_year + random.randint(1, 10)
expiry_month = random.randint(1, 12)
return f"{expiry_month:02d}/{expiry_year % 100:02d}"
def generate_static_cvv(length=3):
return ''.join(str(random.randint(0, 9)) for _ in range(length))
def generate_dynamic_cvv(seed, length=3):
current_time = datetime.now().strftime("%Y%m%d%H")
hash_input = f"{seed}{current_time}"
hashed = hashlib.sha256(hash_input.encode()).hexdigest()
return hashed[:length].upper()
def generate_biometric_token():
bio_data = ''.join(random.choices('0123456789ABCDEF', k=32))
return hashlib.sha256(bio_data.encode()).hexdigest()
def generate_did():
method = random.choice(['ethr', 'key', 'web'])
identifier = ''.join(random.choices('0123456789abcdef', k=40))
return f"did:{method}:{identifier}"
def generate_cardholder_name():
first_names = ["John", "Jane", "Alex", "Emily", "Michael", "Sophia", "David", "Olivia", "James", "Emma"]
last_names = ["Doe", "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Martinez"]
return f"{random.choice(first_names)} {random.choice(last_names)}"
def generate_token(card_number):
token_length = random.randint(16, 19)
token_prefix = random.choice(['9', '8'])
remaining = token_length - 1 - 4
random_part = ''.join(str(random.randint(0, 9)) for _ in range(remaining))
return token_prefix + random_part + card_number[-4:]
def generate_3ds_code():
return ''.join(str(random.randint(0, 9)) for _ in range(6))
def determine_card_type(iin_prefix):
iin_str = str(iin_prefix)
if iin_str.startswith('4'):
return 'Visa', [16, 19], 3
elif iin_str.startswith('5') or iin_str.startswith('2'):
return 'Mastercard', 16, 3
elif iin_str.startswith('34') or iin_str.startswith('37'):
return 'American Express', 15, 4
elif iin_str.startswith('6'):
return 'Discover', [16, 19], 3
elif iin_str.startswith('62') or iin_str.startswith('81'):
return 'UnionPay', [16, 19], 3
elif iin_str.startswith('7'):
return 'Crypto/Stablecoin', [16, 19], 3
else:
return 'Unknown', 16, 3
def generate_cards(iin_prefix, count, include_token=False, include_3ds=False, include_biometric=False, include_dynamic_cvv=False, include_did=False):
card_type, length_range, default_cvv = determine_card_type(iin_prefix)
cards = []
for _ in range(count):
card_number, actual_length = generate_card_number(iin_prefix)
expiry = generate_expiry_date()
cvv_seed = random.randint(100000, 999999)
cvv_length = default_cvv
cvv = generate_dynamic_cvv(cvv_seed, cvv_length) if include_dynamic_cvv else generate_static_cvv(cvv_length)
name = generate_cardholder_name()
card = {
'type': card_type,
'number': card_number,
'length': actual_length,
'expiry': expiry,
'cvv': cvv,
'name': name
}
if include_dynamic_cvv:
card['cvv_note'] = f"Dynamic (seed: {cvv_seed}, changes hourly)"
if include_token:
card['token'] = generate_token(card_number)
if include_3ds:
card['3ds_code'] = generate_3ds_code()
if include_biometric:
card['biometric_token'] = generate_biometric_token()
if include_did:
card['did'] = generate_did()
cards.append(card)
return cards
class CardGeneratorGUI(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Test Card Generator 2030")
self.resize(720, 640)
self.layout = QVBoxLayout()
self.iin_label = QLabel("IIN (6–8 digits):")
self.iin_input = QLineEdit()
self.iin_input.setPlaceholderText("Example: 411111")
self.layout.addWidget(self.iin_label)
self.layout.addWidget(self.iin_input)
self.count_label = QLabel("Number of cards:")
self.count_input = QSpinBox()
self.count_input.setMinimum(1)
self.count_input.setMaximum(200)
self.count_input.setValue(10)
self.layout.addWidget(self.count_label)
self.layout.addWidget(self.count_input)
self.token_cb = QCheckBox("Add tokens (digital wallets / stablecoins)")
self.threeds_cb = QCheckBox("Add 3DS codes")
self.bio_cb = QCheckBox("Add biometric tokens")
self.dyn_cvv_cb = QCheckBox("Add dynamic CVV")
self.did_cb = QCheckBox("Add DID (decentralized identity)")
for cb in [self.token_cb, self.threeds_cb, self.bio_cb, self.dyn_cvv_cb, self.did_cb]:
self.layout.addWidget(cb)
self.generate_btn = QPushButton("Generate Cards")
self.generate_btn.clicked.connect(self.generate)
self.layout.addWidget(self.generate_btn)
self.output = QTextEdit()
self.output.setReadOnly(True)
self.layout.addWidget(self.output)
self.save_btn = QPushButton("Save to File")
self.save_btn.clicked.connect(self.save_result)
self.layout.addWidget(self.save_btn)
self.setLayout(self.layout)
def generate(self):
try:
iin_text = self.iin_input.text().strip()
if not iin_text:
QMessageBox.warning(self, "Error", "Please enter IIN")
return
if not iin_text.isdigit():
QMessageBox.warning(self, "Error", "IIN must contain only digits")
return
iin = int(iin_text)
if len(str(iin)) < 6 or len(str(iin)) > 8:
QMessageBox.warning(self, "Error", "IIN must be between 6 and 8 digits")
return
count = self.count_input.value()
cards = generate_cards(
iin, count,
include_token=self.token_cb.isChecked(),
include_3ds=self.threeds_cb.isChecked(),
include_biometric=self.bio_cb.isChecked(),
include_dynamic_cvv=self.dyn_cvv_cb.isChecked(),
include_did=self.did_cb.isChecked()
)
out = ""
for idx, card in enumerate(cards, 1):
out += f"Card {idx}\n"
out += f"Type: {card['type']}\n"
out += f"Number: {card['number']} (length: {card['length']})\n"
out += f"Expiry: {card['expiry']}\n"
out += f"CVV: {card['cvv']}"
if 'cvv_note' in card:
out += f" ({card['cvv_note']})"
out += "\n"
out += f"Name: {card['name']}\n"
if 'token' in card:
out += f"Token: {card['token']}\n"
if '3ds_code' in card:
out += f"3DS Code: {card['3ds_code']}\n"
if 'biometric_token' in card:
out += f"Biometric Token: {card['biometric_token']}\n"
if 'did' in card:
out += f"DID: {card['did']}\n"
out += "─" * 60 + "\n\n"
self.output.setPlainText(out)
except ValueError as ve:
QMessageBox.warning(self, "Input Error", str(ve))
except Exception as e:
QMessageBox.critical(self, "Error", f"{type(e).__name__}\n{str(e)}")
def save_result(self):
text = self.output.toPlainText().strip()
if not text:
QMessageBox.information(self, "Nothing to save", "Generate some cards first")
return
path, _ = QFileDialog.getSaveFileName(self, "Save As", "", "Text Files (*.txt);;All Files (*.*)")
if path:
try:
with open(path, "w", encoding="utf-8") as f:
f.write(text)
QMessageBox.information(self, "Success", "File saved successfully")
except Exception as e:
QMessageBox.critical(self, "Save Error", str(e))
def main():
app = QApplication(sys.argv)
window = CardGeneratorGUI()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
To run:
- Install Python 3.9+ from python.org
- Open terminal/cmd →
pip install pyqt6 - Save the script → double-click to run
🌐 Alternatives — Online Generators
If you don’t want to run Python locally, these web tools do similar things:
| Tool | What It Does | Link |
|---|---|---|
| Namso Gen | The OG. BIN-based generation, Luhn-valid, bulk export | namso.net |
| Multi-CC Gen | Multiple BINs at once, simpler interface | multi-cc-gen.web.app |
| BinCodes Generator | Generator + validator + BIN lookup combined | bincodes.com |
| TheNamsoGen | Bulk generation, CSV/JSON export, region-specific data | thenamsogen.com |
Why the desktop tool is still better:
- Works offline (no internet required after install)
- No rate limits or captchas
- Advanced features (tokens, 3DS, biometrics, DIDs) not available elsewhere
- Your data stays on your machine
🔧 GitHub Repos — Build Your Own
Want to modify or integrate this into your workflow? Here are open-source alternatives:
| Repo | Language | Stars | What It Does |
|---|---|---|---|
| python-cc-num-gen | Python | — | Simple class-based generator, Visa/MC/Amex/Discover |
| CreditCardGenerator | Swift | — | CLI tool with batch generation + file export |
| credit-card-generator (Node) | Node.js | — | npm package, Triple-DES CVV generation |
| CCGenerator | Python | — | Interactive CLI with performance modes |
| reNamso | HTML/JS | 31★ | Modern rewrite of Namso, no jQuery, clean code |
🔍 BIN Lookup APIs — Know Your Numbers
Before generating, you might want to look up what a BIN actually represents:
| API | Free Tier | What It Returns |
|---|---|---|
| binlist.net | 5/hour | Card brand, type, bank, country |
| HandyAPI BIN | Free with key | Updated weekly, 6-8 digit support |
| BinCodes API | Limited | XML/JSON, card validation |
| BINTable | 100/month | REST API, detailed issuer data |
| API-Ninjas BIN | Free tier | Brand, type, country, validity check |
❓ Common Questions
Q: Will these cards work for real purchases?
No. They pass Luhn validation (the checksum check) but fail at the bank level. There’s no account behind them.
Q: Is this legal?
Generating test numbers = yes. Using them for fraud = no. These are for testing payment forms, not stealing.
Q: What’s a BIN/IIN?
The first 6-8 digits of a card number. They identify the card network (Visa starts with 4, Mastercard with 5, etc.) and the issuing bank.
Q: Why do some Visa cards have 19 digits?
Visa supports both 16 and 19-digit numbers. The generator handles this automatically.
Q: What’s a dynamic CVV?
Some modern cards change their CVV periodically (often hourly) for security. The generator simulates this with a seed-based hash.
Q: What’s a DID?
Decentralized Identifier — a Web3/blockchain identity format. Useful for testing systems that use verifiable credentials.
New to BINs and how they actually work? This breaks it down from zero:
Working BIN Masterclass
One script. Offline. More features than Namso. No excuses.


!