gst-validator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vijay Misal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # gst-validator
2
+
3
+ Validate and parse Indian GSTIN numbers - real checksum verification, state-code lookup, and the embedded PAN, all offline.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install gst-validator
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import { isValidGstin, parseGstin } from 'gst-validator';
15
+
16
+ isValidGstin('27AAAPL1234C1ZE'); // true
17
+ isValidGstin('not-a-gstin'); // false
18
+
19
+ parseGstin('27AAAPL1234C1ZE');
20
+ // {
21
+ // valid: true,
22
+ // gstin: '27AAAPL1234C1ZE',
23
+ // stateCode: '27',
24
+ // stateName: 'Maharashtra',
25
+ // pan: 'AAAPL1234C',
26
+ // panEntityType: 'Individual',
27
+ // registrationNumber: '1',
28
+ // checkDigit: 'E',
29
+ // }
30
+
31
+ parseGstin('not-a-gstin'); // { valid: false }
32
+ ```
33
+
34
+ ## What this validates
35
+
36
+ A GSTIN is 15 characters: `SSPPPPPPPPPPPCZC`
37
+
38
+ | Position | Meaning |
39
+ |---|---|
40
+ | 1-2 | State code |
41
+ | 3-12 | Embedded PAN |
42
+ | 13 | Registration number for this PAN within the state |
43
+ | 14 | Reserved, always `Z` |
44
+ | 15 | Checksum digit (mod-36) |
45
+
46
+ This library checks, all offline:
47
+
48
+ - **Format**: matches the 15-character structure above
49
+ - **State code**: must be a real, currently-assigned GST state/UT code (not just any two digits)
50
+ - **Checksum**: recomputes the mod-36 check digit and confirms it matches - this catches typos that a regex alone would miss
51
+ - **Embedded PAN**: delegates to [`pan-card-validator`](https://www.npmjs.com/package/pan-card-validator) to confirm the embedded PAN has a valid structure and holder-category code
52
+
53
+ Like PAN validation, this confirms a GSTIN is *well-formed and internally consistent* - it cannot confirm the GSTIN is *actually registered*. That requires the [GST Network's own verification API](https://www.gst.gov.in/).
54
+
55
+ ## License
56
+
57
+ MIT
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "gst-validator",
3
+ "version": "1.0.0",
4
+ "description": "Validate and parse Indian GSTIN numbers, including checksum verification and the embedded PAN",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.d.ts",
11
+ "import": "./src/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "src"
16
+ ],
17
+ "scripts": {
18
+ "test": "node --test test/*.test.js"
19
+ },
20
+ "keywords": [
21
+ "gstin",
22
+ "gst",
23
+ "india",
24
+ "validator",
25
+ "kyc",
26
+ "tax-id"
27
+ ],
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/vjymisal0/gstin-validate.git"
31
+ },
32
+ "homepage": "https://github.com/vjymisal0/gstin-validate#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/vjymisal0/gstin-validate/issues"
35
+ },
36
+ "author": "",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "pan-card-validator": "^1.0.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=18"
43
+ }
44
+ }
package/src/gstin.js ADDED
@@ -0,0 +1,129 @@
1
+ import { parsePan } from 'pan-card-validator';
2
+
3
+ // Public spec: 2 state code + 10-char PAN + 1 entity number + 'Z' + 1 checksum.
4
+ const GSTIN_PATTERN = /^([0-9]{2})([A-Z]{5}[0-9]{4}[A-Z])([1-9A-Z])(Z)([0-9A-Z])$/;
5
+ const CHECKSUM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
6
+
7
+ // Published by the GST Network (as of 2026); code 97/99 reserved for
8
+ // centralized/other jurisdictions not tied to a single state.
9
+ const STATE_CODES = {
10
+ '01': 'Jammu and Kashmir',
11
+ '02': 'Himachal Pradesh',
12
+ '03': 'Punjab',
13
+ '04': 'Chandigarh',
14
+ '05': 'Uttarakhand',
15
+ '06': 'Haryana',
16
+ '07': 'Delhi',
17
+ '08': 'Rajasthan',
18
+ '09': 'Uttar Pradesh',
19
+ 10: 'Bihar',
20
+ 11: 'Sikkim',
21
+ 12: 'Arunachal Pradesh',
22
+ 13: 'Nagaland',
23
+ 14: 'Manipur',
24
+ 15: 'Mizoram',
25
+ 16: 'Tripura',
26
+ 17: 'Meghalaya',
27
+ 18: 'Assam',
28
+ 19: 'West Bengal',
29
+ 20: 'Jharkhand',
30
+ 21: 'Odisha',
31
+ 22: 'Chattisgarh',
32
+ 23: 'Madhya Pradesh',
33
+ 24: 'Gujarat',
34
+ 25: 'Daman and Diu',
35
+ 26: 'Dadra and Nagar Haveli',
36
+ 27: 'Maharashtra',
37
+ 28: 'Andhra Pradesh',
38
+ 29: 'Karnataka',
39
+ 30: 'Goa',
40
+ 31: 'Lakshadweep',
41
+ 32: 'Kerala',
42
+ 33: 'Tamil Nadu',
43
+ 34: 'Puducherry',
44
+ 35: 'Andaman and Nicobar Islands',
45
+ 36: 'Telangana',
46
+ 37: 'Andhra Pradesh (New)',
47
+ 38: 'Ladakh',
48
+ 97: 'Other Territory',
49
+ };
50
+
51
+ function normalize(input) {
52
+ return typeof input === 'string' ? input.trim().toUpperCase() : '';
53
+ }
54
+
55
+ function computeChecksum(gstinBody) {
56
+ let factor = 2;
57
+ let sum = 0;
58
+ const mod = CHECKSUM_CHARS.length;
59
+
60
+ for (let i = gstinBody.length - 1; i >= 0; i--) {
61
+ const codePoint = CHECKSUM_CHARS.indexOf(gstinBody[i]);
62
+ let digit = factor * codePoint;
63
+ digit = Math.floor(digit / mod) + (digit % mod);
64
+ sum += digit;
65
+ factor = factor === 2 ? 1 : 2;
66
+ }
67
+
68
+ const checksumIndex = (mod - (sum % mod)) % mod;
69
+ return CHECKSUM_CHARS[checksumIndex];
70
+ }
71
+
72
+ /**
73
+ * Parses a GSTIN into its structural components, verifying its checksum
74
+ * digit and the embedded PAN's structure.
75
+ * @param {string} input
76
+ * @returns {{
77
+ * valid: boolean,
78
+ * gstin?: string,
79
+ * stateCode?: string,
80
+ * stateName?: string,
81
+ * pan?: string,
82
+ * panEntityType?: string,
83
+ * registrationNumber?: string,
84
+ * checkDigit?: string,
85
+ * }}
86
+ */
87
+ export function parseGstin(input) {
88
+ const gstin = normalize(input);
89
+ const match = GSTIN_PATTERN.exec(gstin);
90
+
91
+ if (!match) {
92
+ return { valid: false };
93
+ }
94
+
95
+ const [, stateCode, pan, registrationNumber, , checkDigit] = match;
96
+
97
+ if (!(stateCode in STATE_CODES)) {
98
+ return { valid: false };
99
+ }
100
+
101
+ if (computeChecksum(gstin.slice(0, 14)) !== checkDigit) {
102
+ return { valid: false };
103
+ }
104
+
105
+ const panInfo = parsePan(pan);
106
+ if (!panInfo.valid) {
107
+ return { valid: false };
108
+ }
109
+
110
+ return {
111
+ valid: true,
112
+ gstin,
113
+ stateCode,
114
+ stateName: STATE_CODES[stateCode] ?? null,
115
+ pan,
116
+ panEntityType: panInfo.entityType,
117
+ registrationNumber,
118
+ checkDigit,
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Validates a GSTIN's format, checksum, and embedded PAN structure.
124
+ * @param {string} input
125
+ * @returns {boolean}
126
+ */
127
+ export function isValidGstin(input) {
128
+ return parseGstin(input).valid;
129
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ export interface ParsedGstin {
2
+ valid: boolean;
3
+ gstin?: string;
4
+ stateCode?: string;
5
+ stateName?: string | null;
6
+ pan?: string;
7
+ panEntityType?: string;
8
+ registrationNumber?: string;
9
+ checkDigit?: string;
10
+ }
11
+
12
+ /**
13
+ * Parses a GSTIN into its structural components, verifying its checksum
14
+ * digit and the embedded PAN's structure.
15
+ */
16
+ export function parseGstin(input: string): ParsedGstin;
17
+
18
+ /**
19
+ * Validates a GSTIN's format, checksum, and embedded PAN structure.
20
+ */
21
+ export function isValidGstin(input: string): boolean;
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export { isValidGstin, parseGstin } from './gstin.js';