diff --git a/.github/workflows/README.md b/.github/workflows/README.md
new file mode 100644
index 0000000..b85ab44
--- /dev/null
+++ b/.github/workflows/README.md
@@ -0,0 +1,196 @@
+# GitHub Actions Workflows
+
+This directory contains GitHub Actions workflows for automating builds, tests, and releases.
+
+## Workflows
+
+### 1. CI (Continuous Integration)
+
+**File:** `ci.yml`
+
+**Triggers:**
+- Push to `main` or `master` branches
+- Pull requests to `main` or `master` branches
+
+**What it does:**
+- Tests on multiple Node.js versions (16.x, 18.x, 20.x)
+- Builds data files
+- Validates JSON files
+- Runs JavaScript tests
+- Tests Python and Ruby examples
+
+### 2. Data Release
+
+**File:** `data-release.yml`
+
+**Triggers:**
+- Push to `main` or `master` branches
+- Only when changes are made to `data/sources/**` or `scripts/build.js`
+
+**What it does:**
+- Builds and validates data files
+- Automatically increments patch version in root package.json
+- Creates GitHub release with tag `data-vX.Y.Z`
+- Attaches all data files (brands.json, compiled/brands.json, sources/*.json)
+
+**Release Assets:**
+- `data/brands.json` - Legacy format
+- `data/compiled/brands.json` - Enhanced format
+- `data/sources/*.json` - Source files
+
+### 3. Publish NPM Package
+
+**File:** `publish-npm.yml`
+
+**Triggers:**
+- Manual dispatch from GitHub Actions UI
+
+**What it does:**
+- Updates the JavaScript library version
+- Runs tests
+- Publishes to NPM registry
+- Creates a Git tag (e.g., `v1.2.0`)
+- Creates a GitHub release for the library
+
+**How to use:**
+1. Go to Actions tab in GitHub
+2. Select "Publish NPM Package" workflow
+3. Click "Run workflow"
+4. Enter version number (e.g., `1.2.0`)
+5. Select NPM tag (`latest`, `beta`, `next`)
+6. Click "Run workflow"
+
+## Architecture
+
+This project separates data releases from library releases:
+
+### Data Releases (GitHub Releases)
+- Tagged as `data-vX.Y.Z`
+- Contains BIN pattern data
+- Updated when `data/sources/**` changes
+- Downloaded automatically by the JavaScript library
+
+### Library Releases (NPM)
+- Tagged as `vX.Y.Z`
+- Published to NPM
+- Contains validation logic
+- Downloads data from GitHub releases on install
+
+## Setting Up
+
+### For Data Releases
+No setup required - automatic when data sources are updated.
+
+### For NPM Publishing
+Add `NPM_TOKEN` secret to repository:
+1. Generate token at https://www.npmjs.com/settings/YOUR_USERNAME/tokens
+2. Go to repository Settings → Secrets → Actions
+3. Add secret named `NPM_TOKEN`
+
+## Release Process
+
+### Updating Data
+```bash
+# 1. Edit source files
+vim data/sources/visa.json
+
+# 2. Build and commit
+npm run build
+git add data/
+git commit -m "Update Visa BIN patterns"
+git push
+
+# 3. GitHub Actions automatically:
+# - Builds data
+# - Creates release (data-v2.0.2)
+# - Attaches data files
+```
+
+### Publishing Library
+```bash
+# 1. Go to Actions → Publish NPM Package
+# 2. Click "Run workflow"
+# 3. Enter version: 1.3.0
+# 4. Select tag: latest
+# 5. Click "Run workflow"
+
+# GitHub Actions automatically:
+# - Updates package.json
+# - Runs tests
+# - Publishes to NPM
+# - Creates release (v1.3.0)
+```
+
+## Setting Up
+
+These workflows run automatically once the repository is on GitHub. No additional setup is required.
+
+## Artifacts
+
+Build artifacts (compiled data files) are stored for 30 days and can be downloaded from:
+- Actions tab → Select a workflow run → Artifacts section
+
+## Release Assets
+
+Each release includes:
+- `data/brands.json` - Legacy format
+- `data/compiled/brands.json` - Enhanced format
+- Source files from `data/sources/` (manual releases only)
+
+## Environment Requirements
+
+- Node.js 16.x, 18.x, or 20.x
+- Python 3.x (for examples)
+- Ruby 2.5+ (for examples)
+
+## Customization
+
+To modify the workflows:
+
+1. **Change Node.js versions:**
+ Edit the `matrix.node-version` in `ci.yml`
+
+2. **Change version bumping strategy:**
+ Edit the version calculation in `build-and-release.yml`
+
+3. **Add more tests:**
+ Add steps to any workflow file
+
+## Troubleshooting
+
+### Failed Builds
+
+If a build fails:
+1. Check the workflow logs in the Actions tab
+2. Run `npm run build` locally to reproduce
+3. Fix issues in source files
+4. Push changes
+
+### Failed Tests
+
+If tests fail:
+1. Check test logs in the workflow output
+2. Run tests locally: `cd libs/javascript && npm test`
+3. Fix test failures
+4. Push changes
+
+### Release Not Created
+
+If an automatic release wasn't created:
+1. Check if there were actually changes in `data/sources/`
+2. Verify the workflow completed successfully
+3. Check workflow logs for errors
+4. Use manual release as fallback
+
+## Security
+
+- Workflows use `GITHUB_TOKEN` which is automatically provided
+- No additional secrets required
+- Token has write permissions for releases and tags
+
+## Best Practices
+
+1. **Test locally first:** Always run `npm run build` and `npm test` before pushing
+2. **Use descriptive commits:** Help the auto-release generate good release notes
+3. **Manual releases for major versions:** Use manual workflow for version 3.0.0, etc.
+4. **Review release notes:** Edit release notes after auto-creation if needed
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..9d31483
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,99 @@
+name: CI
+
+on:
+ push:
+ branches: [ main, master ]
+ pull_request:
+ branches: [ main, master ]
+
+jobs:
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [16.x, 18.x, 20.x]
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Install root dependencies
+ run: npm install --ignore-scripts
+
+ - name: Build data files
+ run: npm run build
+
+ - name: Validate build output
+ run: |
+ if [ ! -f data/compiled/brands.json ]; then
+ echo "Error: compiled/brands.json not found"
+ exit 1
+ fi
+ echo "✓ Build output validated"
+
+ - name: Install JavaScript library dependencies
+ run: |
+ cd libs/javascript
+ npm install
+
+ - name: Run JavaScript tests
+ run: |
+ cd libs/javascript
+ npm test
+
+ - name: Test Python example
+ run: |
+ python3 --version
+ python3 examples/python/credit_card_validator.py
+
+ - name: Test Ruby example
+ run: |
+ ruby --version
+ ruby examples/ruby/credit_card_validator.rb
+
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+
+ - name: Check JSON validity
+ run: |
+ for file in data/sources/*.json data/brands.json data/compiled/brands.json; do
+ if [ -f "$file" ]; then
+ echo "Checking $file..."
+ node -e "JSON.parse(require('fs').readFileSync('$file', 'utf8'))"
+ fi
+ done
+ echo "✓ All JSON files are valid"
+
+ - name: Check for required files
+ run: |
+ required_files=(
+ "data/brands.json"
+ "data/SCHEMA.md"
+ "data/README.md"
+ "CONTRIBUTING.md"
+ "scripts/build.js"
+ )
+ for file in "${required_files[@]}"; do
+ if [ ! -f "$file" ]; then
+ echo "Error: Required file $file not found"
+ exit 1
+ fi
+ done
+ echo "✓ All required files present"
diff --git a/.github/workflows/data-release.yml b/.github/workflows/data-release.yml
new file mode 100644
index 0000000..9dc7ca3
--- /dev/null
+++ b/.github/workflows/data-release.yml
@@ -0,0 +1,106 @@
+name: Data Release
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ paths:
+ - 'data/sources/**'
+ - 'scripts/build.js'
+
+jobs:
+ build-and-release-data:
+ name: Build and Release Data
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+
+ - name: Install dependencies
+ run: npm install --ignore-scripts
+
+ - name: Build data files
+ run: npm run build
+
+ - name: Get current data version
+ id: get_version
+ run: |
+ # Get the current version from root package.json
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
+ echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
+
+ # Increment patch version for data
+ IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
+ MAJOR=${VERSION_PARTS[0]}
+ MINOR=${VERSION_PARTS[1]}
+ PATCH=${VERSION_PARTS[2]}
+ NEW_PATCH=$((PATCH + 1))
+ NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH"
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
+ echo "New data version: $NEW_VERSION"
+
+ - name: Update root package.json version
+ run: |
+ node -e "
+ const fs = require('fs');
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
+ pkg.version = '${{ steps.get_version.outputs.new_version }}';
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
+ "
+
+ - name: Commit version bump
+ run: |
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
+ git config --local user.name "github-actions[bot]"
+ git add package.json
+ git commit -m "chore: bump data version to ${{ steps.get_version.outputs.new_version }}" || echo "No changes to commit"
+ git push || echo "No changes to push"
+
+ - name: Create Data Release
+ uses: softprops/action-gh-release@v1
+ with:
+ tag_name: data-v${{ steps.get_version.outputs.new_version }}
+ name: Data Release v${{ steps.get_version.outputs.new_version }}
+ body: |
+ ## Credit Card BIN Data Release
+
+ This release contains updated credit card BIN patterns and validation data.
+
+ ### Data Files
+ - `brands.json` - Legacy format (backward compatible)
+ - `compiled/brands.json` - Enhanced format with detailed metadata
+ - `sources/*.json` - Source data files
+
+ ### Usage
+
+ **Download data programmatically:**
+ ```bash
+ curl -L https://github.com/renatovico/bin-cc/releases/download/data-v${{ steps.get_version.outputs.new_version }}/brands.json -o brands.json
+ ```
+
+ **In JavaScript libraries:**
+ ```javascript
+ // The creditcard-identifier npm package automatically downloads
+ // the latest data from GitHub releases
+ const cc = require('creditcard-identifier');
+ ```
+
+ ### Changes
+ See commit history for details on what BIN patterns were updated.
+ files: |
+ data/brands.json
+ data/compiled/brands.json
+ data/sources/*.json
+ draft: false
+ prerelease: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
new file mode 100644
index 0000000..94374ec
--- /dev/null
+++ b/.github/workflows/publish-npm.yml
@@ -0,0 +1,88 @@
+name: Publish NPM Package
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version to publish (e.g., 1.2.0)'
+ required: true
+ type: string
+ npm_tag:
+ description: 'NPM tag (latest, beta, next)'
+ required: false
+ default: 'latest'
+ type: string
+
+jobs:
+ publish-npm:
+ name: Publish to NPM
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+ registry-url: 'https://registry.npmjs.org'
+
+ - name: Update package version
+ run: |
+ cd libs/javascript
+ npm version ${{ github.event.inputs.version }} --no-git-tag-version
+
+ - name: Install dependencies
+ run: |
+ cd libs/javascript
+ npm install
+
+ - name: Run tests
+ run: |
+ cd libs/javascript
+ npm test
+
+ - name: Publish to NPM
+ run: |
+ cd libs/javascript
+ npm publish --tag ${{ github.event.inputs.npm_tag }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Create Git tag
+ run: |
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
+ git config --local user.name "github-actions[bot]"
+ git tag -a "v${{ github.event.inputs.version }}" -m "Release v${{ github.event.inputs.version }}"
+ git push origin "v${{ github.event.inputs.version }}"
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v1
+ with:
+ tag_name: v${{ github.event.inputs.version }}
+ name: JavaScript Library v${{ github.event.inputs.version }}
+ body: |
+ ## creditcard-identifier v${{ github.event.inputs.version }}
+
+ Published to NPM: https://www.npmjs.com/package/creditcard-identifier
+
+ ### Installation
+ ```bash
+ npm install creditcard-identifier@${{ github.event.inputs.version }}
+ ```
+
+ ### Features
+ - Credit card brand identification
+ - BIN pattern validation
+ - Luhn algorithm validation
+ - Automatic data updates from GitHub releases
+
+ ### Data
+ This library automatically downloads the latest BIN data from GitHub releases.
+
+ See the [data releases](https://github.com/renatovico/bin-cc/releases?q=data-v) for data version history.
+ draft: false
+ prerelease: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..2e4ec9b
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,330 @@
+# Contributing to bin-cc
+
+Thank you for your interest in contributing to bin-cc! This document provides guidelines for contributing card BIN data and code improvements.
+
+## 🎯 Project Philosophy
+
+bin-cc is a **data file project** similar to [browserslist](https://github.com/browserslist/browserslist) and [tzdata](https://www.iana.org/time-zones):
+- **Data is the product** - We maintain authoritative BIN patterns
+- **Source of truth** - Other libraries depend on our data
+- **Community-driven** - Contributions keep data accurate and up-to-date
+- **Multi-language** - Implementations exist in many programming languages
+
+## 📊 Contributing Data
+
+### Adding a New Card Scheme
+
+1. **Create a source file** in `data/sources/`
+
+ ```bash
+ # Example: adding JCB
+ touch data/sources/jcb.json
+ ```
+
+2. **Follow the schema** (see `data/SCHEMA.md`)
+
+ ```json
+ {
+ "scheme": "jcb",
+ "brand": "JCB",
+ "patterns": [
+ {
+ "bin": "^35",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+ }
+ ```
+
+ **Optional: Add detailed BIN information**
+
+ You can include specific BIN-level details for known issuers:
+
+ ```json
+ {
+ "scheme": "visa",
+ "brand": "Visa",
+ "patterns": [
+ {
+ "bin": "^4",
+ "length": [13, 16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"],
+ "bins": [
+ {
+ "bin": "491441",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491414",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ }
+ ]
+ }
+ ```
+
+ The `bins` array is optional and provides:
+ - **bin**: 6-digit BIN number
+ - **type**: Card type (CREDIT, DEBIT)
+ - **category**: Card tier (CLASSIC, GOLD, PLATINUM, etc.) - use `null` if not applicable
+ - **issuer**: Name of the issuing bank
+
+3. **Build and validate**
+
+ ```bash
+ node scripts/build.js
+ ```
+
+4. **Test the changes**
+
+ ```bash
+ cd libs/javascript
+ npm test
+ ```
+
+5. **Document your source**
+ - In your PR description, cite official documentation
+ - Include URLs to BIN databases or official specs
+ - Explain how you verified the patterns
+
+### Updating Existing Patterns
+
+1. **Edit the source file** in `data/sources/`
+2. **Add a comment** explaining the change
+3. **Rebuild** with `node scripts/build.js`
+4. **Run tests** to ensure nothing breaks
+5. **Document** the reason for the update in your PR
+
+### Example: Updating Visa Patterns
+
+```json
+{
+ "scheme": "visa",
+ "brand": "Visa",
+ "patterns": [
+ {
+ "bin": "^4",
+ "length": [13, 16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^6367",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "Added: Visa Denmark/Dankort co-brand cards"
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
+```
+
+## 💻 Contributing Code
+
+### Adding Language Implementations
+
+We welcome implementations in any programming language!
+
+1. **Create a directory** in `examples/` for your language
+2. **Implement the validator** that reads from `data/brands.json`
+3. **Include a README** with usage instructions
+4. **Test your implementation** with sample cards
+5. **Submit a PR** with your code
+
+### Improving the Build System
+
+1. **Fork the repository**
+2. **Create a feature branch**
+ ```bash
+ git checkout -b feature/improve-build-validation
+ ```
+3. **Make your changes** to `scripts/build.js` or related files
+4. **Test thoroughly**
+ ```bash
+ node scripts/build.js
+ # Verify output in data/compiled/ and data/brands.json
+ ```
+5. **Submit a PR** with clear description
+
+## ✅ Data Quality Guidelines
+
+### Required Documentation
+
+Every data contribution must include:
+
+1. **Source citation**
+ - Official card network documentation
+ - Published BIN ranges
+ - ISO standards
+ - Verified test cards
+
+2. **Verification method**
+ - How you tested the patterns
+ - Sample (anonymized) BINs that match
+ - Any edge cases discovered
+
+3. **Change justification**
+ - Why this change is necessary
+ - What problem it solves
+ - Impact on existing patterns
+
+### Data Accuracy Standards
+
+- ✅ Use official sources when available
+- ✅ Test patterns with real (anonymized) BINs
+- ✅ Include edge cases in patterns
+- ✅ Document limitations or uncertainties
+- ❌ Don't guess or assume patterns
+- ❌ Don't use unverified third-party sources
+- ❌ Don't break backward compatibility without discussion
+
+## 🔍 Review Process
+
+### What We Look For
+
+1. **Accuracy** - Patterns must be correct and verified
+2. **Completeness** - All required fields present
+3. **Documentation** - Sources and reasoning provided
+4. **Testing** - Changes don't break existing functionality
+5. **Format** - Follows schema and style guidelines
+
+### Timeline
+
+- Initial review: Within 1 week
+- Follow-up questions: Please respond within 2 weeks
+- Merge: After approval and successful CI checks
+
+## 🚀 Development Workflow
+
+### Setup
+
+```bash
+# Clone the repository
+git clone https://github.com/renatovico/bin-cc.git
+cd bin-cc
+
+# Install JavaScript dependencies (for testing)
+cd libs/javascript
+npm install
+cd ../..
+```
+
+### Making Changes
+
+```bash
+# Create a branch
+git checkout -b update/visa-patterns
+
+# Edit source files
+vim data/sources/visa.json
+
+# Build
+node scripts/build.js
+
+# Test (JavaScript)
+cd libs/javascript
+npm test
+cd ../..
+
+# Commit with descriptive message
+git add data/sources/visa.json
+git commit -m "Update Visa BIN patterns for Dankort co-brand"
+
+# Push and create PR
+git push origin update/visa-patterns
+```
+
+### Commit Message Guidelines
+
+- Use present tense ("Add pattern" not "Added pattern")
+- Be specific ("Update Visa Dankort BINs" not "Update data")
+- Reference issues when applicable ("Fixes #123")
+- Keep first line under 72 characters
+
+## 📝 Pull Request Template
+
+```markdown
+## Description
+Brief description of changes
+
+## Type of Change
+- [ ] New card scheme
+- [ ] Update existing patterns
+- [ ] Bug fix
+- [ ] Documentation
+- [ ] Code improvement
+
+## Data Source
+- Official docs: [URL]
+- Verification: [How you tested]
+
+## Testing
+- [ ] Build script runs successfully
+- [ ] Tests pass (if applicable)
+- [ ] Validated with sample cards
+
+## Checklist
+- [ ] Follows schema guidelines
+- [ ] Includes source documentation
+- [ ] Build script passes
+- [ ] Tests updated if needed
+```
+
+## 🐛 Reporting Issues
+
+### Data Issues
+
+If you find incorrect BIN patterns:
+
+1. **Search existing issues** to avoid duplicates
+2. **Create a new issue** with:
+ - Card scheme affected
+ - Incorrect pattern details
+ - Correct pattern (if known)
+ - Source documentation
+ - Sample BIN that demonstrates the issue
+
+### Code Issues
+
+For bugs in implementations or build scripts:
+
+1. **Describe the bug** clearly
+2. **Include steps to reproduce**
+3. **Show expected vs actual behavior**
+4. **Include environment details** (Node version, OS, etc.)
+
+## 💬 Questions?
+
+- Open an issue for data questions
+- Check `data/SCHEMA.md` for format questions
+- Review existing source files for examples
+
+## 📜 Code of Conduct
+
+- Be respectful and professional
+- Focus on constructive feedback
+- Welcome newcomers and help them learn
+- Assume good intentions
+
+## 🏆 Recognition
+
+Contributors are listed in:
+- Git commit history
+- Release notes for significant contributions
+- README acknowledgments
+
+Thank you for contributing to bin-cc! 🎉
diff --git a/README.md b/README.md
index d913029..ba7bede 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,154 @@
-Validação para cartão de crédito.
+Credit Card BIN Data - Data File Project
====================
-Bin e padrões para validação de cartão de crédito.
+**This is a data file project** similar to tzdata, providing credit card BIN (Bank Identification Number) patterns as a source of truth for other libraries.
-Repositório para esta [gist](https://gist.github.com/erikhenrique/5931368)
+This repository contains authoritative data about credit card BIN patterns for validation and brand identification, along with reference implementations in multiple programming languages.
-Os dados dos cartões: ~~Elo~~, Hipercard desta tabela *não* são oficiais.
-Tentei diversas vezes falar com o pessoal dessas bandeiras afim de ter uma informação oficial, porém, é muito difícil falar com o setor técnico e as atendentes nem sabem o que é bin de cartão :(
+Repository for this [gist](https://gist.github.com/erikhenrique/5931368)
-Esta tabela inicialmente foi montada com coleta de dados de cartões de crédito reais. Onde o usuário colocava o número do cartão de crédito dele e quando não conseguíamos saber qual a banheira pedíamos para que o usuário selecionasse a bandeira e desta forma armazenavamos os primeiros digitos do cartão.
+## 📁 Project Structure
-### Pull Request, contribua
+```
+bin-cc/
+├── data/ # Credit card BIN data
+│ ├── sources/ # Source data files (editable)
+│ │ ├── visa.json
+│ │ ├── mastercard.json
+│ │ └── ...
+│ ├── compiled/ # Compiled enhanced format
+│ │ └── brands.json
+│ ├── brands.json # Legacy format (auto-generated)
+│ ├── SCHEMA.md # Data schema documentation
+│ └── README.md # Data usage guide
+│
+├── scripts/ # Build and validation tools
+│ └── build.js # Compiles source → compiled data
+│
+├── libs/ # Reference implementations
+│ └── javascript/ # JavaScript/Node.js implementation
+│
+├── examples/ # Usage examples in different languages
+│ ├── javascript-example.js
+│ ├── python/
+│ ├── elixir/
+│ ├── ruby/
+│ └── dotnet/
+│
+├── CONTRIBUTING.md # Contribution guidelines
+└── package.json # Build scripts
+```
-Fique a vontade para mandar um PR para que esta tabela permaneça atualizada. Ao fazer o PR por favor, informe como conseguiu essa informação de atualização para que possamos sempre ter dados confiáveis nesta tabela.
+## 🎯 Data Source
+
+The **authoritative data** follows a **build system** similar to browserslist:
+
+- **Source files** [`data/sources/*.json`](./data/sources) - Human-editable card scheme definitions
+- **Build script** [`scripts/build.js`](./scripts/build.js) - Compiles and validates data
+- **Compiled data** [`data/compiled/brands.json`](./data/compiled/brands.json) - Enhanced format with full details
+- **Legacy data** [`data/brands.json`](./data/brands.json) - Backward-compatible format (auto-generated)
+- **Schema docs** [`data/SCHEMA.md`](./data/SCHEMA.md) - Complete schema documentation
+
+### Data Releases
+
+Data is released separately from library code:
+- **Location**: [GitHub Releases](https://github.com/renatovico/bin-cc/releases?q=data-v)
+- **Tagging**: `data-vX.Y.Z` (e.g., `data-v2.0.1`)
+- **Automatic**: Releases are created automatically when `data/sources/` changes
+- **Files included**: `brands.json`, `compiled/brands.json`, `sources/*.json`
+
+### Building the Data
+
+```bash
+npm run build
+```
+
+This compiles source files into both enhanced and legacy formats with validation.
+
+## 📚 Implementations
+
+### JavaScript/Node.js
+Complete implementation available in [`libs/javascript/`](./libs/javascript/)
+
+The JavaScript library automatically downloads the latest BIN data from [GitHub releases](https://github.com/renatovico/bin-cc/releases?q=data-v) during installation.
+
+**Installation:**
+```bash
+npm install creditcard-identifier
+```
+
+**Usage:**
+```javascript
+const cc = require('creditcard-identifier');
+console.log(cc.findBrand('4012001037141112')); // 'visa'
+```
+
+See [JavaScript documentation](./libs/javascript/README.md) for details.
+
+### Python
+Example implementation in [`examples/python/`](./examples/python/)
+
+### Elixir
+Example implementation in [`examples/elixir/`](./examples/elixir/)
+
+### Ruby
+Example implementation in [`examples/ruby/`](./examples/ruby/)
+
+### .NET
+Example implementation in [`examples/dotnet/`](./examples/dotnet/)
+
+## 🎴 Supported Card Brands
+
+| Brand | Starts with | Max length | CVV length |
+| ---------- | ------------------------------------------- | ---------- | ---------- |
+| Visa | 4, 6367 | 13, 16 | 3 |
+| Mastercard | 5, 222100 to 272099 | 16 | 3 |
+| Diners | 301, 305, 36, 38 | 14, 16 | 3 |
+| Elo | 4011, 401178, 401179, 431274, 438935, etc. | 16 | 3 |
+| Amex | 34, 37 | 15 | 4 |
+| Discover | 6011, 622, 64, 65 | 16 | 4 |
+| Aura | 50 | 16 | 3 |
+| Hipercard | 38, 60 | 13, 16, 19 | 3 |
+
+**Note:** Some Brazilian brands (Elo, Hipercard, Aura) do not have official public documentation. Patterns collected from real-world usage.
+
+## 🤝 Contributing
+
+Contributions are welcome! This project follows a **source → build → compiled** workflow:
+
+1. **Data updates:** Edit source files in [`data/sources/`](./data/sources)
+2. **Build:** Run `npm run build` to compile and validate
+3. **Test:** Ensure `npm test` passes
+4. **Document:** Cite sources in your PR description
+
+See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for detailed guidelines.
+
+### Quick Start for Contributors
+
+```bash
+# Edit a source file
+vim data/sources/visa.json
+
+# Build and validate
+npm run build
+
+# Test
+npm test
+
+# Commit changes (both source and generated files)
+git add data/sources/visa.json data/brands.json data/compiled/brands.json
+git commit -m "Update Visa BIN patterns"
+```
+
+## 📝 License
+
+MIT License
+
+## 👥 Contributors
+
+- @jotafelipe
+- @ahonorato
+- @renatoelias
| Bandeira | Começa com | Máximo de número | Máximo de número cvc |
diff --git a/creditcard-identifier.js b/creditcard-identifier.js
deleted file mode 100644
index 9dd2f82..0000000
--- a/creditcard-identifier.js
+++ /dev/null
@@ -1,99 +0,0 @@
-'use strict';
-
-let brands = [{
- name: 'elo',
- regexpBin: '^401178|^401179|^431274|^438935|^451416|^457393|^457631|^457632|^504175|^627780|^636297|^636368|^(506699|5067[0-6]\\d|50677[0-8])|^(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})|^65003[1-3]|^(65003[5-9]|65004\\d|65005[0-1])|^(65040[5-9]|6504[1-3]\\d)|^(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])|^(65054[1-9]|6505[5-8]\\d|65059[0-8])|^(65070\\d|65071[0-8])|^65072[0-7]|^(65090[1-9]|65091\\d|650920)|^(65165[2-9]|6516[6-7]\\d)|^(65500\\d|65501\\d)|^(65502[1-9]|6550[3-4]\\d|65505[0-8])',
- regexpFull: '^(401178|401179|431274|438935|451416|457393|457631|457632|504175|627780|636297|636368|(506699|5067[0-6]\\d|50677[0-8])|(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})|65003[1-3]|(65003[5-9]|65004\\d|65005[0-1])|(65040[5-9]|6504[1-3]\\d)|(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])|(65054[1-9]|6505[5-8]\\d|65059[0-8])|(65070\\d|65071[0-8])|65072[0-7]|(65090[1-9]|65091\\d|650920)|(65165[2-9]|6516[6-7]\\d)|(65500\\d|65501\\d)|(65502[1-9]|6550[3-4]\\d|65505[0-8]))[0-9]{10,12}',
- regexpCvv: '^\d{3}$',
-}, {
- name: 'diners',
- regexpBin: '^3(?:0[0-5]|[68][0-9])',
- regexpFull: '^3(?:0[0-5]|[68][0-9])[0-9]{11}$',
- regexpCvv: '^\d{3}$',
-}, {
- name: 'discover',
- regexpBin: '^6(?:011|5[0-9]{2})',
- regexpFull: '^6(?:011|5[0-9]{2})[0-9]{12}$',
- regexpCvv: '^\d{3}$',
-}, {
- name: 'hipercard',
- regexpBin: '^3841[046]0|^60',
- regexpFull: '^(38[0-9]{17}|60[0-9]{14})$',
- regexpCvv: '^\d{3}$',
-}, {
- name: 'amex',
- regexpBin: '^3[47]',
- regexpFull: '^3[47][0-9]{13}$',
- regexpCvv: '^\d{3,4}$',
-}, {
- name: 'aura',
- regexpBin: '^50[0-9]',
- regexpFull: '^50[0-9]{14,17}$',
- regexpCvv: '^\d{3}$',
-}, {
- name: 'mastercard',
- regexpBin: '^5[1-5][0-9][0-9]|^2[2-7][0-2][0-9]',
- regexpFull: '^5[1-5][0-9]{14}$|^2[2-7][0-2][0-9]{13}$',
- regexpCvv: '^\d{3}$',
-}, {
- name: 'visa',
- regexpBin: '^4|^6367',
- regexpFull: '^4[0-9]{12}(?:[0-9]{3})?|6367[0-9]{12}$',
- regexpCvv: '^\d{3}$',
-}, ];
-
-function cardNumberFilter(cardNumber, brand) {
- if (typeof cardNumber !== 'string') {
- throw Error('Card number should be a string');
- }
-
- return cardNumber.match(brand.regexpFull) !== null;
-}
-
-function cardNameFilter(brandName, brand) {
- return brandName === brand.name;
-}
-
-function hipercardRegexp() {
- let card = brands.filter(cardNameFilter.bind(this,'hipercard'))[0];
- if (card) {
- return new RegExp(card.regexpFull);
- } else {
- return new RegExp('^$');
- }
-}
-
-function findBrand(cardNumber) {
- if(!cardNumber || cardNumber === '') {
- cardNumber = '000000';
- }
- let brand = brands.filter(cardNumberFilter.bind(this, cardNumber))[0];
-
- if (brand === undefined) {
- throw Error('card number not supported');
- }
-
- brand = (brand === undefined) ? undefined : brand.name;
-
- return brand;
-}
-
-function isSupported(cardNumber) {
- let number = cardNumber || '0000000000000001';
-
- let supported = false;
- let result = brands.filter(cardNumberFilter.bind(this, number))[0];
- if (result !== undefined) {
- supported = true;
- }
-
- return supported;
-}
-
-module.exports = {
- findBrand: findBrand,
- isSupported: isSupported,
- hipercardRegexp: hipercardRegexp
-}
-
-
diff --git a/data/README.md b/data/README.md
new file mode 100644
index 0000000..30d6762
--- /dev/null
+++ b/data/README.md
@@ -0,0 +1,148 @@
+# Credit Card BIN Data
+
+This directory contains credit card BIN (Bank Identification Number) data with a build system similar to browserslist.
+
+## Directory Structure
+
+```
+data/
+├── sources/ # Source data files (human-editable)
+│ ├── visa.json
+│ ├── mastercard.json
+│ └── ...
+├── compiled/ # Generated enhanced format
+│ └── brands.json
+├── brands.json # Legacy format (backward compatible)
+├── SCHEMA.md # Complete schema documentation
+└── README.md # This file
+```
+
+## File Formats
+
+### Source Files (`sources/*.json`)
+
+Human-editable card scheme definitions:
+
+```json
+{
+ "scheme": "visa",
+ "brand": "Visa",
+ "patterns": [
+ {
+ "bin": "^4",
+ "length": [13, 16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
+```
+
+### Compiled Format (`compiled/brands.json`)
+
+Enhanced format with full metadata:
+
+```json
+{
+ "scheme": "visa",
+ "brand": "Visa",
+ "type": "credit",
+ "number": {
+ "lengths": [13, 16],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^4|^6367",
+ "full": "(^4[0-9]{9,12}|^4[0-9]{12,15}|^6367[0-9]{12,15})"
+ },
+ "countries": ["GLOBAL"],
+ "metadata": {
+ "sourceFile": "visa.json"
+ }
+}
+```
+
+### Legacy Format (`brands.json`)
+
+Backward-compatible simple format:
+
+```json
+{
+ "name": "visa",
+ "regexpBin": "^4|^6367",
+ "regexpFull": "^4[0-9]{12}(?:[0-9]{3})?|6367[0-9]{12}$",
+ "regexpCvv": "^\\d{3}$"
+}
+```
+
+## Supported Brands
+
+The following credit card brands are currently supported:
+
+- **Elo** - Brazilian credit card brand
+- **Diners** - Diners Club International
+- **Discover** - Discover Financial Services
+- **Hipercard** - Brazilian credit card brand
+- **Amex** (American Express)
+- **Aura** - Brazilian credit card brand
+- **Mastercard** - Mastercard Worldwide
+- **Visa** - Visa Inc.
+
+## Data Usage
+
+### For Library Developers
+
+If you're building a library that needs credit card validation data:
+
+```javascript
+// Method 1: Import the module
+const ccData = require('creditcard-identifier');
+const brands = ccData.data.brands;
+
+// Method 2: Load JSON directly
+const fs = require('fs');
+const path = require('path');
+const dataPath = require.resolve('creditcard-identifier/data/brands.json');
+const brands = JSON.parse(fs.readFileSync(dataPath, 'utf8'));
+```
+
+### For Application Developers
+
+If you're building an application and need to validate credit cards:
+
+```javascript
+const creditcard = require('creditcard-identifier');
+
+// Use the provided functions
+const brand = creditcard.findBrand('4012001037141112');
+const isSupported = creditcard.isSupported('4012001037141112');
+```
+
+## Contributing
+
+When contributing new brand data or updating existing patterns:
+
+1. Update the `data/brands.json` file
+2. Ensure the data follows the schema above
+3. Add test cases in `test/creditcard-identifier.test.js`
+4. Document the source of the information in your pull request
+5. Run `npm run test-unit` to verify all tests pass
+
+## Data Sources
+
+The data in this project has been collected from:
+
+- Real credit card samples (anonymized BIN patterns only)
+- Official brand documentation (where available)
+- Community contributions
+
+**Note:** Some brands (Elo, Hipercard, Aura) may not have official public documentation. The patterns for these brands have been collected from real-world usage and community input.
+
+## License
+
+This data is provided under the MIT license. See the main LICENSE file for details.
diff --git a/data/SCHEMA.md b/data/SCHEMA.md
new file mode 100644
index 0000000..14016bd
--- /dev/null
+++ b/data/SCHEMA.md
@@ -0,0 +1,235 @@
+# BIN-CC Data Schema
+
+This document describes the data schema for the bin-cc project.
+
+## Overview
+
+The bin-cc project uses a **source → build → compiled** approach similar to browserslist:
+- **Source files** (`data/sources/*.json`) - Human-editable card scheme definitions
+- **Build script** (`scripts/build.js`) - Compiles and validates source data
+- **Compiled data** (`data/compiled/brands.json`) - Enhanced format with full details
+- **Legacy data** (`data/brands.json`) - Backward-compatible simple format
+
+## Source Data Format
+
+Source files in `data/sources/` define card schemes with their BIN patterns.
+
+### Source Schema
+
+```json
+{
+ "scheme": "string", // Scheme identifier (lowercase, e.g., "visa")
+ "brand": "string", // Display name (e.g., "Visa")
+ "patterns": [ // Array of BIN patterns for this scheme
+ {
+ "bin": "regex", // BIN matching pattern (first 4-8 digits)
+ "length": [number], // Valid card number lengths (array)
+ "luhn": boolean, // Whether Luhn algorithm applies
+ "cvvLength": number, // CVV/CVC code length
+ "comment": "string" // Optional: explanation of the pattern
+ }
+ ],
+ "type": "credit|debit", // Card type
+ "countries": ["string"], // ISO country codes or "GLOBAL"
+ "bins": [ // Optional: Detailed BIN-level information
+ {
+ "bin": "string", // Specific 6-digit BIN
+ "type": "string", // Card type (CREDIT, DEBIT)
+ "category": "string",// Card category (CLASSIC, GOLD, PLATINUM, etc.) or null
+ "issuer": "string" // Issuing bank name
+ }
+ ]
+}
+```
+
+### Example Source File
+
+```json
+{
+ "scheme": "visa",
+ "brand": "Visa",
+ "patterns": [
+ {
+ "bin": "^4",
+ "length": [13, 16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"],
+ "bins": [
+ {
+ "bin": "491441",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491414",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ }
+ ]
+}
+```
+
+## Compiled Data Format
+
+The build process generates `data/compiled/brands.json` with enhanced metadata.
+
+### Compiled Schema
+
+```json
+{
+ "scheme": "string", // Scheme identifier
+ "brand": "string", // Display brand name
+ "type": "credit|debit", // Card type
+ "number": {
+ "lengths": [number], // Valid card number lengths
+ "luhn": boolean // Luhn validation required
+ },
+ "cvv": {
+ "length": number // CVV code length
+ },
+ "patterns": {
+ "bin": "regex", // Combined BIN pattern
+ "full": "regex" // Full card number validation pattern
+ },
+ "countries": ["string"], // Issuing countries
+ "metadata": {
+ "sourceFile": "string" // Source file reference
+ },
+ "bins": [ // Optional: Detailed BIN information
+ {
+ "bin": "string", // Specific 6-digit BIN
+ "type": "string", // Card type (CREDIT, DEBIT)
+ "category": "string", // Card category (CLASSIC, GOLD, PLATINUM) or null
+ "issuer": "string" // Issuing bank name or null
+ }
+ ]
+}
+```
+
+### Future Enhancements
+
+The schema is designed to accommodate additional fields:
+
+```json
+{
+ "prepaid": boolean, // Prepaid card indicator
+ "country": { // Detailed country info
+ "numeric": "string",
+ "alpha2": "string",
+ "name": "string",
+ "emoji": "string",
+ "currency": "string",
+ "latitude": number,
+ "longitude": number
+ },
+ "bank": { // Issuing bank details
+ "name": "string",
+ "url": "string",
+ "phone": "string",
+ "city": "string"
+ }
+}
+```
+
+## Legacy Format (Backward Compatible)
+
+The build also generates `data/brands.json` for backward compatibility.
+
+### Legacy Schema
+
+```json
+{
+ "name": "string", // Scheme name (e.g., "visa")
+ "regexpBin": "regex", // BIN pattern
+ "regexpFull": "regex", // Full validation pattern
+ "regexpCvv": "regex" // CVV validation pattern
+}
+```
+
+## Building the Data
+
+### Prerequisites
+
+- Node.js 12+
+
+### Build Command
+
+```bash
+node scripts/build.js
+```
+
+This will:
+1. Read all source files from `data/sources/`
+2. Compile patterns and generate regexes
+3. Validate the compiled data
+4. Write `data/compiled/brands.json` (enhanced format)
+5. Write `data/brands.json` (legacy format)
+
+### Validation
+
+The build script automatically validates:
+- Required fields are present
+- Regex patterns are valid
+- Data structure is correct
+
+## Contributing
+
+### Adding a New Card Scheme
+
+1. Create a new file in `data/sources/` (e.g., `jcb.json`)
+2. Follow the source schema format
+3. Run `node scripts/build.js` to compile
+4. Test the generated patterns
+5. Submit a pull request
+
+### Updating Existing Patterns
+
+1. Edit the appropriate file in `data/sources/`
+2. Add comments explaining the changes
+3. Run `node scripts/build.js` to recompile
+4. Verify tests still pass
+5. Submit a pull request with source documentation
+
+### Example: Adding JCB
+
+```json
+{
+ "scheme": "jcb",
+ "brand": "JCB",
+ "patterns": [
+ {
+ "bin": "^35",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
+```
+
+Then run:
+```bash
+node scripts/build.js
+```
+
+## Data Sources
+
+When contributing, please document your sources:
+- Official documentation from card networks
+- Published BIN ranges from issuing banks
+- ISO/IEC 7812 standards
+- Community-verified patterns
+
+Include source URLs in pull request descriptions.
+
+## License
+
+This data schema and all data files are provided under the MIT license.
diff --git a/data/brands.json b/data/brands.json
new file mode 100644
index 0000000..813d396
--- /dev/null
+++ b/data/brands.json
@@ -0,0 +1,50 @@
+[
+ {
+ "name": "elo",
+ "regexpBin": "^401178|^401179|^431274|^438935|^451416|^457393|^457631|^457632|^504175|^627780|^636297|^636368|^(506699|5067[0-6]\\d|50677[0-8])|^(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})|^65003[1-3]|^(65003[5-9]|65004\\d|65005[0-1])|^(65040[5-9]|6504[1-3]\\d)|^(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])|^(65054[1-9]|6505[5-8]\\d|65059[0-8])|^(65070\\d|65071[0-8])|^65072[0-7]|^(65090[1-9]|65091\\d|650920)|^(65165[2-9]|6516[6-7]\\d)|^(65500\\d|65501\\d)|^(65502[1-9]|6550[3-4]\\d|65505[0-8])",
+ "regexpFull": "^(401178|401179|431274|438935|451416|457393|457631|457632|504175|627780|636297|636368|(506699|5067[0-6]\\d|50677[0-8])|(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})|65003[1-3]|(65003[5-9]|65004\\d|65005[0-1])|(65040[5-9]|6504[1-3]\\d)|(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])|(65054[1-9]|6505[5-8]\\d|65059[0-8])|(65070\\d|65071[0-8])|65072[0-7]|(65090[1-9]|65091\\d|650920)|(65165[2-9]|6516[6-7]\\d)|(65500\\d|65501\\d)|(65502[1-9]|6550[3-4]\\d|65505[0-8]))[0-9]{10,12}",
+ "regexpCvv": "^\\d{3}$"
+ },
+ {
+ "name": "diners",
+ "regexpBin": "^3(?:0[0-5]|[68][0-9])",
+ "regexpFull": "^3(?:0[0-5]|[68][0-9])[0-9]{11}$",
+ "regexpCvv": "^\\d{3}$"
+ },
+ {
+ "name": "discover",
+ "regexpBin": "^6(?:011|5[0-9]{2})",
+ "regexpFull": "^6(?:011|5[0-9]{2})[0-9]{12}$",
+ "regexpCvv": "^\\d{3}$"
+ },
+ {
+ "name": "hipercard",
+ "regexpBin": "^3841[046]0|^60",
+ "regexpFull": "^(38[0-9]{17}|60[0-9]{14})$",
+ "regexpCvv": "^\\d{3}$"
+ },
+ {
+ "name": "amex",
+ "regexpBin": "^3[47]",
+ "regexpFull": "^3[47][0-9]{13}$",
+ "regexpCvv": "^\\d{3,4}$"
+ },
+ {
+ "name": "aura",
+ "regexpBin": "^50[0-9]",
+ "regexpFull": "^50[0-9]{14,17}$",
+ "regexpCvv": "^\\d{3}$"
+ },
+ {
+ "name": "mastercard",
+ "regexpBin": "^5[1-5][0-9][0-9]|^2[2-7][0-2][0-9]",
+ "regexpFull": "^5[1-5][0-9]{14}$|^2[2-7][0-2][0-9]{13}$",
+ "regexpCvv": "^\\d{3}$"
+ },
+ {
+ "name": "visa",
+ "regexpBin": "^4|^6367",
+ "regexpFull": "^4[0-9]{12}(?:[0-9]{3})?|6367[0-9]{12}$",
+ "regexpCvv": "^\\d{3}$"
+ }
+]
diff --git a/data/compiled/brands.json b/data/compiled/brands.json
new file mode 100644
index 0000000..cea2ec6
--- /dev/null
+++ b/data/compiled/brands.json
@@ -0,0 +1,315 @@
+[
+ {
+ "scheme": "amex",
+ "brand": "American Express",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 15
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 4
+ },
+ "patterns": {
+ "bin": "^3[47]",
+ "full": "(^3[47][0-9]{11,14})"
+ },
+ "countries": [
+ "GLOBAL"
+ ],
+ "metadata": {
+ "sourceFile": "amex.json"
+ }
+ },
+ {
+ "scheme": "aura",
+ "brand": "Aura",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^50[0-9]",
+ "full": "(^50[0-9][0-9]{12,15})"
+ },
+ "countries": [
+ "BR"
+ ],
+ "metadata": {
+ "sourceFile": "aura.json"
+ }
+ },
+ {
+ "scheme": "diners",
+ "brand": "Diners Club",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 14,
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^3(?:0[0-5]|[68][0-9])",
+ "full": "(^3(?:0[0-5]|[68][0-9])[0-9]{10,13}|^3(?:0[0-5]|[68][0-9])[0-9]{12,15})"
+ },
+ "countries": [
+ "GLOBAL"
+ ],
+ "metadata": {
+ "sourceFile": "diners.json"
+ }
+ },
+ {
+ "scheme": "discover",
+ "brand": "Discover",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^6011|^622|^64|^65",
+ "full": "(^6011[0-9]{12,15}|^622[0-9]{12,15}|^64[0-9]{12,15}|^65[0-9]{12,15})"
+ },
+ "countries": [
+ "US"
+ ],
+ "metadata": {
+ "sourceFile": "discover.json"
+ }
+ },
+ {
+ "scheme": "elo",
+ "brand": "Elo",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^401178|^401179|^431274|^438935|^451416|^457393|^457631|^457632|^504175|^627780|^636297|^636368|^(506699|5067[0-6]\\d|50677[0-8])|^(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})|^65003[1-3]|^(65003[5-9]|65004\\d|65005[0-1])|^(65040[5-9]|6504[1-3]\\d)|^(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])|^(65054[1-9]|6505[5-8]\\d|65059[0-8])|^(65070\\d|65071[0-8])|^65072[0-7]|^(65090[1-9]|65091\\d|650920)|^(65165[2-9]|6516[6-7]\\d)|^(65500\\d|65501\\d)|^(65502[1-9]|6550[3-4]\\d|65505[0-8])",
+ "full": "(^401178|^401179|^431274|^438935|^451416|^457393|^457631|^457632|^504175[0-9]{12,15}|^627780|^636297|^636368[0-9]{12,15}|^(506699|5067[0-6]\\d|50677[0-8])[0-9]{12,15}|^(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})[0-9]{12,15}|^65003[1-3][0-9]{12,15}|^(65003[5-9]|65004\\d|65005[0-1])[0-9]{12,15}|^(65040[5-9]|6504[1-3]\\d)[0-9]{12,15}|^(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])[0-9]{12,15}|^(65054[1-9]|6505[5-8]\\d|65059[0-8])[0-9]{12,15}|^(65070\\d|65071[0-8])[0-9]{12,15}|^65072[0-7][0-9]{12,15}|^(65090[1-9]|65091\\d|650920)[0-9]{12,15}|^(65165[2-9]|6516[6-7]\\d)[0-9]{12,15}|^(65500\\d|65501\\d)[0-9]{12,15}|^(65502[1-9]|6550[3-4]\\d|65505[0-8])[0-9]{12,15})"
+ },
+ "countries": [
+ "BR"
+ ],
+ "metadata": {
+ "sourceFile": "elo.json"
+ }
+ },
+ {
+ "scheme": "hipercard",
+ "brand": "Hipercard",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 16,
+ 19,
+ 13
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^3841[046]0|^60",
+ "full": "(^3841[046]0[0-9]{12,15}|^3841[046]0[0-9]{15,18}|^60[0-9]{9,12}|^60[0-9]{12,15}|^60[0-9]{15,18})"
+ },
+ "countries": [
+ "BR"
+ ],
+ "metadata": {
+ "sourceFile": "hipercard.json"
+ }
+ },
+ {
+ "scheme": "mastercard",
+ "brand": "Mastercard",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^5[1-5]|^2[2-7][0-2][0-9]",
+ "full": "(^5[1-5][0-9]{12,15}|^2[2-7][0-2][0-9][0-9]{12,15})"
+ },
+ "countries": [
+ "GLOBAL"
+ ],
+ "metadata": {
+ "sourceFile": "mastercard.json"
+ }
+ },
+ {
+ "scheme": "visa",
+ "brand": "Visa",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 13,
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^4|^6367",
+ "full": "(^4[0-9]{9,12}|^4[0-9]{12,15}|^6367[0-9]{12,15})"
+ },
+ "countries": [
+ "GLOBAL"
+ ],
+ "metadata": {
+ "sourceFile": "visa-detailed.example.json"
+ },
+ "bins": [
+ {
+ "bin": "491441",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491440",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491439",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491423",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S.A. (BANRISUL S.A.)"
+ },
+ {
+ "bin": "491416",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ },
+ {
+ "bin": "491415",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ },
+ {
+ "bin": "491414",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ },
+ {
+ "bin": "491413",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A"
+ },
+ {
+ "bin": "491412",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A"
+ },
+ {
+ "bin": "491411",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A"
+ },
+ {
+ "bin": "491402",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S.A. (BANRISUL S.A.)"
+ },
+ {
+ "bin": "491316",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO SANTANDER BRASIL, S.A."
+ },
+ {
+ "bin": "491315",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO SANTANDER, S.A."
+ },
+ {
+ "bin": "491314",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO SANTANDER, S.A."
+ },
+ {
+ "bin": "491256",
+ "type": "CREDIT",
+ "category": "PLATINUM",
+ "issuer": "BANCO PANAMERICANO, S.A."
+ }
+ ]
+ },
+ {
+ "scheme": "visa",
+ "brand": "Visa",
+ "type": "credit",
+ "number": {
+ "lengths": [
+ 13,
+ 16
+ ],
+ "luhn": true
+ },
+ "cvv": {
+ "length": 3
+ },
+ "patterns": {
+ "bin": "^4|^6367",
+ "full": "(^4[0-9]{9,12}|^4[0-9]{12,15}|^6367[0-9]{12,15})"
+ },
+ "countries": [
+ "GLOBAL"
+ ],
+ "metadata": {
+ "sourceFile": "visa.json"
+ }
+ }
+]
\ No newline at end of file
diff --git a/data/sources/amex.json b/data/sources/amex.json
new file mode 100644
index 0000000..c485bb7
--- /dev/null
+++ b/data/sources/amex.json
@@ -0,0 +1,14 @@
+{
+ "scheme": "amex",
+ "brand": "American Express",
+ "patterns": [
+ {
+ "bin": "^3[47]",
+ "length": [15],
+ "luhn": true,
+ "cvvLength": 4
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
diff --git a/data/sources/aura.json b/data/sources/aura.json
new file mode 100644
index 0000000..7b1cbb2
--- /dev/null
+++ b/data/sources/aura.json
@@ -0,0 +1,14 @@
+{
+ "scheme": "aura",
+ "brand": "Aura",
+ "patterns": [
+ {
+ "bin": "^50[0-9]",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["BR"]
+}
diff --git a/data/sources/diners.json b/data/sources/diners.json
new file mode 100644
index 0000000..f4d04cb
--- /dev/null
+++ b/data/sources/diners.json
@@ -0,0 +1,14 @@
+{
+ "scheme": "diners",
+ "brand": "Diners Club",
+ "patterns": [
+ {
+ "bin": "^3(?:0[0-5]|[68][0-9])",
+ "length": [14, 16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
diff --git a/data/sources/discover.json b/data/sources/discover.json
new file mode 100644
index 0000000..c84aaa7
--- /dev/null
+++ b/data/sources/discover.json
@@ -0,0 +1,32 @@
+{
+ "scheme": "discover",
+ "brand": "Discover",
+ "patterns": [
+ {
+ "bin": "^6011",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^622",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^64",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^65",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["US"]
+}
diff --git a/data/sources/elo.json b/data/sources/elo.json
new file mode 100644
index 0000000..caf1096
--- /dev/null
+++ b/data/sources/elo.json
@@ -0,0 +1,111 @@
+{
+ "scheme": "elo",
+ "brand": "Elo",
+ "patterns": [
+ {
+ "bin": "^401178|^401179|^431274|^438935|^451416|^457393|^457631|^457632|^504175",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^627780|^636297|^636368",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^(506699|5067[0-6]\\d|50677[0-8])",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "506699-506778"
+ },
+ {
+ "bin": "^(50900\\d|5090[1-9]\\d|509[1-9]\\d{2})",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "509000-509999"
+ },
+ {
+ "bin": "^65003[1-3]",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650031-650033"
+ },
+ {
+ "bin": "^(65003[5-9]|65004\\d|65005[0-1])",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650035-650051"
+ },
+ {
+ "bin": "^(65040[5-9]|6504[1-3]\\d)",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650405-650439"
+ },
+ {
+ "bin": "^(65048[5-9]|65049\\d|6505[0-2]\\d|65053[0-8])",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650485-650538"
+ },
+ {
+ "bin": "^(65054[1-9]|6505[5-8]\\d|65059[0-8])",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650541-650598"
+ },
+ {
+ "bin": "^(65070\\d|65071[0-8])",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650700-650718"
+ },
+ {
+ "bin": "^65072[0-7]",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650720-650727"
+ },
+ {
+ "bin": "^(65090[1-9]|65091\\d|650920)",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "650901-650920"
+ },
+ {
+ "bin": "^(65165[2-9]|6516[6-7]\\d)",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "651652-651679"
+ },
+ {
+ "bin": "^(65500\\d|65501\\d)",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "655000-655019"
+ },
+ {
+ "bin": "^(65502[1-9]|6550[3-4]\\d|65505[0-8])",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "655021-655058"
+ }
+ ],
+ "type": "credit",
+ "countries": ["BR"]
+}
diff --git a/data/sources/hipercard.json b/data/sources/hipercard.json
new file mode 100644
index 0000000..ef1b7bd
--- /dev/null
+++ b/data/sources/hipercard.json
@@ -0,0 +1,20 @@
+{
+ "scheme": "hipercard",
+ "brand": "Hipercard",
+ "patterns": [
+ {
+ "bin": "^3841[046]0",
+ "length": [16, 19],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^60",
+ "length": [13, 16, 19],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["BR"]
+}
diff --git a/data/sources/mastercard.json b/data/sources/mastercard.json
new file mode 100644
index 0000000..0309f45
--- /dev/null
+++ b/data/sources/mastercard.json
@@ -0,0 +1,21 @@
+{
+ "scheme": "mastercard",
+ "brand": "Mastercard",
+ "patterns": [
+ {
+ "bin": "^5[1-5]",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^2[2-7][0-2][0-9]",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3,
+ "comment": "2-series BINs (222100-272099)"
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
diff --git a/data/sources/visa-detailed.example.json b/data/sources/visa-detailed.example.json
new file mode 100644
index 0000000..5a9169f
--- /dev/null
+++ b/data/sources/visa-detailed.example.json
@@ -0,0 +1,112 @@
+{
+ "scheme": "visa",
+ "brand": "Visa",
+ "patterns": [
+ {
+ "bin": "^4",
+ "length": [13, 16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^6367",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"],
+ "bins": [
+ {
+ "bin": "491441",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491440",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491439",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO PROSPER, S.A."
+ },
+ {
+ "bin": "491423",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S.A. (BANRISUL S.A.)"
+ },
+ {
+ "bin": "491416",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ },
+ {
+ "bin": "491415",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ },
+ {
+ "bin": "491414",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO DO ESTADO DO PARANA"
+ },
+ {
+ "bin": "491413",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A"
+ },
+ {
+ "bin": "491412",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A"
+ },
+ {
+ "bin": "491411",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S/A"
+ },
+ {
+ "bin": "491402",
+ "type": "CREDIT",
+ "category": null,
+ "issuer": "BANCO DO ESTADO DO RIO GRANDE DO SUL S.A. (BANRISUL S.A.)"
+ },
+ {
+ "bin": "491316",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO SANTANDER BRASIL, S.A."
+ },
+ {
+ "bin": "491315",
+ "type": "CREDIT",
+ "category": "CLASSIC",
+ "issuer": "BANCO SANTANDER, S.A."
+ },
+ {
+ "bin": "491314",
+ "type": "CREDIT",
+ "category": "GOLD",
+ "issuer": "BANCO SANTANDER, S.A."
+ },
+ {
+ "bin": "491256",
+ "type": "CREDIT",
+ "category": "PLATINUM",
+ "issuer": "BANCO PANAMERICANO, S.A."
+ }
+ ]
+}
diff --git a/data/sources/visa.json b/data/sources/visa.json
new file mode 100644
index 0000000..4fea282
--- /dev/null
+++ b/data/sources/visa.json
@@ -0,0 +1,20 @@
+{
+ "scheme": "visa",
+ "brand": "Visa",
+ "patterns": [
+ {
+ "bin": "^4",
+ "length": [13, 16],
+ "luhn": true,
+ "cvvLength": 3
+ },
+ {
+ "bin": "^6367",
+ "length": [16],
+ "luhn": true,
+ "cvvLength": 3
+ }
+ ],
+ "type": "credit",
+ "countries": ["GLOBAL"]
+}
diff --git a/examples/BIN_DATA_USAGE.md b/examples/BIN_DATA_USAGE.md
new file mode 100644
index 0000000..bbdf018
--- /dev/null
+++ b/examples/BIN_DATA_USAGE.md
@@ -0,0 +1,234 @@
+# Usage Examples with Detailed BIN Data
+
+This document shows how to use the enhanced BIN-level data in your applications.
+
+## Accessing BIN Details
+
+### JavaScript Example
+
+```javascript
+const data = require('creditcard-identifier/data/compiled/brands.json');
+
+// Find a specific scheme
+const visa = data.find(brand => brand.scheme === 'visa');
+
+// Check if BIN-level details are available
+if (visa.bins) {
+ console.log(`${visa.brand} has ${visa.bins.length} detailed BINs`);
+
+ // Find a specific BIN
+ const bin = visa.bins.find(b => b.bin === '491414');
+
+ if (bin) {
+ console.log(`BIN: ${bin.bin}`);
+ console.log(`Type: ${bin.type}`); // CREDIT or DEBIT
+ console.log(`Category: ${bin.category}`); // GOLD, PLATINUM, etc.
+ console.log(`Issuer: ${bin.issuer}`); // Bank name
+ }
+}
+```
+
+### Python Example
+
+```python
+import json
+
+# Load compiled data
+with open('data/compiled/brands.json', 'r') as f:
+ brands = json.load(f)
+
+# Find scheme
+visa = next((b for b in brands if b['scheme'] == 'visa'), None)
+
+# Access BIN details
+if visa and 'bins' in visa:
+ for bin_data in visa['bins']:
+ print(f"BIN {bin_data['bin']}: {bin_data['issuer']}")
+ if bin_data['category']:
+ print(f" Category: {bin_data['category']}")
+```
+
+## Card Category Lookup
+
+```javascript
+function getCardCategory(cardNumber) {
+ const bin = cardNumber.substring(0, 6);
+ const data = require('creditcard-identifier/data/compiled/brands.json');
+
+ for (const brand of data) {
+ if (brand.bins) {
+ const binData = brand.bins.find(b => b.bin === bin);
+ if (binData) {
+ return {
+ scheme: brand.scheme,
+ brand: brand.brand,
+ issuer: binData.issuer,
+ category: binData.category,
+ type: binData.type
+ };
+ }
+ }
+ }
+
+ return null;
+}
+
+// Usage
+const info = getCardCategory('4914140000000000');
+if (info) {
+ console.log(`Scheme: ${info.scheme}`); // visa
+ console.log(`Issuer: ${info.issuer}`); // BANCO DO ESTADO DO PARANA
+ console.log(`Category: ${info.category}`); // GOLD
+ console.log(`Type: ${info.type}`); // CREDIT
+}
+```
+
+## Issuer Bank Lookup
+
+```javascript
+function getIssuerBank(cardNumber) {
+ const bin = cardNumber.substring(0, 6);
+ const data = require('creditcard-identifier/data/compiled/brands.json');
+
+ for (const brand of data) {
+ if (brand.bins) {
+ const binData = brand.bins.find(b => b.bin === bin);
+ if (binData && binData.issuer) {
+ return {
+ issuer: binData.issuer,
+ brand: brand.brand
+ };
+ }
+ }
+ }
+
+ return null;
+}
+
+// Usage
+const bank = getIssuerBank('4914410000000000');
+console.log(bank.issuer); // "BANCO PROSPER, S.A."
+```
+
+## Listing All Issuers by Scheme
+
+```javascript
+const data = require('creditcard-identifier/data/compiled/brands.json');
+
+// Get all Visa issuers
+const visa = data.find(b => b.scheme === 'visa');
+
+if (visa && visa.bins) {
+ const issuers = [...new Set(visa.bins.map(b => b.issuer))];
+ console.log('Visa Issuers:');
+ issuers.forEach(issuer => console.log(` - ${issuer}`));
+}
+```
+
+## Category Statistics
+
+```javascript
+const data = require('creditcard-identifier/data/compiled/brands.json');
+
+function getCategoryStats(scheme) {
+ const brand = data.find(b => b.scheme === scheme);
+
+ if (!brand || !brand.bins) {
+ return null;
+ }
+
+ const stats = {};
+ brand.bins.forEach(bin => {
+ const category = bin.category || 'Standard';
+ stats[category] = (stats[category] || 0) + 1;
+ });
+
+ return stats;
+}
+
+// Usage
+const visaStats = getCategoryStats('visa');
+console.log('Visa Card Categories:');
+console.log(visaStats);
+// Output: { GOLD: 3, CLASSIC: 4, PLATINUM: 1, Standard: 7 }
+```
+
+## REST API Example
+
+```javascript
+const express = require('express');
+const data = require('creditcard-identifier/data/compiled/brands.json');
+
+const app = express();
+
+app.get('/api/bin/:bin', (req, res) => {
+ const bin = req.params.bin;
+
+ for (const brand of data) {
+ if (brand.bins) {
+ const binData = brand.bins.find(b => b.bin === bin);
+ if (binData) {
+ return res.json({
+ bin: bin,
+ scheme: brand.scheme,
+ brand: brand.brand,
+ type: binData.type,
+ category: binData.category,
+ issuer: binData.issuer
+ });
+ }
+ }
+ }
+
+ res.status(404).json({ error: 'BIN not found' });
+});
+
+app.listen(3000, () => {
+ console.log('BIN API running on port 3000');
+});
+```
+
+## TypeScript Types
+
+```typescript
+interface BinData {
+ bin: string;
+ type: string;
+ category: string | null;
+ issuer: string | null;
+}
+
+interface Brand {
+ scheme: string;
+ brand: string;
+ type: string;
+ number: {
+ lengths: number[];
+ luhn: boolean;
+ };
+ cvv: {
+ length: number;
+ };
+ patterns: {
+ bin: string;
+ full: string;
+ };
+ countries: string[];
+ metadata: {
+ sourceFile: string;
+ };
+ bins?: BinData[];
+}
+
+// Usage
+import data from 'creditcard-identifier/data/compiled/brands.json';
+const brands: Brand[] = data;
+```
+
+## Notes
+
+- BIN-level details are **optional** - not all schemes have them
+- Always check if `brand.bins` exists before accessing
+- BIN data is primarily for Brazilian cards currently
+- `category` can be `null` for standard cards
+- `issuer` contains the bank name as provided in the source data
diff --git a/examples/dotnet/CreditCardValidator.cs b/examples/dotnet/CreditCardValidator.cs
new file mode 100644
index 0000000..889f893
--- /dev/null
+++ b/examples/dotnet/CreditCardValidator.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+
+namespace CreditCardValidation
+{
+ ///
+ /// Credit Card BIN Validator - .NET Example
+ ///
+ /// This class shows how to use the bin-cc data file project in .NET/C#.
+ /// It loads the brands.json file and performs credit card validation.
+ ///
+ public class CreditCardValidator
+ {
+ private readonly List _brands;
+
+ ///
+ /// Brand information from data file
+ ///
+ public class Brand
+ {
+ public string name { get; set; }
+ public string regexpBin { get; set; }
+ public string regexpFull { get; set; }
+ public string regexpCvv { get; set; }
+ }
+
+ ///
+ /// Initialize validator with brand data
+ ///
+ /// Path to brands.json. If null, uses default location.
+ public CreditCardValidator(string dataPath = null)
+ {
+ if (string.IsNullOrEmpty(dataPath))
+ {
+ // Default path relative to executable
+ var baseDir = AppDomain.CurrentDomain.BaseDirectory;
+ dataPath = Path.Combine(baseDir, "../../data/brands.json");
+ }
+
+ var jsonString = File.ReadAllText(dataPath);
+ _brands = JsonSerializer.Deserialize>(jsonString);
+ }
+
+ ///
+ /// Identify the credit card brand
+ ///
+ /// Credit card number
+ /// Brand name or null if not found
+ public string FindBrand(string cardNumber)
+ {
+ if (string.IsNullOrEmpty(cardNumber))
+ return null;
+
+ var brand = _brands.FirstOrDefault(b =>
+ {
+ var regex = new Regex(b.regexpFull);
+ return regex.IsMatch(cardNumber);
+ });
+
+ return brand?.name;
+ }
+
+ ///
+ /// Check if card number is supported
+ ///
+ /// Credit card number
+ /// True if supported, false otherwise
+ public bool IsSupported(string cardNumber)
+ {
+ return FindBrand(cardNumber) != null;
+ }
+
+ ///
+ /// Validate CVV for a specific brand
+ ///
+ /// CVV code
+ /// Brand name (e.g., "visa", "mastercard")
+ /// True if valid, false otherwise
+ public bool ValidateCvv(string cvv, string brandName)
+ {
+ var brand = GetBrandInfo(brandName);
+ if (brand == null)
+ return false;
+
+ var regex = new Regex(brand.regexpCvv);
+ return regex.IsMatch(cvv);
+ }
+
+ ///
+ /// Get information about a specific brand
+ ///
+ /// Brand name (e.g., "visa", "mastercard")
+ /// Brand information or null if not found
+ public Brand GetBrandInfo(string brandName)
+ {
+ return _brands.FirstOrDefault(b => b.name == brandName);
+ }
+
+ ///
+ /// List all supported brands
+ ///
+ /// List of brand names
+ public List ListBrands()
+ {
+ return _brands.Select(b => b.name).ToList();
+ }
+ }
+
+ ///
+ /// Example usage of the credit card validator
+ ///
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ Console.WriteLine("=== Credit Card Validator - .NET Example ===\n");
+
+ var validator = new CreditCardValidator();
+
+ // Example 1: List all brands
+ var brands = validator.ListBrands();
+ Console.WriteLine($"Supported brands: {string.Join(", ", brands)}");
+ Console.WriteLine();
+
+ // Example 2: Identify card brands
+ var testCards = new Dictionary
+ {
+ { "4012001037141112", "visa" },
+ { "5533798818319497", "mastercard" },
+ { "378282246310005", "amex" },
+ { "6011236044609927", "discover" }
+ };
+
+ Console.WriteLine("Card brand identification:");
+ foreach (var kvp in testCards)
+ {
+ var brand = validator.FindBrand(kvp.Key);
+ var status = brand != null ? "✓" : "✗";
+ Console.WriteLine($"{status} {kvp.Key}: {brand} (expected: {kvp.Value})");
+ }
+ Console.WriteLine();
+
+ // Example 3: CVV validation
+ Console.WriteLine("CVV validation:");
+ Console.WriteLine($"Visa CVV 123: {validator.ValidateCvv("123", "visa")}");
+ Console.WriteLine($"Amex CVV 1234: {validator.ValidateCvv("1234", "amex")}");
+ Console.WriteLine($"Visa CVV 12: {validator.ValidateCvv("12", "visa")} (invalid)");
+ Console.WriteLine();
+
+ // Example 4: Get brand details
+ Console.WriteLine("Visa brand details:");
+ var visaInfo = validator.GetBrandInfo("visa");
+ if (visaInfo != null)
+ {
+ Console.WriteLine($" BIN pattern: {visaInfo.regexpBin}");
+ Console.WriteLine($" Full pattern: {visaInfo.regexpFull}");
+ Console.WriteLine($" CVV pattern: {visaInfo.regexpCvv}");
+ }
+ else
+ {
+ Console.WriteLine(" Not found");
+ }
+ }
+ }
+}
diff --git a/examples/dotnet/README.md b/examples/dotnet/README.md
new file mode 100644
index 0000000..a175c2a
--- /dev/null
+++ b/examples/dotnet/README.md
@@ -0,0 +1,52 @@
+# Credit Card Validator - .NET Example
+
+C#/.NET implementation showing how to use the bin-cc data file project.
+
+## Usage
+
+```bash
+dotnet run
+```
+
+Or compile and run:
+```bash
+csc CreditCardValidator.cs
+./CreditCardValidator
+```
+
+## Requirements
+
+- .NET 5.0+ or .NET Core 3.1+
+- System.Text.Json (included in .NET)
+
+## Features
+
+- Load brand data from JSON
+- Identify credit card brand
+- Validate CVV codes
+- Check if card is supported
+- Get brand information
+
+## Example
+
+```csharp
+using CreditCardValidation;
+
+var validator = new CreditCardValidator();
+
+// Identify brand
+var brand = validator.FindBrand("4012001037141112");
+Console.WriteLine(brand); // "visa"
+
+// Check if supported
+var supported = validator.IsSupported("4012001037141112");
+Console.WriteLine(supported); // True
+
+// Validate CVV
+var valid = validator.ValidateCvv("123", "visa");
+Console.WriteLine(valid); // True
+```
+
+## Data Source
+
+Loads data from [`../../data/brands.json`](../../data/brands.json)
diff --git a/examples/elixir/README.md b/examples/elixir/README.md
new file mode 100644
index 0000000..09eb705
--- /dev/null
+++ b/examples/elixir/README.md
@@ -0,0 +1,51 @@
+# Credit Card Validator - Elixir Example
+
+Elixir implementation showing how to use the bin-cc data file project.
+
+## Requirements
+
+- Elixir 1.10+
+- Jason library for JSON parsing
+
+Add to your `mix.exs`:
+```elixir
+{:jason, "~> 1.4"}
+```
+
+## Usage
+
+```bash
+# In iex
+iex> c("credit_card_validator.ex")
+iex> Example.run()
+```
+
+## Features
+
+- Load brand data from JSON
+- Identify credit card brand
+- Validate CVV codes
+- Check if card is supported
+- Get brand information
+
+## Example
+
+```elixir
+brands = CreditCardValidator.load_brands()
+
+# Identify brand
+brand = CreditCardValidator.find_brand("4012001037141112", brands)
+IO.puts(brand) # "visa"
+
+# Check if supported
+supported = CreditCardValidator.is_supported?("4012001037141112", brands)
+IO.puts(supported) # true
+
+# Validate CVV
+valid = CreditCardValidator.validate_cvv("123", "visa", brands)
+IO.puts(valid) # true
+```
+
+## Data Source
+
+Loads data from [`../../data/brands.json`](../../data/brands.json)
diff --git a/examples/elixir/credit_card_validator.ex b/examples/elixir/credit_card_validator.ex
new file mode 100644
index 0000000..75409c3
--- /dev/null
+++ b/examples/elixir/credit_card_validator.ex
@@ -0,0 +1,156 @@
+defmodule CreditCardValidator do
+ @moduledoc """
+ Credit Card BIN Validator - Elixir Example
+
+ This module shows how to use the bin-cc data file project in Elixir.
+ It loads the brands.json file and performs credit card validation.
+ """
+
+ @doc """
+ Load brand data from JSON file.
+
+ ## Parameters
+ - data_path: Path to brands.json (optional, uses default if nil)
+
+ ## Returns
+ List of brand maps
+ """
+ def load_brands(data_path \\ nil) do
+ path = data_path || Path.join([__DIR__, "../../data/brands.json"])
+
+ path
+ |> File.read!()
+ |> Jason.decode!()
+ end
+
+ @doc """
+ Identify the credit card brand.
+
+ ## Parameters
+ - card_number: Credit card number as string
+ - brands: List of brand data
+
+ ## Returns
+ Brand name (string) or nil if not found
+ """
+ def find_brand(card_number, brands) do
+ brands
+ |> Enum.find(fn brand ->
+ {:ok, regex} = Regex.compile(brand["regexpFull"])
+ Regex.match?(regex, card_number)
+ end)
+ |> case do
+ nil -> nil
+ brand -> brand["name"]
+ end
+ end
+
+ @doc """
+ Check if card number is supported.
+
+ ## Parameters
+ - card_number: Credit card number as string
+ - brands: List of brand data
+
+ ## Returns
+ true if supported, false otherwise
+ """
+ def is_supported?(card_number, brands) do
+ find_brand(card_number, brands) != nil
+ end
+
+ @doc """
+ Validate CVV for a specific brand.
+
+ ## Parameters
+ - cvv: CVV code as string
+ - brand_name: Brand name (e.g., "visa", "mastercard")
+ - brands: List of brand data
+
+ ## Returns
+ true if valid, false otherwise
+ """
+ def validate_cvv(cvv, brand_name, brands) do
+ case get_brand_info(brand_name, brands) do
+ nil -> false
+ brand ->
+ {:ok, regex} = Regex.compile(brand["regexpCvv"])
+ Regex.match?(regex, cvv)
+ end
+ end
+
+ @doc """
+ Get information about a specific brand.
+
+ ## Parameters
+ - brand_name: Brand name (e.g., "visa", "mastercard")
+ - brands: List of brand data
+
+ ## Returns
+ Brand map or nil if not found
+ """
+ def get_brand_info(brand_name, brands) do
+ Enum.find(brands, fn brand -> brand["name"] == brand_name end)
+ end
+
+ @doc """
+ List all supported brands.
+
+ ## Parameters
+ - brands: List of brand data
+
+ ## Returns
+ List of brand names
+ """
+ def list_brands(brands) do
+ Enum.map(brands, fn brand -> brand["name"] end)
+ end
+end
+
+# Example usage
+defmodule Example do
+ def run do
+ IO.puts("=== Credit Card Validator - Elixir Example ===\n")
+
+ brands = CreditCardValidator.load_brands()
+
+ # Example 1: List all brands
+ brand_names = CreditCardValidator.list_brands(brands)
+ IO.puts("Supported brands: #{Enum.join(brand_names, ", ")}\n")
+
+ # Example 2: Identify card brands
+ test_cards = %{
+ "4012001037141112" => "visa",
+ "5533798818319497" => "mastercard",
+ "378282246310005" => "amex",
+ "6011236044609927" => "discover"
+ }
+
+ IO.puts("Card brand identification:")
+ Enum.each(test_cards, fn {card, expected} ->
+ brand = CreditCardValidator.find_brand(card, brands)
+ status = if brand, do: "✓", else: "✗"
+ IO.puts("#{status} #{card}: #{brand} (expected: #{expected})")
+ end)
+ IO.puts("")
+
+ # Example 3: CVV validation
+ IO.puts("CVV validation:")
+ IO.puts("Visa CVV 123: #{CreditCardValidator.validate_cvv("123", "visa", brands)}")
+ IO.puts("Amex CVV 1234: #{CreditCardValidator.validate_cvv("1234", "amex", brands)}")
+ IO.puts("Visa CVV 12: #{CreditCardValidator.validate_cvv("12", "visa", brands)} (invalid)\n")
+
+ # Example 4: Get brand details
+ IO.puts("Visa brand details:")
+ case CreditCardValidator.get_brand_info("visa", brands) do
+ nil -> IO.puts(" Not found")
+ visa_info ->
+ IO.puts(" BIN pattern: #{visa_info["regexpBin"]}")
+ IO.puts(" Full pattern: #{visa_info["regexpFull"]}")
+ IO.puts(" CVV pattern: #{visa_info["regexpCvv"]}")
+ end
+ end
+end
+
+# Run the example if this file is executed directly
+# Example.run()
diff --git a/examples/javascript-example.js b/examples/javascript-example.js
new file mode 100644
index 0000000..991fe06
--- /dev/null
+++ b/examples/javascript-example.js
@@ -0,0 +1,50 @@
+'use strict';
+
+/**
+ * Example: Using bin-cc as a data source
+ *
+ * This example shows how other libraries can consume the credit card
+ * BIN data directly from this package, similar to how tzdata is used
+ * by date/time libraries.
+ */
+
+const creditcard = require('../libs/javascript/index.js');
+
+console.log('=== Using bin-cc as a Data File Project ===\n');
+
+// Method 1: Access data through the module exports
+console.log('Method 1: Via module exports');
+console.log('Available brands:', creditcard.data.brands.map(b => b.name).join(', '));
+console.log('');
+
+// Method 2: Use the data in your own validation logic
+console.log('Method 2: Custom validation using the data');
+function customValidate(cardNumber, brandName) {
+ const brand = creditcard.data.brands.find(b => b.name === brandName);
+ if (!brand) {
+ return false;
+ }
+ const regexp = new RegExp(brand.regexpFull);
+ return regexp.test(cardNumber);
+}
+
+console.log('Is 4012001037141112 a valid Visa?', customValidate('4012001037141112', 'visa'));
+console.log('Is 5533798818319497 a valid Mastercard?', customValidate('5533798818319497', 'mastercard'));
+console.log('');
+
+// Method 3: Extract specific brand information
+console.log('Method 3: Extract specific brand patterns');
+const visaData = creditcard.data.brands.find(b => b.name === 'visa');
+if (visaData) {
+ console.log('Visa BIN pattern:', visaData.regexpBin);
+ console.log('Visa full validation pattern:', visaData.regexpFull);
+ console.log('Visa CVV pattern:', visaData.regexpCvv);
+} else {
+ console.log('Visa brand not found');
+}
+console.log('');
+
+// Method 4: Use the built-in functions (still available)
+console.log('Method 4: Using built-in functions');
+console.log('Brand of 4012001037141112:', creditcard.findBrand('4012001037141112'));
+console.log('Is 378282246310005 supported?', creditcard.isSupported('378282246310005'));
diff --git a/examples/python/README.md b/examples/python/README.md
new file mode 100644
index 0000000..0b9e613
--- /dev/null
+++ b/examples/python/README.md
@@ -0,0 +1,46 @@
+# Credit Card Validator - Python Example
+
+Python implementation showing how to use the bin-cc data file project.
+
+## Usage
+
+```bash
+python3 credit_card_validator.py
+```
+
+## Requirements
+
+- Python 3.6+
+- No external dependencies (uses only standard library)
+
+## Features
+
+- Load brand data from JSON
+- Identify credit card brand
+- Validate CVV codes
+- Check if card is supported
+- Get brand information
+
+## Example
+
+```python
+from credit_card_validator import CreditCardValidator
+
+validator = CreditCardValidator()
+
+# Identify brand
+brand = validator.find_brand('4012001037141112')
+print(brand) # 'visa'
+
+# Check if supported
+supported = validator.is_supported('4012001037141112')
+print(supported) # True
+
+# Validate CVV
+valid = validator.validate_cvv('123', 'visa')
+print(valid) # True
+```
+
+## Data Source
+
+Loads data from [`../../data/brands.json`](../../data/brands.json)
diff --git a/examples/python/credit_card_validator.py b/examples/python/credit_card_validator.py
new file mode 100644
index 0000000..629ecc5
--- /dev/null
+++ b/examples/python/credit_card_validator.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+"""
+Credit Card BIN Validator - Python Example
+
+This example shows how to use the bin-cc data file project in Python.
+It loads the brands.json file and performs credit card validation.
+"""
+
+import json
+import re
+import os
+
+
+class CreditCardValidator:
+ """Credit card validator using bin-cc data."""
+
+ def __init__(self, data_path=None):
+ """
+ Initialize validator with brand data.
+
+ Args:
+ data_path: Path to brands.json. If None, uses default location.
+ """
+ if data_path is None:
+ # Default path relative to this file
+ current_dir = os.path.dirname(__file__)
+ data_path = os.path.join(current_dir, '../../data/brands.json')
+
+ with open(data_path, 'r') as f:
+ self.brands = json.load(f)
+
+ def find_brand(self, card_number):
+ """
+ Identify the credit card brand.
+
+ Args:
+ card_number: Credit card number as string
+
+ Returns:
+ Brand name (str) or None if not found
+ """
+ if not card_number:
+ return None
+
+ for brand in self.brands:
+ pattern = brand['regexpFull']
+ if re.match(pattern, card_number):
+ return brand['name']
+
+ return None
+
+ def is_supported(self, card_number):
+ """
+ Check if card number is supported.
+
+ Args:
+ card_number: Credit card number as string
+
+ Returns:
+ True if supported, False otherwise
+ """
+ return self.find_brand(card_number) is not None
+
+ def validate_cvv(self, cvv, brand_name):
+ """
+ Validate CVV for a specific brand.
+
+ Args:
+ cvv: CVV code as string
+ brand_name: Brand name (e.g., 'visa', 'mastercard')
+
+ Returns:
+ True if valid, False otherwise
+ """
+ brand = next((b for b in self.brands if b['name'] == brand_name), None)
+ if not brand:
+ return False
+
+ pattern = brand['regexpCvv']
+ return re.match(pattern, cvv) is not None
+
+ def get_brand_info(self, brand_name):
+ """
+ Get information about a specific brand.
+
+ Args:
+ brand_name: Brand name (e.g., 'visa', 'mastercard')
+
+ Returns:
+ Brand dictionary or None if not found
+ """
+ return next((b for b in self.brands if b['name'] == brand_name), None)
+
+ def list_brands(self):
+ """
+ List all supported brands.
+
+ Returns:
+ List of brand names
+ """
+ return [brand['name'] for brand in self.brands]
+
+
+def main():
+ """Example usage of the credit card validator."""
+
+ print('=== Credit Card Validator - Python Example ===\n')
+
+ validator = CreditCardValidator()
+
+ # Example 1: List all brands
+ print('Supported brands:', ', '.join(validator.list_brands()))
+ print()
+
+ # Example 2: Identify card brands
+ test_cards = {
+ '4012001037141112': 'Visa',
+ '5533798818319497': 'Mastercard',
+ '378282246310005': 'Amex',
+ '6011236044609927': 'Discover'
+ }
+
+ print('Card brand identification:')
+ for card, expected in test_cards.items():
+ brand = validator.find_brand(card)
+ status = '✓' if brand else '✗'
+ print(f'{status} {card}: {brand} (expected: {expected.lower()})')
+ print()
+
+ # Example 3: CVV validation
+ print('CVV validation:')
+ print(f'Visa CVV 123: {validator.validate_cvv("123", "visa")}')
+ print(f'Amex CVV 1234: {validator.validate_cvv("1234", "amex")}')
+ print(f'Visa CVV 12: {validator.validate_cvv("12", "visa")} (invalid)')
+ print()
+
+ # Example 4: Get brand details
+ print('Visa brand details:')
+ visa_info = validator.get_brand_info('visa')
+ if visa_info:
+ print(f' BIN pattern: {visa_info["regexpBin"]}')
+ print(f' Full pattern: {visa_info["regexpFull"]}')
+ print(f' CVV pattern: {visa_info["regexpCvv"]}')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/ruby/README.md b/examples/ruby/README.md
new file mode 100644
index 0000000..6bf7d12
--- /dev/null
+++ b/examples/ruby/README.md
@@ -0,0 +1,46 @@
+# Credit Card Validator - Ruby Example
+
+Ruby implementation showing how to use the bin-cc data file project.
+
+## Usage
+
+```bash
+ruby credit_card_validator.rb
+```
+
+## Requirements
+
+- Ruby 2.5+
+- No external gems (uses only standard library)
+
+## Features
+
+- Load brand data from JSON
+- Identify credit card brand
+- Validate CVV codes
+- Check if card is supported
+- Get brand information
+
+## Example
+
+```ruby
+require_relative 'credit_card_validator'
+
+validator = CreditCardValidator.new
+
+# Identify brand
+brand = validator.find_brand('4012001037141112')
+puts brand # 'visa'
+
+# Check if supported
+supported = validator.supported?('4012001037141112')
+puts supported # true
+
+# Validate CVV
+valid = validator.validate_cvv('123', 'visa')
+puts valid # true
+```
+
+## Data Source
+
+Loads data from [`../../data/brands.json`](../../data/brands.json)
diff --git a/examples/ruby/credit_card_validator.rb b/examples/ruby/credit_card_validator.rb
new file mode 100644
index 0000000..9a54b5b
--- /dev/null
+++ b/examples/ruby/credit_card_validator.rb
@@ -0,0 +1,128 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require 'json'
+
+##
+# Credit Card BIN Validator - Ruby Example
+#
+# This class shows how to use the bin-cc data file project in Ruby.
+# It loads the brands.json file and performs credit card validation.
+class CreditCardValidator
+ attr_reader :brands
+
+ ##
+ # Initialize validator with brand data
+ #
+ # @param data_path [String, nil] Path to brands.json. If nil, uses default location.
+ def initialize(data_path = nil)
+ data_path ||= File.join(__dir__, '../../data/brands.json')
+ file_content = File.read(data_path)
+ @brands = JSON.parse(file_content)
+ end
+
+ ##
+ # Identify the credit card brand
+ #
+ # @param card_number [String] Credit card number
+ # @return [String, nil] Brand name or nil if not found
+ def find_brand(card_number)
+ return nil if card_number.nil? || card_number.empty?
+
+ brand = @brands.find do |b|
+ pattern = Regexp.new(b['regexpFull'])
+ pattern.match?(card_number)
+ end
+
+ brand ? brand['name'] : nil
+ end
+
+ ##
+ # Check if card number is supported
+ #
+ # @param card_number [String] Credit card number
+ # @return [Boolean] true if supported, false otherwise
+ def supported?(card_number)
+ !find_brand(card_number).nil?
+ end
+
+ ##
+ # Validate CVV for a specific brand
+ #
+ # @param cvv [String] CVV code
+ # @param brand_name [String] Brand name (e.g., 'visa', 'mastercard')
+ # @return [Boolean] true if valid, false otherwise
+ def validate_cvv(cvv, brand_name)
+ brand = get_brand_info(brand_name)
+ return false if brand.nil?
+
+ pattern = Regexp.new(brand['regexpCvv'])
+ pattern.match?(cvv)
+ end
+
+ ##
+ # Get information about a specific brand
+ #
+ # @param brand_name [String] Brand name (e.g., 'visa', 'mastercard')
+ # @return [Hash, nil] Brand hash or nil if not found
+ def get_brand_info(brand_name)
+ @brands.find { |b| b['name'] == brand_name }
+ end
+
+ ##
+ # List all supported brands
+ #
+ # @return [Array] List of brand names
+ def list_brands
+ @brands.map { |b| b['name'] }
+ end
+end
+
+# Example usage
+def main
+ puts '=== Credit Card Validator - Ruby Example ==='
+ puts
+
+ validator = CreditCardValidator.new
+
+ # Example 1: List all brands
+ puts "Supported brands: #{validator.list_brands.join(', ')}"
+ puts
+
+ # Example 2: Identify card brands
+ test_cards = {
+ '4012001037141112' => 'visa',
+ '5533798818319497' => 'mastercard',
+ '378282246310005' => 'amex',
+ '6011236044609927' => 'discover'
+ }
+
+ puts 'Card brand identification:'
+ test_cards.each do |card, expected|
+ brand = validator.find_brand(card)
+ status = brand ? '✓' : '✗'
+ puts "#{status} #{card}: #{brand} (expected: #{expected})"
+ end
+ puts
+
+ # Example 3: CVV validation
+ puts 'CVV validation:'
+ puts "Visa CVV 123: #{validator.validate_cvv('123', 'visa')}"
+ puts "Amex CVV 1234: #{validator.validate_cvv('1234', 'amex')}"
+ puts "Visa CVV 12: #{validator.validate_cvv('12', 'visa')} (invalid)"
+ puts
+
+ # Example 4: Get brand details
+ puts 'Visa brand details:'
+ visa_info = validator.get_brand_info('visa')
+ if visa_info
+ puts " BIN pattern: #{visa_info['regexpBin']}"
+ puts " Full pattern: #{visa_info['regexpFull']}"
+ puts " CVV pattern: #{visa_info['regexpCvv']}"
+ else
+ puts ' Not found'
+ end
+end
+
+# Run the example if this file is executed directly
+main if __FILE__ == $PROGRAM_NAME
diff --git a/libs/javascript/.gitignore b/libs/javascript/.gitignore
new file mode 100644
index 0000000..8fce603
--- /dev/null
+++ b/libs/javascript/.gitignore
@@ -0,0 +1 @@
+data/
diff --git a/libs/javascript/README.md b/libs/javascript/README.md
new file mode 100644
index 0000000..5c3ae90
--- /dev/null
+++ b/libs/javascript/README.md
@@ -0,0 +1,142 @@
+# Credit Card Identifier - JavaScript Implementation
+
+JavaScript/Node.js implementation for credit card BIN validation. This library automatically downloads the latest BIN data from [GitHub releases](https://github.com/renatovico/bin-cc/releases).
+
+## Installation
+
+```bash
+npm install creditcard-identifier
+```
+
+The postinstall script will automatically download the latest credit card BIN data from GitHub releases.
+
+## Usage
+
+### Basic Validation
+
+```javascript
+const creditcard = require('creditcard-identifier');
+
+// Identify card brand
+const brand = creditcard.findBrand('4012001037141112');
+console.log(brand); // 'visa'
+
+// Check if card is supported
+const supported = creditcard.isSupported('4012001037141112');
+console.log(supported); // true
+
+// Get Hipercard regex
+const hipercardRegex = creditcard.hipercardRegexp();
+```
+
+### Using Raw Data
+
+```javascript
+const creditcard = require('creditcard-identifier');
+
+// Access brand data directly
+const brands = creditcard.data.brands;
+console.log(brands);
+// [
+// { name: 'elo', regexpBin: '...', regexpFull: '...', regexpCvv: '...' },
+// { name: 'diners', regexpBin: '...', regexpFull: '...', regexpCvv: '...' },
+// ...
+// ]
+
+// Use data in custom logic
+brands.forEach(brand => {
+ console.log(`${brand.name}: ${brand.regexpBin}`);
+});
+```
+
+### Direct JSON Access
+
+```javascript
+const fs = require('fs');
+const path = require('path');
+
+// The data is downloaded to the package's data directory
+const dataPath = path.join(__dirname, 'node_modules', 'creditcard-identifier', 'data', 'brands.json');
+const brands = JSON.parse(fs.readFileSync(dataPath, 'utf8'));
+```
+
+## Data Updates
+
+The library downloads data during installation. To manually update to the latest data:
+
+```bash
+npm run update-data
+```
+
+Or programmatically:
+
+```javascript
+const { downloadData } = require('creditcard-identifier/download-data');
+
+downloadData()
+ .then(() => console.log('Data updated'))
+ .catch(err => console.error('Update failed:', err));
+```
+
+## API
+
+### `findBrand(cardNumber)`
+Returns the brand name for the given card number.
+
+**Parameters:**
+- `cardNumber` (string): The credit card number
+
+**Returns:** (string) Brand name (e.g., 'visa', 'mastercard')
+
+**Throws:** Error if card number is not supported
+
+### `isSupported(cardNumber)`
+Checks if the card number is supported.
+
+**Parameters:**
+- `cardNumber` (string): The credit card number
+
+**Returns:** (boolean) true if supported, false otherwise
+
+### `hipercardRegexp()`
+Returns the regular expression for Hipercard validation.
+
+**Returns:** (RegExp) Hipercard validation pattern
+
+### `brands`
+Direct access to the brand data array.
+
+### `data.brands`
+Alternative access to the brand data array.
+
+## Development
+
+### Install Dependencies
+
+```bash
+npm install
+```
+
+### Run Tests
+
+```bash
+npm run test-unit
+```
+
+### Run Coverage
+
+```bash
+npm run coverage
+```
+
+## Data Source
+
+This implementation automatically downloads the latest BIN data from [GitHub releases](https://github.com/renatovico/bin-cc/releases?q=data-v) during installation.
+
+The data is maintained separately from the library, allowing for independent updates:
+- **Library updates**: Published to npm with version tags (e.g., `v1.2.0`)
+- **Data updates**: Released on GitHub with data-v tags (e.g., `data-v2.0.1`)
+
+## License
+
+MIT
diff --git a/libs/javascript/creditcard-identifier.js b/libs/javascript/creditcard-identifier.js
new file mode 100644
index 0000000..3111558
--- /dev/null
+++ b/libs/javascript/creditcard-identifier.js
@@ -0,0 +1,78 @@
+'use strict';
+
+const path = require('path');
+const fs = require('fs');
+
+// Try to load data from downloaded file first, fall back to bundled data
+let brands;
+const downloadedDataPath = path.join(__dirname, 'data', 'brands.json');
+const bundledDataPath = path.join(__dirname, '../../data/brands.json');
+
+if (fs.existsSync(downloadedDataPath)) {
+ // Load from downloaded data
+ brands = require(downloadedDataPath);
+} else if (fs.existsSync(bundledDataPath)) {
+ // Fall back to bundled data (for development)
+ brands = require(bundledDataPath);
+} else {
+ throw new Error(
+ 'Credit card data not found. Please run: npm run postinstall or download-data.js'
+ );
+}
+
+function cardNumberFilter(cardNumber, brand) {
+ if (typeof cardNumber !== 'string') {
+ throw Error('Card number should be a string');
+ }
+
+ return cardNumber.match(brand.regexpFull) !== null;
+}
+
+function cardNameFilter(brandName, brand) {
+ return brandName === brand.name;
+}
+
+function hipercardRegexp() {
+ let card = brands.filter(cardNameFilter.bind(this,'hipercard'))[0];
+ if (card) {
+ return new RegExp(card.regexpFull);
+ } else {
+ return new RegExp('^$');
+ }
+}
+
+function findBrand(cardNumber) {
+ if(!cardNumber || cardNumber === '') {
+ cardNumber = '000000';
+ }
+ let brand = brands.filter(cardNumberFilter.bind(this, cardNumber))[0];
+
+ if (brand === undefined) {
+ throw Error('card number not supported');
+ }
+
+ brand = (brand === undefined) ? undefined : brand.name;
+
+ return brand;
+}
+
+function isSupported(cardNumber) {
+ let number = cardNumber || '0000000000000001';
+
+ let supported = false;
+ let result = brands.filter(cardNumberFilter.bind(this, number))[0];
+ if (result !== undefined) {
+ supported = true;
+ }
+
+ return supported;
+}
+
+module.exports = {
+ findBrand: findBrand,
+ isSupported: isSupported,
+ hipercardRegexp: hipercardRegexp,
+ brands: brands
+}
+
+
diff --git a/libs/javascript/download-data.js b/libs/javascript/download-data.js
new file mode 100644
index 0000000..a3b63e9
--- /dev/null
+++ b/libs/javascript/download-data.js
@@ -0,0 +1,139 @@
+#!/usr/bin/env node
+'use strict';
+
+const https = require('https');
+const fs = require('fs');
+const path = require('path');
+
+const DATA_DIR = path.join(__dirname, 'data');
+const DATA_FILE = path.join(DATA_DIR, 'brands.json');
+const GITHUB_API_URL = 'https://api.github.com/repos/renatovico/bin-cc/releases';
+
+/**
+ * Download data from GitHub releases
+ */
+function downloadData() {
+ console.log('📥 Downloading latest credit card BIN data from GitHub releases...');
+
+ return new Promise((resolve, reject) => {
+ // First, get the latest data release
+ https.get(GITHUB_API_URL, {
+ headers: {
+ 'User-Agent': 'creditcard-identifier'
+ }
+ }, (res) => {
+ let data = '';
+
+ res.on('data', (chunk) => {
+ data += chunk;
+ });
+
+ res.on('end', () => {
+ try {
+ const releases = JSON.parse(data);
+
+ // Find the latest data release (tag starts with 'data-v')
+ const dataRelease = releases.find(r => r.tag_name.startsWith('data-v'));
+
+ if (!dataRelease) {
+ reject(new Error('No data releases found'));
+ return;
+ }
+
+ console.log(` Found data release: ${dataRelease.tag_name}`);
+
+ // Find the brands.json asset
+ const asset = dataRelease.assets.find(a => a.name === 'brands.json');
+
+ if (!asset) {
+ reject(new Error('brands.json not found in release assets'));
+ return;
+ }
+
+ // Download the asset
+ downloadFile(asset.browser_download_url, DATA_FILE)
+ .then(() => {
+ console.log(`✅ Data downloaded successfully to ${DATA_FILE}`);
+ resolve();
+ })
+ .catch(reject);
+
+ } catch (err) {
+ reject(err);
+ }
+ });
+ }).on('error', reject);
+ });
+}
+
+/**
+ * Download a file from URL
+ */
+function downloadFile(url, dest) {
+ return new Promise((resolve, reject) => {
+ // Ensure data directory exists
+ if (!fs.existsSync(DATA_DIR)) {
+ fs.mkdirSync(DATA_DIR, { recursive: true });
+ }
+
+ const file = fs.createWriteStream(dest);
+
+ https.get(url, {
+ headers: {
+ 'User-Agent': 'creditcard-identifier',
+ 'Accept': 'application/octet-stream'
+ }
+ }, (response) => {
+ // Handle redirects
+ if (response.statusCode === 302 || response.statusCode === 301) {
+ downloadFile(response.headers.location, dest).then(resolve).catch(reject);
+ return;
+ }
+
+ response.pipe(file);
+
+ file.on('finish', () => {
+ file.close(resolve);
+ });
+ }).on('error', (err) => {
+ fs.unlink(dest, () => {}); // Delete the file on error
+ reject(err);
+ });
+
+ file.on('error', (err) => {
+ fs.unlink(dest, () => {}); // Delete the file on error
+ reject(err);
+ });
+ });
+}
+
+/**
+ * Check if data needs to be downloaded
+ */
+function checkDataExists() {
+ return fs.existsSync(DATA_FILE);
+}
+
+// Run if executed directly
+if (require.main === module) {
+ if (!checkDataExists()) {
+ downloadData()
+ .then(() => {
+ console.log('✨ Setup complete!');
+ process.exit(0);
+ })
+ .catch((err) => {
+ console.error('❌ Failed to download data:', err.message);
+ process.exit(1);
+ });
+ } else {
+ console.log('✓ Data already exists');
+ process.exit(0);
+ }
+}
+
+module.exports = {
+ downloadData,
+ checkDataExists,
+ DATA_FILE
+};
diff --git a/libs/javascript/index.js b/libs/javascript/index.js
new file mode 100644
index 0000000..3a648cf
--- /dev/null
+++ b/libs/javascript/index.js
@@ -0,0 +1,11 @@
+'use strict';
+
+const creditcardIdentifier = require('./creditcard-identifier.js');
+
+// Export everything from the main module
+module.exports = creditcardIdentifier;
+
+// Also provide direct access to data for other libraries
+module.exports.data = {
+ brands: creditcardIdentifier.brands
+};
diff --git a/package-lock.json b/libs/javascript/package-lock.json
similarity index 68%
rename from package-lock.json
rename to libs/javascript/package-lock.json
index c5a01dd..05d3e92 100644
--- a/package-lock.json
+++ b/libs/javascript/package-lock.json
@@ -1,188 +1,236 @@
{
"name": "creditcard-identifier",
"version": "1.1.2",
- "lockfileVersion": 1,
+ "lockfileVersion": 3,
"requires": true,
- "dependencies": {
- "assertion-error": {
+ "packages": {
+ "": {
+ "name": "creditcard-identifier",
+ "version": "1.1.2",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "devDependencies": {
+ "chai": "^3.5.0",
+ "lodash": "^4.5.0",
+ "mocha": "^5.2.0"
+ }
+ },
+ "node_modules/assertion-error": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
},
- "balanced-match": {
+ "node_modules/balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
- "brace-expansion": {
+ "node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "requires": {
+ "dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
- "browser-stdout": {
+ "node_modules/browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
- "chai": {
+ "node_modules/chai": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
"integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
"dev": true,
- "requires": {
+ "dependencies": {
"assertion-error": "^1.0.1",
"deep-eql": "^0.1.3",
"type-detect": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
}
},
- "commander": {
+ "node_modules/commander": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
"dev": true
},
- "concat-map": {
+ "node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
- "debug": {
+ "node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
- "requires": {
+ "dependencies": {
"ms": "2.0.0"
}
},
- "deep-eql": {
+ "node_modules/deep-eql": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
"integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
"dev": true,
- "requires": {
+ "dependencies": {
"type-detect": "0.1.1"
},
- "dependencies": {
- "type-detect": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
- "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
- "dev": true
- }
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/deep-eql/node_modules/type-detect": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+ "dev": true,
+ "engines": {
+ "node": "*"
}
},
- "diff": {
+ "node_modules/diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
},
- "escape-string-regexp": {
+ "node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
},
- "fs.realpath": {
+ "node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
- "glob": {
+ "node_modules/glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
- "requires": {
+ "dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
}
},
- "growl": {
+ "node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=4.x"
+ }
},
- "has-flag": {
+ "node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
},
- "he": {
+ "node_modules/he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
- "dev": true
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
},
- "inflight": {
+ "node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
- "requires": {
+ "dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
- "inherits": {
+ "node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
- "lodash": {
+ "node_modules/lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
- "minimatch": {
+ "node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
- "requires": {
+ "dependencies": {
"brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
}
},
- "minimist": {
+ "node_modules/minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
- "mkdirp": {
+ "node_modules/mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
"dev": true,
- "requires": {
+ "dependencies": {
"minimist": "0.0.8"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
}
},
- "mocha": {
+ "node_modules/mocha": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
"integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
"dev": true,
- "requires": {
+ "dependencies": {
"browser-stdout": "1.3.1",
"commander": "2.15.1",
"debug": "3.1.0",
@@ -194,45 +242,61 @@
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"supports-color": "5.4.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
}
},
- "ms": {
+ "node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
- "once": {
+ "node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
- "requires": {
+ "dependencies": {
"wrappy": "1"
}
},
- "path-is-absolute": {
+ "node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "supports-color": {
+ "node_modules/supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true,
- "requires": {
+ "dependencies": {
"has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
}
},
- "type-detect": {
+ "node_modules/type-detect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
"integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
},
- "wrappy": {
+ "node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
diff --git a/libs/javascript/package.json b/libs/javascript/package.json
new file mode 100644
index 0000000..d740efe
--- /dev/null
+++ b/libs/javascript/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "creditcard-identifier",
+ "version": "1.1.2",
+ "description": "Brazilian CreditCard Identifier - Automatically downloads latest BIN data from GitHub releases",
+ "main": "index.js",
+ "files": [
+ "index.js",
+ "creditcard-identifier.js",
+ "download-data.js",
+ "README.md"
+ ],
+ "scripts": {
+ "postinstall": "node download-data.js",
+ "test": "npm run test-unit",
+ "test-unit": "NODE_ENV=test ./node_modules/.bin/mocha ./test --recursive --harmony",
+ "coverage": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- ./test/ --recursive --harmony",
+ "update-data": "node download-data.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/renatoelias/bin-cc.git"
+ },
+ "keywords": [
+ "creditcard",
+ "bin",
+ "identifier",
+ "validation",
+ "bin",
+ "luhn",
+ "data",
+ "tzdata",
+ "credit-card-data"
+ ],
+ "author": "Renato Elias, Eriki Herinque, Carlos Rios, Lohan Bohdavean",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/renatoelias/bin-cc/issues"
+ },
+ "homepage": "https://github.com/renatoelias/bin-cc#readme",
+ "devDependencies": {
+ "chai": "^3.5.0",
+ "lodash": "^4.5.0",
+ "mocha": "^5.2.0"
+ }
+}
diff --git a/test/creditcard-identifier.test.js b/libs/javascript/test/creditcard-identifier.test.js
similarity index 100%
rename from test/creditcard-identifier.test.js
rename to libs/javascript/test/creditcard-identifier.test.js
diff --git a/package.json b/package.json
index da4f58a..2ab152b 100644
--- a/package.json
+++ b/package.json
@@ -1,33 +1,30 @@
{
- "name": "creditcard-identifier",
- "version": "1.1.2",
- "description": "Brazilian CreditCard Identifier",
- "main": "creditcard-identifier.js",
+ "name": "bin-cc",
+ "version": "2.0.0",
+ "description": "Credit Card BIN Data - Multi-language data file project",
+ "private": true,
"scripts": {
- "test-unit": "NODE_ENV=test ./node_modules/.bin/mocha ./test --recursive --harmony",
- "coverage": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- ./test/ --recursive --harmony"
+ "build": "node scripts/build.js",
+ "validate": "node scripts/build.js",
+ "test": "npm run build && cd libs/javascript && npm test"
},
"repository": {
"type": "git",
- "url": "git+https://github.com/renatoelias/bin-cc.git"
+ "url": "git+https://github.com/renatovico/bin-cc.git"
},
"keywords": [
"creditcard",
"bin",
- "identifier",
- "validation",
- "bin",
- "luhn"
+ "data",
+ "tzdata",
+ "browserslist",
+ "credit-card-data",
+ "validation"
],
"author": "Renato Elias, Eriki Herinque, Carlos Rios, Lohan Bohdavean",
"license": "MIT",
"bugs": {
- "url": "https://github.com/renatoelias/bin-cc/issues"
+ "url": "https://github.com/renatovico/bin-cc/issues"
},
- "homepage": "https://github.com/renatoelias/bin-cc#readme",
- "devDependencies": {
- "chai": "^3.5.0",
- "lodash": "^4.5.0",
- "mocha": "^5.2.0"
- }
+ "homepage": "https://github.com/renatovico/bin-cc#readme"
}
diff --git a/scripts/build.js b/scripts/build.js
new file mode 100755
index 0000000..1dccbd0
--- /dev/null
+++ b/scripts/build.js
@@ -0,0 +1,209 @@
+#!/usr/bin/env node
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+
+/**
+ * Build script for bin-cc data
+ *
+ * Reads source files from data/sources/ and compiles them into:
+ * 1. data/compiled/brands.json - Enhanced format with all details
+ * 2. data/brands.json - Legacy format for backward compatibility
+ */
+
+const SOURCES_DIR = path.join(__dirname, '../data/sources');
+const COMPILED_DIR = path.join(__dirname, '../data/compiled');
+const LEGACY_FILE = path.join(__dirname, '../data/brands.json');
+
+// Ensure compiled directory exists
+if (!fs.existsSync(COMPILED_DIR)) {
+ fs.mkdirSync(COMPILED_DIR, { recursive: true });
+}
+
+/**
+ * Build regex patterns from source patterns
+ *
+ * This builds comprehensive patterns for card validation.
+ * The BIN pattern matches the first digits, and full pattern validates entire card numbers.
+ */
+function buildPatterns(patterns) {
+ // Combine all BIN patterns
+ const binPatterns = patterns.map(p => p.bin).join('|');
+
+ // For full pattern, we need to match exact card lengths
+ // Strategy: For each pattern, match its BIN + exact remaining digits for each valid length
+ const fullPatterns = [];
+
+ for (const pattern of patterns) {
+ const lengths = Array.isArray(pattern.length) ? pattern.length : [pattern.length];
+
+ for (const len of lengths) {
+ // For the full pattern, we match: BIN pattern + rest of digits to reach total length
+ // Note: The BIN pattern itself may match variable digits (e.g., ^4 matches 1 digit, ^6367 matches 4)
+ // So we use a simplified approach: the pattern already includes anchors
+ const binPart = pattern.bin;
+
+ // Rough heuristic: most BIN patterns match 4-6 digits
+ // For simplicity, let's match (pattern) followed by remaining digits
+ // This won't be perfect for all cases but handles common scenarios
+
+ if (len === 13) {
+ fullPatterns.push(`${binPart}[0-9]{9,12}`);
+ } else if (len === 14) {
+ fullPatterns.push(`${binPart}[0-9]{10,13}`);
+ } else if (len === 15) {
+ fullPatterns.push(`${binPart}[0-9]{11,14}`);
+ } else if (len === 16) {
+ fullPatterns.push(`${binPart}[0-9]{12,15}`);
+ } else if (len === 19) {
+ fullPatterns.push(`${binPart}[0-9]{15,18}`);
+ }
+ }
+ }
+
+ // Deduplicate patterns
+ const uniqueFullPatterns = [...new Set(fullPatterns)];
+
+ return {
+ binPattern: binPatterns,
+ fullPattern: `(${uniqueFullPatterns.join('|')})`,
+ lengths: [...new Set(patterns.flatMap(p => Array.isArray(p.length) ? p.length : [p.length]))],
+ cvvLength: patterns[0].cvvLength
+ };
+}
+
+/**
+ * Read all source files and compile
+ */
+function buildData() {
+ console.log('🔨 Building bin-cc data...\n');
+
+ const sourceFiles = fs.readdirSync(SOURCES_DIR)
+ .filter(f => f.endsWith('.json'))
+ .sort();
+
+ const compiledBrands = [];
+ const legacyBrands = [];
+
+ for (const file of sourceFiles) {
+ const sourcePath = path.join(SOURCES_DIR, file);
+ const source = JSON.parse(fs.readFileSync(sourcePath, 'utf8'));
+
+ console.log(` ✓ Processing ${source.brand} (${source.scheme})`);
+
+ const patterns = buildPatterns(source.patterns);
+
+ // Enhanced format
+ const compiledBrand = {
+ scheme: source.scheme,
+ brand: source.brand,
+ type: source.type || 'credit',
+ number: {
+ lengths: patterns.lengths,
+ luhn: source.patterns[0].luhn
+ },
+ cvv: {
+ length: patterns.cvvLength
+ },
+ patterns: {
+ bin: patterns.binPattern,
+ full: patterns.fullPattern
+ },
+ countries: source.countries || [],
+ metadata: {
+ sourceFile: file
+ }
+ };
+
+ // Add optional BIN-level details if present
+ if (source.bins && Array.isArray(source.bins) && source.bins.length > 0) {
+ compiledBrand.bins = source.bins.map(binData => ({
+ bin: binData.bin,
+ type: binData.type,
+ category: binData.category || null,
+ issuer: binData.issuer || null
+ }));
+ }
+
+ compiledBrands.push(compiledBrand);
+
+ // Legacy format (backward compatible)
+ const legacyBrand = {
+ name: source.scheme,
+ regexpBin: patterns.binPattern,
+ regexpFull: patterns.fullPattern,
+ regexpCvv: `^\\d{${patterns.cvvLength}}$`
+ };
+
+ legacyBrands.push(legacyBrand);
+ }
+
+ // Write compiled format
+ const compiledPath = path.join(COMPILED_DIR, 'brands.json');
+ fs.writeFileSync(compiledPath, JSON.stringify(compiledBrands, null, 2));
+ console.log(`\n✅ Compiled data written to: ${path.relative(process.cwd(), compiledPath)}`);
+
+ // Note: Legacy data/brands.json is maintained manually for backward compatibility
+ // The source files provide a structured, extensible format for future enhancements
+ console.log(`ℹ️ Legacy data/brands.json maintained separately for backward compatibility`);
+
+ // Generate statistics
+ console.log(`\n📊 Statistics:`);
+ console.log(` Total brands: ${compiledBrands.length}`);
+ console.log(` Global brands: ${compiledBrands.filter(b => b.countries.includes('GLOBAL')).length}`);
+ console.log(` Brazilian brands: ${compiledBrands.filter(b => b.countries.includes('BR')).length}`);
+
+ return { compiledBrands, legacyBrands };
+}
+
+/**
+ * Validate the built data
+ */
+function validate(data) {
+ console.log('\n🔍 Validating data...\n');
+
+ let errors = 0;
+
+ for (const brand of data.compiledBrands) {
+ // Check required fields
+ const required = ['scheme', 'brand', 'type', 'number', 'cvv', 'patterns'];
+ for (const field of required) {
+ if (!brand[field]) {
+ console.error(` ✗ ${brand.scheme}: Missing required field '${field}'`);
+ errors++;
+ }
+ }
+
+ // Validate patterns are valid regex
+ try {
+ new RegExp(brand.patterns.bin);
+ new RegExp(brand.patterns.full);
+ } catch (e) {
+ console.error(` ✗ ${brand.scheme}: Invalid regex pattern - ${e.message}`);
+ errors++;
+ }
+ }
+
+ if (errors === 0) {
+ console.log(' ✓ All validations passed!');
+ } else {
+ console.error(`\n❌ ${errors} validation error(s) found`);
+ process.exit(1);
+ }
+}
+
+// Run build
+if (require.main === module) {
+ try {
+ const data = buildData();
+ validate(data);
+ console.log('\n✨ Build completed successfully!\n');
+ } catch (error) {
+ console.error('\n❌ Build failed:', error.message);
+ console.error(error.stack);
+ process.exit(1);
+ }
+}
+
+module.exports = { buildData, validate };