india-kit 0.1.0__py3-none-any.whl

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.
india_kit/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ from .identity.pan import validate_pan, mask_pan, mock_pan
2
+ from .identity.aadhaar import validate_aadhaar, mock_aadhaar
3
+ from .identity.gstin import validate_gstin, mock_gstin
4
+ from .banking.ifsc import validate_ifsc, lookup_ifsc
5
+ from .banking.upi import validate_upi
6
+ from .address.pincode import validate_pincode, lookup_pincode
7
+ from .finance.gst import split_gst
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ __all__ = [
12
+ "validate_pan", "mask_pan", "mock_pan",
13
+ "validate_aadhaar", "mock_aadhaar",
14
+ "validate_gstin", "mock_gstin",
15
+ "validate_ifsc", "lookup_ifsc",
16
+ "validate_upi",
17
+ "validate_pincode", "lookup_pincode",
18
+ "split_gst",
19
+ ]
@@ -0,0 +1,3 @@
1
+ from .pincode import validate_pincode, lookup_pincode
2
+
3
+ __all__ = ["validate_pincode", "lookup_pincode"]
@@ -0,0 +1,29 @@
1
+ import re
2
+
3
+ _PINCODE_RE = re.compile(r'^[1-9][0-9]{5}$')
4
+
5
+ _PINCODE_DB = {
6
+ "110001": {"district": "Central Delhi", "state": "Delhi"},
7
+ "400001": {"district": "Mumbai City", "state": "Maharashtra"},
8
+ "560001": {"district": "Bangalore Urban", "state": "Karnataka"},
9
+ "600001": {"district": "Chennai", "state": "Tamil Nadu"},
10
+ "500001": {"district": "Hyderabad", "state": "Telangana"},
11
+ "700001": {"district": "Kolkata", "state": "West Bengal"},
12
+ "380001": {"district": "Ahmedabad", "state": "Gujarat"},
13
+ "411001": {"district": "Pune", "state": "Maharashtra"},
14
+ "302001": {"district": "Jaipur", "state": "Rajasthan"},
15
+ "226001": {"district": "Lucknow", "state": "Uttar Pradesh"},
16
+ }
17
+
18
+
19
+ def validate_pincode(pin: str) -> dict:
20
+ if not isinstance(pin, str):
21
+ return {"valid": False, "reason": "must be a string"}
22
+ p = pin.strip()
23
+ if not _PINCODE_RE.match(p):
24
+ return {"valid": False, "reason": "invalid pincode (must be 6 digits, first digit 1-9)"}
25
+ return {"valid": True}
26
+
27
+
28
+ def lookup_pincode(pin: str) -> dict | None:
29
+ return _PINCODE_DB.get(pin.strip())
@@ -0,0 +1,4 @@
1
+ from .ifsc import validate_ifsc, lookup_ifsc
2
+ from .upi import validate_upi
3
+
4
+ __all__ = ["validate_ifsc", "lookup_ifsc", "validate_upi"]
@@ -0,0 +1,25 @@
1
+ import re
2
+
3
+ _IFSC_RE = re.compile(r'^[A-Z]{4}0[A-Z0-9]{6}$')
4
+
5
+ _IFSC_DB = {
6
+ "SBIN0000001": {"bank": "State Bank of India", "branch": "Mumbai Main", "city": "Mumbai"},
7
+ "HDFC0000002": {"bank": "HDFC Bank", "branch": "Delhi Main", "city": "Delhi"},
8
+ "ICIC0000003": {"bank": "ICICI Bank", "branch": "Bangalore Main", "city": "Bangalore"},
9
+ "AXIS0000004": {"bank": "Axis Bank", "branch": "Chennai Main", "city": "Chennai"},
10
+ "KKBK0000005": {"bank": "Kotak Mahindra Bank", "branch": "Pune Main", "city": "Pune"},
11
+ }
12
+
13
+
14
+ def validate_ifsc(ifsc: str) -> dict:
15
+ if not isinstance(ifsc, str):
16
+ return {"valid": False, "reason": "must be a string"}
17
+ i = ifsc.strip().upper()
18
+ if not _IFSC_RE.match(i):
19
+ return {"valid": False, "reason": "invalid IFSC format (expected AAAA0XXXXXX)"}
20
+ return {"valid": True}
21
+
22
+
23
+ def lookup_ifsc(ifsc: str) -> dict | None:
24
+ i = ifsc.strip().upper()
25
+ return _IFSC_DB.get(i)
@@ -0,0 +1,11 @@
1
+ import re
2
+
3
+ _UPI_RE = re.compile(r'^[a-zA-Z0-9._+%\-]{2,}@[a-zA-Z]{2,}$')
4
+
5
+
6
+ def validate_upi(vpa: str) -> dict:
7
+ if not isinstance(vpa, str):
8
+ return {"valid": False, "reason": "must be a string"}
9
+ if not _UPI_RE.match(vpa.strip()):
10
+ return {"valid": False, "reason": "invalid UPI VPA format (expected localpart@bank)"}
11
+ return {"valid": True}
@@ -0,0 +1,3 @@
1
+ from .gst import split_gst
2
+
3
+ __all__ = ["split_gst"]
@@ -0,0 +1,6 @@
1
+ def split_gst(amount: float, rate_percent: float, intra_state: bool = True) -> dict:
2
+ total_tax = round(amount * rate_percent / 100, 2)
3
+ if intra_state:
4
+ half = round(total_tax / 2, 2)
5
+ return {"cgst": half, "sgst": half, "igst": 0.0, "total_tax": total_tax}
6
+ return {"cgst": 0.0, "sgst": 0.0, "igst": total_tax, "total_tax": total_tax}
@@ -0,0 +1,9 @@
1
+ from .pan import validate_pan, mask_pan, mock_pan
2
+ from .aadhaar import validate_aadhaar, mock_aadhaar
3
+ from .gstin import validate_gstin, mock_gstin
4
+
5
+ __all__ = [
6
+ "validate_pan", "mask_pan", "mock_pan",
7
+ "validate_aadhaar", "mock_aadhaar",
8
+ "validate_gstin", "mock_gstin",
9
+ ]
@@ -0,0 +1,66 @@
1
+ import random
2
+
3
+ _D = [
4
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
5
+ [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
6
+ [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
7
+ [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
8
+ [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
9
+ [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
10
+ [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
11
+ [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
12
+ [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
13
+ [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
14
+ ]
15
+
16
+ _P = [
17
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
18
+ [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
19
+ [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
20
+ [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
21
+ [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
22
+ [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
23
+ [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
24
+ [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
25
+ ]
26
+
27
+ _INV = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9]
28
+
29
+
30
+ def _verhoeff_check(number: str) -> bool:
31
+ c = 0
32
+ for i, ch in enumerate(reversed(number)):
33
+ c = _D[c][_P[i % 8][int(ch)]]
34
+ return c == 0
35
+
36
+
37
+ def _verhoeff_checksum(number: str) -> int:
38
+ c = 0
39
+ padded = number + "0"
40
+ for i, ch in enumerate(reversed(padded)):
41
+ c = _D[c][_P[i % 8][int(ch)]]
42
+ return _INV[c]
43
+
44
+
45
+ def validate_aadhaar(aadhaar: str) -> dict:
46
+ if not isinstance(aadhaar, str):
47
+ return {"valid": False, "reason": "must be a string"}
48
+ a = aadhaar.strip().replace(" ", "")
49
+ if not a.isdigit() or len(a) != 12:
50
+ return {"valid": False, "reason": "must be exactly 12 digits"}
51
+ if a[0] in "01":
52
+ return {"valid": False, "reason": "first digit cannot be 0 or 1"}
53
+ if not _verhoeff_check(a):
54
+ return {"valid": False, "reason": "invalid Verhoeff checksum"}
55
+ return {"valid": True}
56
+
57
+
58
+ def mock_aadhaar() -> str:
59
+ while True:
60
+ first = str(random.randint(2, 9))
61
+ rest = ''.join([str(random.randint(0, 9)) for _ in range(10)])
62
+ base = first + rest
63
+ check = _verhoeff_checksum(base)
64
+ candidate = base + str(check)
65
+ if _verhoeff_check(candidate):
66
+ return candidate
@@ -0,0 +1,35 @@
1
+ import re
2
+ import random
3
+ import string
4
+ from .pan import validate_pan, mock_pan
5
+
6
+
7
+ _GSTIN_RE = re.compile(r'^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$')
8
+
9
+ _STATE_CODES = [
10
+ "01","02","03","04","05","06","07","08","09","10",
11
+ "11","12","13","14","15","16","17","18","19","20",
12
+ "21","22","23","24","25","26","27","28","29","30",
13
+ "31","32","33","34","35","36","37",
14
+ ]
15
+
16
+
17
+ def validate_gstin(gstin: str) -> dict:
18
+ if not isinstance(gstin, str):
19
+ return {"valid": False, "reason": "must be a string"}
20
+ g = gstin.strip().upper()
21
+ if not _GSTIN_RE.match(g):
22
+ return {"valid": False, "reason": "invalid GSTIN format"}
23
+ embedded_pan = g[2:12]
24
+ result = validate_pan(embedded_pan)
25
+ if not result["valid"]:
26
+ return {"valid": False, "reason": "embedded PAN is invalid"}
27
+ return {"valid": True}
28
+
29
+
30
+ def mock_gstin() -> str:
31
+ state = random.choice(_STATE_CODES)
32
+ pan = mock_pan()
33
+ entity = str(random.randint(1, 9))
34
+ checksum = random.choice(string.digits + string.ascii_uppercase)
35
+ return state + pan + entity + "Z" + checksum
@@ -0,0 +1,30 @@
1
+ import re
2
+ import random
3
+ import string
4
+
5
+
6
+ _PAN_RE = re.compile(r'^[A-Z]{5}[0-9]{4}[A-Z]$')
7
+
8
+ _ENTITY_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
9
+
10
+
11
+ def validate_pan(pan: str) -> dict:
12
+ if not isinstance(pan, str):
13
+ return {"valid": False, "reason": "must be a string"}
14
+ p = pan.strip().upper()
15
+ if not _PAN_RE.match(p):
16
+ return {"valid": False, "reason": "invalid PAN format (expected AAAAA9999A)"}
17
+ return {"valid": True}
18
+
19
+
20
+ def mask_pan(pan: str) -> str:
21
+ p = pan.strip().upper()
22
+ return p[:3] + "XX" + p[5:]
23
+
24
+
25
+ def mock_pan() -> str:
26
+ letters = string.ascii_uppercase
27
+ part1 = ''.join(random.choices(letters, k=5))
28
+ part2 = ''.join(random.choices(string.digits, k=4))
29
+ part3 = random.choice(letters)
30
+ return part1 + part2 + part3
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: india-kit
3
+ Version: 0.1.0
4
+ Summary: The definitive Indian developer toolkit — validate Aadhaar, PAN, GSTIN, IFSC, UPI, pincode and more
5
+ Project-URL: Homepage, https://github.com/Inreal-Solutions/India-kit
6
+ Project-URL: Repository, https://github.com/Inreal-Solutions/India-kit
7
+ Project-URL: Issues, https://github.com/Inreal-Solutions/India-kit/issues
8
+ Author-email: Inreal Solutions <aryan23062001@gmail.com>
9
+ License: MIT
10
+ Keywords: aadhaar,fintech,gst,gstin,ifsc,india,indian,pan,pincode,upi,validate,validator
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+
22
+ # india-kit
23
+
24
+ The definitive Indian developer toolkit for Python. Validate Aadhaar, PAN, GSTIN, IFSC, UPI, pincode and more — zero dependencies, fully typed.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install india-kit
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```python
35
+ from india_kit import (
36
+ validate_pan, mask_pan, mock_pan,
37
+ validate_aadhaar, mock_aadhaar,
38
+ validate_gstin, mock_gstin,
39
+ validate_ifsc, lookup_ifsc,
40
+ validate_upi,
41
+ validate_pincode, lookup_pincode,
42
+ split_gst,
43
+ )
44
+
45
+ validate_pan("ABCDE1234F") # {"valid": True}
46
+ validate_aadhaar("234123412346") # {"valid": True/False}
47
+ validate_gstin("27ABCDE1234F1Z5") # {"valid": True/False}
48
+ validate_ifsc("SBIN0000001") # {"valid": True}
49
+ validate_upi("alice@icici") # {"valid": True}
50
+ validate_pincode("110001") # {"valid": True}
51
+
52
+ lookup_ifsc("SBIN0000001") # {"bank": "State Bank of India", ...}
53
+ lookup_pincode("110001") # {"district": "Central Delhi", "state": "Delhi"}
54
+
55
+ split_gst(1000, 18) # {"cgst": 90.0, "sgst": 90.0, "igst": 0.0, "total_tax": 180.0}
56
+ split_gst(1000, 18, intra_state=False) # {"cgst": 0.0, "sgst": 0.0, "igst": 180.0, ...}
57
+
58
+ mask_pan("ABCDE1234F") # "ABCXX1234F"
59
+ mock_pan() # random valid PAN
60
+ mock_aadhaar() # random valid Aadhaar
61
+ ```
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,15 @@
1
+ india_kit/__init__.py,sha256=RCsZq-uQCWtr3SDe7syRmc5CqCIQ4k-jaidURCD-FJ0,636
2
+ india_kit/address/__init__.py,sha256=X7aAV4xzR7W0ctGyR_7IE4ICUD7QVXyftTIjx_VkxkI,104
3
+ india_kit/address/pincode.py,sha256=wF15bwHWtSHGdvK64Y097CN_3eRy7V_gg2baEdpkck4,1109
4
+ india_kit/banking/__init__.py,sha256=4pNZYIqzdSae8w3J6_ZsnPQgaRdGrdlYdb-8NQgdE4E,135
5
+ india_kit/banking/ifsc.py,sha256=KXESkZt-eaWb4Hyx2jZVwvpvxcKdgpqBcYEA0Y7bVkw,935
6
+ india_kit/banking/upi.py,sha256=7StJa9lvlr4VyDboTZMBEhyiM0ROt2AJ1E3X8ooqpEY,366
7
+ india_kit/finance/__init__.py,sha256=tkl1nS2CfTBbyhCApJZ7aW3iA0rEhIK3ca8YGOJBE44,52
8
+ india_kit/finance/gst.py,sha256=PbAoo-mT1D8N9zkMmjL5JE55wBOlouaImwlNGJQ__Ms,360
9
+ india_kit/identity/__init__.py,sha256=Iv0WF5vBHv_SH6j9YC-vzYDrvpK5rF3td93FR8XdfNc,283
10
+ india_kit/identity/aadhaar.py,sha256=7-_CkYEIaODD3L-L509FHZaVnWhsQKzb5r_UmMYZ6H8,1916
11
+ india_kit/identity/gstin.py,sha256=jOTXqYoVu-iVC7G4UFUFkSU_B2-RwlYDyi5CwBiaHPA,1083
12
+ india_kit/identity/pan.py,sha256=4xgBFirJUP-6Hgp7EdD2R7i0_kMD39NnsEjiN9zt-Vo,767
13
+ india_kit-0.1.0.dist-info/METADATA,sha256=zmCVfdreqKPI9H5WAIxZtw_K9bfWQ-c144_vJeN09lM,2289
14
+ india_kit-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
15
+ india_kit-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any