gstin-tools
v1.0.0
Published
A tiny, dependency-free TypeScript library to validate and parse Indian GSTINs and work with related artefacts
Downloads
4
Maintainers
Readme
gstin-tools
A tiny, dependency-free TypeScript library to validate and parse Indian GSTINs and work with related artefacts (PAN extraction, state code lookup, check-digit computation, masking). Includes a robust implementation of the GSTIN check digit (Luhn mod-36 variant) and complete state/UT code tables (including 97/99).
Features
- ✅ Dependency-free: No external dependencies
- ✅ TypeScript-first: Written in TypeScript with full type definitions
- ✅ Tiny bundle size: < 5KB minified + gzipped
- ✅ Tree-shakeable: Individual function exports
- ✅ ESM + CJS: Supports both module systems
- ✅ 100% test coverage: Thoroughly tested
- ✅ Correct checksum: Proper Luhn mod-36 implementation
- ✅ Complete state codes: All Indian states/UTs including special codes (97, 99)
- ✅ PAN validation: Format validation and type extraction
- ✅ Safe masking: Utilities for logging/PII protection
Installation
npm install gstin-toolsyarn add gstin-toolspnpm add gstin-toolsQuick Start
import { validateGSTIN, parseGSTIN, computeCheckDigit, fromComponents } from 'gstin-tools';
// Quick validation
console.log(validateGSTIN('27AABCU9603R1ZN').ok); // true
// Parse GSTIN components
const parsed = parseGSTIN('27AABCU9603R1ZN');
console.log(parsed);
// {
// raw: '27AABCU9603R1ZN',
// stateCode: '27',
// stateName: 'Maharashtra',
// pan: 'AABCU9603R',
// panType: 'C',
// entity: '1',
// defaultZ: 'Z',
// checkDigit: 'N'
// }
// Compute checksum
console.log(computeCheckDigit('27AABCU9603R1Z')); // 'N'
// Generate GSTIN from components
console.log(fromComponents('27', 'AABCU9603R', '1')); // '27AABCU9603R1ZN'API Reference
Validation Functions
validateGSTIN(input: string, options?: ValidateOptions): ValidateResult
Validates a GSTIN string and returns detailed validation results.
const result = validateGSTIN('27AABCU9603R1ZN');
console.log(result.ok); // true
console.log(result.parsed); // ParsedGSTIN object
console.log(result.issues); // Array of validation issues (if any)Options:
allowSpecialStates?: boolean- Accept 97 (Other Territory) and 99 (Centre Jurisdiction). Default:truecollectAllIssues?: boolean- Return all issues instead of failing fast. Default:truenormalizeInput?: boolean- Uppercase and trim input. Default:true
isValidGSTIN(input: string): boolean
Quick boolean validation (fast-fail, minimal allocation).
console.log(isValidGSTIN('27AABCU9603R1ZN')); // true
console.log(isValidGSTIN('invalid')); // falseparseGSTIN(input: string, safe?: boolean): ParsedGSTIN
Parses a GSTIN into its components. Throws on invalid input unless safe is true.
const parsed = parseGSTIN('27AABCU9603R1ZN');
// Returns ParsedGSTIN object
// Safe parsing (returns detailed error)
try {
const parsed = parseGSTIN('invalid-gstin', true);
} catch (error) {
console.log(error.message); // Detailed validation error
}Checksum Functions
computeCheckDigit(first14: string): string
Computes the correct check digit for the first 14 characters of a GSTIN using Luhn mod-36.
console.log(computeCheckDigit('27AABCU9603R1Z')); // 'N'verifyCheckDigit(gstin: string): boolean
Verifies if a 15-character GSTIN has the correct checksum.
console.log(verifyCheckDigit('27AABCU9603R1ZN')); // true
console.log(verifyCheckDigit('27AABCU9603R1ZX')); // falseGeneration Functions
fromComponents(stateCode: string, pan: string, entity: string, defaultZ?: 'Z'): string
Generates a valid GSTIN from components, automatically computing the check digit.
const gstin = fromComponents('27', 'AABCU9603R', '1');
console.log(gstin); // '27AABCU9603R1ZN'State Code Functions
gstStateName(code: string): string | undefined
Returns the state/UT name for a given code.
console.log(gstStateName('27')); // 'Maharashtra'
console.log(gstStateName('97')); // 'Other Territory'
console.log(gstStateName('99')); // 'Centre Jurisdiction'isValidStateCode(code: string, options?: { allowSpecial?: boolean }): boolean
Validates a state/UT code.
console.log(isValidStateCode('27')); // true
console.log(isValidStateCode('97')); // true
console.log(isValidStateCode('25')); // false (deprecated)
console.log(isValidStateCode('97', { allowSpecial: false })); // falsePAN Functions
validatePAN(pan: string): PanValidationResult
Validates PAN format and extracts the type.
const result = validatePAN('AABCU9603R');
console.log(result.ok); // true
console.log(result.type); // 'C' (Company)extractPAN(gstin: string): string | undefined
Extracts the PAN from a GSTIN.
console.log(extractPAN('27AABCU9603R1ZN')); // 'AABCU9603R'Helper Functions
maskGSTIN(gstin: string, mode?: 'show-last-3' | 'show-pan-only'): string
Masks GSTIN for safe logging/display.
console.log(maskGSTIN('27AABCU9603R1ZN')); // '************1ZN'
console.log(maskGSTIN('27AABCU9603R1ZN', 'show-pan-only')); // '**AABCU9603R***'normalizeGSTIN(input: string): string
Normalizes GSTIN input (trim, uppercase, remove invisible chars).
console.log(normalizeGSTIN(' 27aabcu9603r1zn ')); // '27AABCU9603R1ZN'Data Tables
The library includes complete data tables for:
State/UT Codes
- All 38 Indian states and Union Territories
- Special codes: 97 (Other Territory), 99 (Centre Jurisdiction)
- Excludes deprecated code 25 (Daman & Diu, merged into 26)
PAN Types
- P: Individual
- C: Company
- H: HUF (Hindu Undivided Family)
- F: Firm
- A: AOP (Association of Persons)
- B: BOI (Body of Individuals)
- L: Local Authority
- J: Artificial Juridical Person
- G: Government
- T: Trust
- N: Non-Resident
- K: Krish (legacy category)
Validation Rules
The library validates GSTINs according to the official specification:
- Length: Exactly 15 characters
- State code: Valid 2-digit state/UT code (positions 1-2)
- PAN: Valid PAN format AAAAA9999A (positions 3-12)
- Entity code: 1-9 or A-Z, but not 0 (position 13)
- Default Z: Must be 'Z' for regular taxpayers (position 14)
- Check digit: Luhn mod-36 checksum (position 15)
Tree Shaking
The library supports tree shaking via individual exports:
// Import only what you need
import { validateGSTIN } from 'gstin-tools/validation';
import { computeCheckDigit } from 'gstin-tools/checksum';
import { maskGSTIN } from 'gstin-tools/helpers';
import { gstStateName } from 'gstin-tools/state-codes';Examples
Form Validation
import { validateGSTIN } from 'gstin-tools';
function validateGSTINField(input: string) {
const result = validateGSTIN(input, {
normalizeInput: true,
collectAllIssues: true
});
if (!result.ok) {
return result.issues?.map(issue => issue.message);
}
return null; // Valid
}Invoice Processing
import { parseGSTIN, maskGSTIN } from 'gstin-tools';
function processInvoice(gstinInput: string) {
try {
const gstin = parseGSTIN(gstinInput);
console.log(`State: ${gstin.stateName}`);
console.log(`PAN: ${gstin.pan} (${gstin.panType})`);
console.log(`Masked: ${maskGSTIN(gstin.raw)}`);
return gstin;
} catch (error) {
console.error('Invalid GSTIN:', error.message);
return null;
}
}Batch Validation
import { isValidGSTIN } from 'gstin-tools';
function validateGSTINBatch(gstins: string[]) {
return gstins.map(gstin => ({
gstin,
valid: isValidGSTIN(gstin)
}));
}Browser Support
This library works in all modern browsers and Node.js 18+. The build targets ES2020.
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
License
MIT © Naveen Bhavnani
Disclaimer
This is not an official government library. While we strive for accuracy, always verify GSTINs through official GSTN portals for production use.
