├── .gitignore
├── README.md
├── browserid
├── css
│ └── fingerprint.css
├── index.html
├── index.py
└── js
│ └── fingerprint.js
├── config
└── user.json
├── main.py
├── model
├── app.py
├── captcha_model.h5
├── example.py
└── utils.py
├── requirements.txt
└── utils
├── __init__.py
├── _algorithm.py
├── _params.py
└── captcha_solving.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | *.pyc
3 | *.pyo
4 | *.pyd
5 | .env
6 | .venv
7 | env/
8 | venv/
9 | ENV/
10 | *.sqlite3
11 | .vscode/
12 | .DS_Store
13 | Thumbs.db
14 | *.key
15 | *.pem
16 | *.crt
17 | *.log
18 | .ipynb_checkpoints/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VCB DigiBank UNOFFICIAL API 🚀
2 |
3 | ## Giới thiệu 📢
4 |
5 | Đây là project tự động hóa đăng nhập và lấy lịch sử giao dịch tài khoản Vietcombank (VCB DigiBank) thông qua API web. Dự án sử dụng Python để mô phỏng quá trình đăng nhập, giải captcha, mã hóa/giải mã dữ liệu và lấy thông tin giao dịch. Ngoài ra, project còn cung cấp giao diện web đơn giản để lấy BrowserID (Fingerprint) phục vụ cho quá trình đăng nhập.
6 |
7 | ## Tính năng ✨
8 |
9 | - 🔐 Tự động đăng nhập tài khoản VCB DigiBank bằng username, password và BrowserID.
10 | - 🤖 Tự động giải captcha bằng API (Dùng của mình hoặc tự build theo folder model).
11 | - 🔒 Mã hóa/giải mã dữ liệu theo chuẩn của VCB.
12 | - 📊 Lấy toàn bộ lịch sử giao dịch trong khoảng thời gian tùy chọn.
13 | - 🌐 Hỗ trợ proxy và tùy chỉnh session.
14 | - 🖥️ Giao diện web lấy BrowserID (Fingerprint) dễ sử dụng.
15 |
16 | ## Cấu trúc thư mục 📁
17 |
18 | ```
19 | VCB/
20 | ├── main.py
21 | ├── utils/
22 | │ ├── _algorithm.py
23 | │ ├── _params.py
24 | │ ├── captcha_solving.py
25 | │ └── __init__.py
26 | ├── config/
27 | │ └── user.json
28 | ├── broserid/
29 | │ ├── index.html
30 | │ ├── index.py
31 | │ ├── css/
32 | │ │ └── fingerprint.css
33 | │ └── js/
34 | │ └── fingerprint.js
35 | ├── model/
36 | │ ├── app.py
37 | │ ├── captcha_model.h5
38 | │ ├── example.py
39 | │ └── utils.py
40 | ├── requirements.txt
41 | └── README.md
42 | ```
43 |
44 | ## Hướng dẫn sử dụng 🛠️
45 |
46 | ### 1. Cài đặt thư viện
47 |
48 | Cài đặt các thư viện cần thiết (yêu cầu Python 3.8+):
49 |
50 | ```sh
51 | pip install -r requirements.txt
52 | ```
53 |
54 | ### 2. Cấu hình tài khoản
55 |
56 | Điền đầy đủ thông tin vào file `config/user.json`
57 |
58 | ### 3. Lấy BrowserID
59 |
60 | - Mở file `broserid/index.html` trên trình duyệt.
61 | - Copy giá trị `Visitor ID` (BrowserID) và điền vào biến `browser_id` trong `main.py` nếu muốn sử dụng BrowserID mới.
62 |
63 | ### 4. Chạy chương trình
64 |
65 | ```sh
66 | python main.py
67 | ```
68 |
69 | Kết quả sẽ trả về lịch sử giao dịch tài khoản.
70 |
71 | ---
72 |
73 | ## Ví dụ sử dụng các hàm thao tác với Thư viện VCB-API
74 |
75 | ```python
76 | # main.py
77 |
78 | #... exist code...
79 |
80 | # Đăng nhập tài khoản
81 | session = vcb_api.login(username="your_username", password="your_password", browser_id="your_browserid")
82 |
83 | # Lấy thông tin tài khoản
84 | account_info = vcb_api.get_account_info(session)
85 | print("Thông tin tài khoản:", account_info)
86 |
87 | # Lấy lịch sử giao dịch trong khoảng thời gian
88 | history = vcb_api.get_transaction_history(session, from_date="2024-01-01", to_date="2024-05-28")
89 | print("Lịch sử giao dịch:", history)
90 |
91 | # Kiểm tra số tài khoản bank bất kỳ
92 | nquiry_holdername = vcb_api._nquiry_holdername("0345370248", BANKCODE().MB["bankcode"])
93 | print("Thông tin bank: ", nquiry_holdername)
94 | ```
95 |
96 | > Thay thế `your_username`, `your_password`, `your_browserid` bằng thông tin thực tế của bạn.
97 | > Các hàm có thể thay đổi tùy theo cách bạn tổ chức code trong `utils/` hoặc `main.py`.
98 |
99 | ---
100 |
101 | ## Lưu ý ⚠️
102 |
103 | - ❗ Không chia sẻ thông tin tài khoản cho bất kỳ ai.
104 | - 🚫 Không sử dụng project cho mục đích thương mại hoặc vi phạm pháp luật.
105 | - 📚 Project chỉ mang tính chất nghiên cứu và học tập.
106 |
107 | ## Đóng góp 🤝
108 |
109 | Mọi đóng góp, báo lỗi hoặc ý tưởng cải tiến xin gửi issue hoặc pull request.
110 |
111 | ---
112 |
113 | **Tác giả:** LeVietHung - Netrotion
114 | **Liên hệ:** [GitHub](https://github.com/netrotion)
115 |
116 | ## ☕ Buy me a coffee
117 |
118 | Nếu bạn thấy dự án hữu ích và muốn ủng hộ tác giả, bạn có thể gửi đóng góp qua chuyển khoản:
119 |
120 | - **Ngân hàng:** Vietcombank
121 | - **Số tài khoản:** 1057236828
122 | - **Chủ tài khoản:** Lê Việt Hùng
--------------------------------------------------------------------------------
/browserid/css/fingerprint.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #f6f8fa;
3 | font-family: 'Segoe UI', Arial, sans-serif;
4 | color: #222;
5 | margin: 0;
6 | padding: 0;
7 | }
8 | .container {
9 | max-width: 420px;
10 | margin: 40px auto;
11 | background: #fff;
12 | border-radius: 12px;
13 | box-shadow: 0 4px 24px rgba(0,0,0,0.07);
14 | padding: 32px 24px 24px 24px;
15 | }
16 | h1 {
17 | font-size: 1.5em;
18 | margin-bottom: 18px;
19 | color: #0984e3;
20 | text-align: center;
21 | }
22 | .visitor-box {
23 | display: flex;
24 | align-items: center;
25 | gap: 10px;
26 | background: #f1f7ff;
27 | border: 1.5px solid #d6eaff;
28 | border-radius: 8px;
29 | padding: 18px 14px;
30 | margin-bottom: 18px;
31 | justify-content: space-between;
32 | }
33 | .visitor-label {
34 | font-weight: 500;
35 | color: #555;
36 | margin-right: 8px;
37 | }
38 | .visitor-id {
39 | font-family: 'Fira Mono', 'Consolas', monospace;
40 | font-size: 1.08em;
41 | color: #222;
42 | background: #eaf6ff;
43 | padding: 4px 8px;
44 | border-radius: 5px;
45 | word-break: break-all;
46 | flex: 1 1 auto;
47 | margin-right: 8px;
48 | }
49 | .copy-btn {
50 | background: #0984e3;
51 | color: #fff;
52 | border: none;
53 | border-radius: 5px;
54 | padding: 6px 14px;
55 | font-size: 1em;
56 | font-weight: 500;
57 | cursor: pointer;
58 | transition: background 0.15s;
59 | }
60 | .copy-btn:hover {
61 | background: #065fa7;
62 | }
63 | .info-box {
64 | background: #f9f9f9;
65 | border: 1px solid #e0e0e0;
66 | border-radius: 8px;
67 | padding: 16px 14px;
68 | color: #444;
69 | font-size: 1em;
70 | min-height: 60px;
71 | margin-bottom: 8px;
72 | }
73 | .copied-msg {
74 | color: #27ae60;
75 | font-size: 0.98em;
76 | margin-left: 8px;
77 | display: none;
78 | }
--------------------------------------------------------------------------------
/browserid/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Fingerprint
9 |
10 |
11 |
12 |
Get BrowserID Page
13 |
14 | - Đây là trang lấy BrowserID
15 | - Đảm bảo site được mở dưới giao thức https
16 | - Giá trị này được sử dụng để xử lý session đã đăng nhập bank của trình duyệt
17 | - Lưu ý: Không được bỏ trống hoặc điền sai BrowserID!
18 | - Nếu không có hosting, truy cập link sau để lấy BrowserID chuẩn:
19 | Click here
20 |
21 |
22 | Visitor ID:
23 | Đang lấy...
24 |
25 | Đã copy!
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/browserid/index.py:
--------------------------------------------------------------------------------
1 | __import__("webbrowser").open("https://bank-task-s1s5.onrender.com/get_browser_id")
--------------------------------------------------------------------------------
/browserid/js/fingerprint.js:
--------------------------------------------------------------------------------
1 | function setCookie(name, value, days) {
2 | const d = new Date();
3 | d.setTime(d.getTime() + days*24*60*60*1000);
4 | const expires = "expires=" + d.toUTCString();
5 | document.cookie = name + "=" + value + ";" + expires + ";path=/";
6 | }
7 | async function getFingerprint() {
8 | const fpPromise = FingerprintJS.load({ monitoring: !1 });
9 | const fp = await fpPromise;
10 | const result = await fp.get();
11 | setCookie("visitorId", result.visitorId, 7);
12 | document.getElementById('visitor-id').textContent = result.visitorId;
13 | }
14 | getFingerprint();
15 | document.getElementById('copy-btn').onclick = function() {
16 | const visitorId = document.getElementById('visitor-id').textContent;
17 | navigator.clipboard.writeText(visitorId).then(function() {
18 | const msg = document.getElementById('copied-msg');
19 | msg.style.display = 'inline';
20 | setTimeout(() => { msg.style.display = 'none'; }, 1200);
21 | });
22 | };
--------------------------------------------------------------------------------
/config/user.json:
--------------------------------------------------------------------------------
1 | {
2 | "username" : "",
3 | "password" : "",
4 | "browser_id" : ""
5 | }
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from utils import *
2 |
3 | #https://vcbdigibank.vietcombank.com.vn/default-libs_shared_services_src_index_ts.3ac1e1e8a8a6e65f.
4 |
5 | class VCB:
6 | def __init__(self, username: str = "", password: str = "", browserID = "", proxies: dict = {}, session: object = None) -> None:
7 | if session:
8 | self.session = session
9 | else:
10 | self.session = requests.Session()
11 | if proxies:
12 | self.session.proxies.update(
13 | {
14 | "http": proxies.get("http"),
15 | "https": proxies.get("https")
16 | }
17 | )
18 | self.session.headers.update(self._headers())
19 | update_cookies = self.session.get("https://vcbdigibank.vietcombank.com.vn/auth")
20 |
21 | self.mid = {
22 | "login" : 6,
23 | "balance" : 13,
24 | "transaction_history" : 14,
25 | "nquiry_holdername" : 917
26 |
27 | }
28 | self.session_id = ""
29 | self.access_key = ""
30 | self.cif = None
31 | self.mobileId = ""
32 | self.accountNo = ""
33 | self.clientId = ""
34 | self.login_message = ""
35 | self.x_lim_id = ""
36 | self.x_requests_id = ""
37 | self.is_login = False
38 | self.username = username
39 | self.password = password
40 | self.browserID = browserID
41 | self.BANKLISTS = BANK_CODE()
42 | self.private_key, self.public_key = gen_keys()
43 | login_data = self._login()
44 | try:
45 | self.login_message = login_data["message"]
46 | except:
47 | self.login_message = ""
48 | self.login_logs = login_data["message"]
49 | self.is_login = login_data["status"]
50 |
51 | def _get_default_json(self, additional_dict: dict = {}) -> dict:
52 | default = {
53 | "DT": "Windows",
54 | "E": None,
55 | "OV": "10",
56 | "PM": "Chrome 136.0.0.0",
57 | "accountType": "D",
58 | "appVersion": "",
59 | "browserId": self.browserID,
60 | "cif": None,
61 | "clientPubKey": self.public_key,
62 | "lang": "vi",
63 | "user": self.username,
64 | }
65 | if self.access_key:
66 | default["accountNo"] = self.accountNo
67 | default["clientId"] = self.clientId
68 | default["cif"] = self.cif
69 | default["mobileId"] = self.mobileId
70 | default["sessionId"] = self.session_id
71 | self.x_lim_id = x_lim_id(self.username)
72 | self.x_requests_id = x_requests_id(self.username)
73 | return {
74 | **default,
75 | **additional_dict
76 | }
77 |
78 | def _headers(self) -> dict:
79 | return {
80 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
81 | 'accept-language': 'vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5',
82 | 'cache-control': 'no-cache',
83 | 'dnt': '1',
84 | 'pragma': 'no-cache',
85 | 'priority': 'u=0, i',
86 | 'sec-ch-ua': '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
87 | 'sec-ch-ua-mobile': '?0',
88 | 'sec-ch-ua-platform': '"Windows"',
89 | 'sec-fetch-dest': 'document',
90 | 'sec-fetch-mode': 'navigate',
91 | 'sec-fetch-site': 'same-origin',
92 | 'sec-fetch-user': '?1',
93 | 'upgrade-insecure-requests': '1',
94 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
95 | }
96 |
97 | def _post_headers(self, additional_dict: dict = {}) -> dict:
98 | headers = {
99 | 'accept' : 'application/json, text/plain, */*',
100 | 'accept-language' : 'vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5',
101 | 'cache-control' : 'no-cache',
102 | 'content-type' : 'application/json',
103 | 'dnt' : '1',
104 | 'origin' : 'https://vcbdigibank.vietcombank.com.vn',
105 | 'pragma' : 'no-cache',
106 | 'priority' : 'u=1, i',
107 | 'referer' : 'https://vcbdigibank.vietcombank.com.vn/',
108 | 'sec-ch-ua' : '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
109 | 'sec-ch-ua-mobile' : '?0',
110 | 'sec-ch-ua-platform': '"Windows"',
111 | 'sec-fetch-dest' : 'empty',
112 | 'sec-fetch-mode' : 'cors',
113 | 'sec-fetch-site' : 'same-site',
114 | 'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
115 | 'x-channel' : 'Web',
116 | 'x-lim-id' : self.x_lim_id,
117 | 'x-request-id' : self.x_requests_id,
118 | **additional_dict
119 | }
120 | return headers
121 |
122 | def _login(self) -> dict:
123 | """dang nhap web"""
124 | captcha_guid = create_guid()
125 | response = self.session.post(
126 | url = "https://digiapp.vietcombank.com.vn/authen-service/v1/login",
127 | json=encrypt_request(
128 | data=self._get_default_json(
129 | {
130 | "captchaToken": captcha_guid,
131 | "captchaValue": Captcha(captcha_guid).solving(),
132 | "mid": self.mid["login"],
133 | "password": self.password,
134 | }
135 | ),
136 | client_pub_key_str=self.public_key,
137 | server_pub_key_base64=LOCAL["publicKey"]
138 | ),
139 | headers = self._post_headers()
140 | ).json()
141 | dec = decrypt_response(response, self.private_key)
142 | if dec["code"] == "019":
143 | return {
144 | "status": False,
145 | "message" : "chưa mở login web setting",
146 | "data" : dec
147 | }
148 | elif dec["code"] == "20231":
149 | return {
150 | "status": False,
151 | "message" : "browser is not activated",
152 | "data" : dec
153 | }
154 | elif dec["code"] == "0111":
155 | """captcha not fit"""
156 | return self._login()
157 | elif dec["code"] == "9993":
158 | return {
159 | "status": False,
160 | "message" : "Data format error",
161 | "data" : dec
162 | }
163 | elif dec["code"] == "3005":
164 | return {
165 | "status": False,
166 | "message" : "Unable username or password",
167 | "data" : dec
168 | }
169 | elif dec["code"] == "00":
170 | self.session_id = dec["sessionId"]
171 | self.access_key = dec["accessKey"]
172 | self.cif = dec["userInfo"]["cif"]
173 | self.mobileId = dec["userInfo"]["mobileId"]
174 | self.accountNo = dec["userInfo"]["defaultAccount"]
175 | self.clientId = dec["userInfo"]["clientId"]
176 |
177 | return {
178 | "status": True,
179 | "message" : "login success",
180 | "data" : dec
181 | }
182 | return {
183 | "status" : False,
184 | "message" : "login failed",
185 | "data" : dec
186 | }
187 |
188 | def _checksum():
189 | #khong can thiet :D
190 | pass
191 |
192 | def _bank_soft_otp():
193 | #khong can thiet :D
194 | pass
195 |
196 | def _get_home_popup():
197 | #khong can thiet :D
198 | pass
199 |
200 | def _get_config_value():
201 | #khong can thiet :D
202 | pass
203 |
204 | def _nquiry_holdername(self, accountNo: str = "", bankCode: str = ""):
205 | """kiem tra so tai khoan bank trong nuoc"""
206 |
207 | response = self.session.post(
208 | url="https://digiapp.vietcombank.com.vn/napas-service/v1/inquiry-holdername",
209 | json=encrypt_request(
210 | data=self._get_default_json(
211 | {
212 | "mid": self.mid["nquiry_holdername"],
213 | "accountNo": accountNo,
214 | "bankCode": bankCode
215 | }
216 | ),
217 | client_pub_key_str=self.public_key,
218 | server_pub_key_base64=LOCAL["publicKey"]
219 | ),
220 | headers=self._post_headers()
221 | ).json()
222 | return decrypt_response(response, self.private_key)
223 |
224 | def _get_account_balance(self, format: object = int) -> dict:
225 | """lay so du hien tai"""
226 | response = self.session.post(
227 | url="https://digiapp.vietcombank.com.vn/bank-service/v2/get-account-detail",
228 | json= encrypt_request(
229 | data = self._get_default_json(
230 | {"mid" : self.mid["balance"]}
231 | ),
232 | client_pub_key_str=self.public_key,
233 | server_pub_key_base64=LOCAL["publicKey"]
234 | ),
235 | headers=self._post_headers()
236 | ).json()
237 | dec = decrypt_response(response, self.private_key)
238 | if dec["code"] == "00":
239 | balance = dec["accountDetail"]["availBalance"]
240 | if format == int:
241 | balance = int(balance.replace(",","").replace(".",""))
242 | return {
243 | "code" : dec["code"],
244 | "status" : True,
245 | "balance" : balance,
246 | "full_content" : dec
247 | }
248 |
249 | elif dec["code"] in ["108", "917"]:
250 | self._login()
251 | return self._get_account_balance(format=format)
252 |
253 | return {
254 | "status" : False,
255 | "code" : dec["code"],
256 | "full_content" : dec
257 | }
258 |
259 | def _transaction_history(self, from_date: str ="", to_date: str = "", page_index: int = 0) -> json:
260 | """
261 | lay danh sach lich su giao dich
262 | "fromDate": "12/05/2025",
263 | "toDate": "19/05/2025",
264 | """
265 | history = []
266 | response = self.session.post(
267 | url = "https://digiapp.vietcombank.com.vn/bank-service/v1/transaction-history",
268 | json=encrypt_request(
269 | data = self._get_default_json(
270 | {
271 | "mid" : self.mid["transaction_history"],
272 | "lengthInPage": 10,
273 | "pageIndex": page_index,
274 | "fromDate": from_date,
275 | "toDate": to_date,
276 | }
277 | ),
278 | client_pub_key_str=self.public_key,
279 | server_pub_key_base64=LOCAL["publicKey"]
280 | ),
281 | headers = self._post_headers()
282 | ).json()
283 |
284 | dec = decrypt_response(response, self.private_key)
285 | if dec["code"] == "00":
286 | return {
287 | "code" : dec["code"],
288 | "status" : True,
289 | "history" : dec["transactions"],
290 | "next_index" : dec["nextIndex"]
291 | }
292 |
293 | elif dec["code"] in ["108", "917"]:
294 | self._login()
295 | return self._transaction_history(from_date, to_date)
296 |
297 | return {
298 | "status" : False,
299 | "code" : dec["code"],
300 | "full_content" : dec
301 | }
302 |
303 | def get_full_transation_history(self, from_date: str = "", to_date: str = ""):
304 | """lay tat ca danh sach lich su giao dich"""
305 | if not self.is_login:
306 | raise Exception(f"Cannot login! : {self.login_logs}")
307 |
308 | def initalizing_response(data: dict = {}) -> list:
309 | if data["status"]:return data["history"]
310 | return []
311 |
312 | page_index = 0
313 | history = []
314 | while True:
315 | page_data = self._transaction_history(from_date, to_date, page_index)
316 | if int(page_data["next_index"]) == 1:
317 | transactions = initalizing_response(page_data)
318 | else:
319 | break
320 | history.extend(transactions)
321 | page_index += 1
322 | return {
323 | "code" : "00",
324 | "status" : True,
325 | "history" : history
326 | }
327 |
328 | if __name__ == "__main__":
329 | userdata = json.loads(open("config/user.json", "r", encoding="utf-8").read())
330 | username, password, browser_id = userdata["username"], userdata["password"], userdata["browser_id"]
331 |
332 | print(
333 | VCB(
334 | username=username,
335 | password=password,
336 | browserID=browser_id
337 | ).get_full_transation_history(*get_date())#._nquiry_holdername("", BANK_CODE().MB["bankcode"])#_get_account_balance()
338 | )
339 |
--------------------------------------------------------------------------------
/model/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, request, jsonify
2 | import tensorflow as tf
3 | from tensorflow import keras
4 | import numpy as np
5 | import cv2
6 | import os
7 | from utils import preprocess_image, decode_prediction
8 | import base64
9 | from PIL import Image
10 | import io
11 | import urllib
12 |
13 | app = Flask(__name__)
14 |
15 | model = keras.models.load_model("captcha_model.h5", custom_objects={'leaky_relu': tf.nn.leaky_relu})
16 | img_width = 160
17 | img_height = 60
18 | max_length = 5
19 | characters = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
20 | char_to_num = keras.layers.StringLookup(vocabulary=list(characters), mask_token=None)
21 | num_to_char = keras.layers.StringLookup(vocabulary=char_to_num.get_vocabulary(), mask_token=None, invert=True)
22 |
23 |
24 | @app.route("/predict", methods=["POST"])
25 | def predict():
26 | """
27 | data = {
28 | "data": b64 or link_img
29 | }
30 | """
31 | data = request.get_json()
32 | if not data or "data" not in data:
33 | return jsonify({"error": "No data provided"}), 400
34 |
35 | image_data = data["data"]
36 |
37 | if image_data.startswith("data:image/jpeg;base64,"):
38 | image_data = image_data.replace("data:image/jpeg;base64,", "")
39 | image_data = base64.b64decode(image_data)
40 | image = Image.open(io.BytesIO(image_data))
41 | image = np.array(image.convert("L"))
42 |
43 | elif image_data.startswith("http"):
44 | image = Image.open(urllib.request.urlopen(image_data))
45 | image = np.array(image.convert("L"))
46 |
47 | image = preprocess_image(image, img_width, img_height)
48 | image_tensor = tf.convert_to_tensor(image, dtype=tf.float32)
49 | image_tensor = tf.expand_dims(image_tensor, axis=0) # Thêm batch
50 | preds = model.predict(image_tensor)
51 | result_text = decode_prediction(preds, num_to_char, max_length)
52 |
53 | return jsonify({"result": result_text})
54 |
55 | if __name__ == "__main__":
56 | app.run(port=5000, debug=False)
57 |
--------------------------------------------------------------------------------
/model/captcha_model.h5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netrotion/VCB-API/dec315c81f4aa0f23f054f9d3c8e865c1a721934/model/captcha_model.h5
--------------------------------------------------------------------------------
/model/example.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import base64
3 |
4 | image_path = ""
5 |
6 | url = "http://127.0.0.1:5000/predict"
7 | js = {"data" : f"data:image/jpeg;base64,{base64.b64encode(open(image_path, 'rb').read()).decode()}"}
8 |
9 | response = requests.post(url, json=js)
10 |
11 | if response.status_code == 200:
12 | print("Prediction:", response.json()["result"])
13 | else:
14 | print("Error:", response.status_code, response.text)
15 |
--------------------------------------------------------------------------------
/model/utils.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import tensorflow as tf
4 |
5 | def preprocess_image(img, img_width, img_height):
6 | img = cv2.resize(img, (img_width, img_height))
7 | img = img.astype("float32") / 255.0
8 | img = img.T
9 | img = np.expand_dims(img, axis=-1)
10 | return img
11 |
12 |
13 | def decode_prediction(preds, num_to_char, max_length) -> str:
14 | input_len = np.ones(preds.shape[0]) * preds.shape[1]
15 | results = tf.keras.backend.ctc_decode(preds, input_length=input_len, greedy=True)[0][0][:, :max_length]
16 |
17 | output_text = []
18 | for res in results:
19 | text = tf.strings.reduce_join(num_to_char(res)).numpy().decode("utf-8")
20 | output_text.append(text)
21 |
22 | return output_text[0] if output_text else ""
23 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==2.2.5
2 | curl_cffi==0.10.0
3 | pycryptodome==3.21.0
4 | crcmod==1.7
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .captcha_solving import *
2 | from ._algorithm import *
3 | from ._params import *
--------------------------------------------------------------------------------
/utils/_algorithm.py:
--------------------------------------------------------------------------------
1 | from curl_cffi import requests
2 | import base64
3 | import os
4 | import json
5 | from Crypto.PublicKey import RSA
6 | from Crypto.Cipher import AES, PKCS1_v1_5
7 | from Crypto.Util import Counter
8 | from Crypto.Random import get_random_bytes
9 | from Crypto import Random
10 | from datetime import datetime, timezone, timedelta
11 | from time import time
12 | from random import random
13 | import crcmod
14 | import hashlib
15 | VN_TZ = timezone(timedelta(hours=7))
16 |
17 | #static
18 | LOCAL = {
19 | "name": "production",
20 | "production": True,
21 | "manifestPath": "/assets/module-federation.manifest.production.json",
22 | "mobileCallUrl": "https://callapi-iframe.vietcombank.com.vn/",
23 | "apiUrl": "https://digiapp.vietcombank.com.vn",
24 | "mediaBaseUrl": "https://digibankm5.vietcombank.com.vn/get_file",
25 | "uploadAvatarUrl": "https://digibankm5.vietcombank.com.vn/upfile/avatar_upload",
26 | "removeAvatarUrl": "https://digibankm5.vietcombank.com.vn/contact/remove_my_avatar",
27 | "uploadAES": "2dcd8b543bd95a83",
28 | "uploadHMac": "94280a1454bc5634a33181125fcedc08",
29 | "token": "dmNiOjI3YWNkNDM1YmRiMjU5NTRhY2Q2NDliNmE1MTNmYmI5",
30 | "publicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFpa3FRckl6WkprVXZIaXNqZnU1WkNOK1RMeS8vNDNDSWM1aEpFNzA5VElLM0hiY0M5dnVjMitQUEV0STZwZVNVR3FPbkZvWU93bDNpOHJSZFNhSzE3RzJSWk4wMU1JcVJJSi82YWM5SDRMMTFkdGZRdFI3S0hxRjdLRDBmajZ2VTRrYjUrMGN3UjNSdW1CdkRlTWxCT2FZRXBLd3VFWTlFR3F5OWJjYjVFaE5HYnh4TmZiVWFvZ3V0VndHNUMxZUtZSXR6YVlkNnRhbzNncTdzd05IN3A2VWRsdHJDcHhTd0ZFdmM3ZG91RTJzS3JQRHA4MDdaRzJkRnNsS3h4bVI0V0hESFdmSDBPcHpyQjVLS1dRTnl6WHhUQlhlbHFyV1pFQ0xSeXBOcTdQKzFDeWZnVFNkUTM1ZmRPN00xTW5pU0JUMVYzM0xkaFhvNzMvOXFENWU1VlFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t",
31 | "pciEndpoint": "https://iframe.vietcombank.com.vn",
32 | "secretKey": "79a96ccb09b127f81a0f48c23ef7c084e5132862a39c643c3964b6bd24e98685",
33 | "crcKey": "6q93-@u9",
34 | "uploadRemittanceUrl": "https://uploadgw.vietcombank.com.vn",
35 | "downloadRemittanceUrl": "https://downloadgw-cde.vietcombank.com.vn",
36 | "insightEndpoint": "https://vcbinsight.vietcombank.com.vn",
37 | "storeKey": {
38 | "HERCU": "vcb.hercu"
39 | },
40 | "vdn": {
41 | "MASS_VI": "5301",
42 | "MASS_VI_ERROR": "5302",
43 | "MASS_EN": "5303",
44 | "VIP_VI": "5304",
45 | "VIP_EN": "5305"
46 | },
47 | "loyaltyRewards": "https://loyalty.vietcombank.com.vn/portal-web/"
48 | }
49 |
50 | def get_date() -> tuple:
51 | """
52 | Trả về tuple (from_date, to_date) dạng 'dd/mm/yyyy'.
53 | from_date: 30 ngày trước hôm nay
54 | to_date: hôm nay
55 | """
56 | now = datetime.now(VN_TZ)
57 | to_date = now.strftime("%d/%m/%Y")
58 | from_date = (now - timedelta(days=30)).strftime("%d/%m/%Y")
59 | return from_date, to_date
60 |
61 | def gen_keys() -> tuple:
62 | key = RSA.generate(1024)
63 | private_key = key.export_key().decode()
64 | public_key = key.publickey().export_key().decode().replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace("\n", "")
65 | return private_key, public_key
66 |
67 | def aes_encrypt(data: str, key: str, iv: str) -> bytes:
68 | ctr = Counter.new(128, initial_value=int.from_bytes(iv, byteorder='big'))
69 | cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
70 | return cipher.encrypt(data)
71 |
72 | def aes_decrypt(ciphertext: bytes, key: str, iv: str) -> str:
73 | ctr = Counter.new(128, initial_value=int.from_bytes(iv, byteorder='big'))
74 | cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
75 | return cipher.decrypt(ciphertext)
76 |
77 | def encrypt_request(data: dict, client_pub_key_str: str, server_pub_key_base64: str) -> dict:
78 | try:
79 | m = get_random_bytes(32)
80 | O = get_random_bytes(16)
81 | f = {
82 | "clientPubKey": client_pub_key_str,
83 | **data
84 | }
85 | cipher = AES.new(m, AES.MODE_CTR, nonce=b'', initial_value=O)
86 | plaintext = json.dumps(f).encode("utf-8")
87 | ciphertext = cipher.encrypt(plaintext)
88 | encrypted_data = O + ciphertext
89 | d_base64 = base64.b64encode(encrypted_data).decode()
90 | server_pub_key_pem = base64.b64decode(server_pub_key_base64).decode()
91 | rsa_key = RSA.importKey(server_pub_key_pem)
92 | cipher_rsa = PKCS1_v1_5.new(rsa_key)
93 | encrypted_key = cipher_rsa.encrypt(base64.b64encode(m))
94 | k_base64 = base64.b64encode(encrypted_key).decode()
95 | return {
96 | "d": d_base64,
97 | "k": k_base64
98 | }
99 |
100 | except Exception as e:
101 | print("Encryption error:", e)
102 | return {
103 | "d": "",
104 | "k": ""
105 | }
106 |
107 | def decrypt_response(enc_obj: dict, private_key_pem: str) -> dict:
108 | k = base64.b64decode(enc_obj["k"])
109 | d = base64.b64decode(enc_obj["d"])
110 | rsa_key = RSA.import_key(private_key_pem)
111 | cipher_rsa = PKCS1_v1_5.new(rsa_key)
112 | sentinel = Random.new().read(15)
113 | aes_key_base64 = cipher_rsa.decrypt(k, sentinel)
114 | aes_key = base64.b64decode(aes_key_base64)
115 | iv = d[:16]
116 | data = d[16:]
117 | decrypted_data = aes_decrypt(data, aes_key, iv)
118 | return json.loads(decrypted_data.decode())
119 |
120 | def x_lim_id(params):
121 | hash_obj = hashlib.sha256((params + LOCAL["crcKey"]).encode())
122 | W = hash_obj.hexdigest()
123 |
124 | return W
125 |
126 | def x_requests_id(params):
127 | millis = str(int(time() * 1000))
128 | rand = str(int(random() * 100))
129 | crc16 = crcmod.predefined.mkCrcFun('crc-16')
130 | crc_val = format(crc16(params.encode()), 'x')
131 | U = millis + rand + crc_val
132 | return U
133 |
134 |
--------------------------------------------------------------------------------
/utils/_params.py:
--------------------------------------------------------------------------------
1 | class BANK_CODE:
2 | def __init__(self):
3 | self.VIETCOMBANK = {"fullname": "Ngân hàng TMCP Ngoại Thương Việt Nam", "bankcode": "970436", "sub_name": "NGÂN HÀNG TMCP NGOẠI THƯƠNG VIỆT NAM"}
4 | self.BIDV = {"fullname": "Ngân hàng Đầu tư và phát triển Việt Nam", "bankcode": "970418", "sub_name": "NGÂN HÀNG ĐẦU TƯ VÀ PHÁT TRIỂN VIỆT NAM"}
5 | self.VIETINBANK = {"fullname": "Ngân hàng Công Thương Việt Nam", "bankcode": "970415", "sub_name": "NGÂN HÀNG CÔNG THƯƠNG VIỆT NAM"}
6 | self.AGRIBANK = {"fullname": "Ngân hàng Nông nghiệp và phát triển nông thôn Việt Nam", "bankcode": "970405", "sub_name": "NGÂN HÀNG NÔNG NGHIỆP VÀ PHÁT TRIỂN NÔNG THÔN VIỆT NAM"}
7 | self.ABBANK = {"fullname": "Ngân hàng An Bình", "bankcode": "970425", "sub_name": "NGÂN HÀNG AN BÌNH"}
8 | self.ACB = {"fullname": "Ngân hàng Á Châu", "bankcode": "970416", "sub_name": "NGÂN HÀNG Á CHÂU"}
9 | self.ANZ = {"fullname": "Ngân hàng TNHH Một Thành Viên ANZ Việt Nam", "bankcode": "01602002", "sub_name": "NGÂN HÀNG TNHH MỘT THÀNH VIÊN ANZ VIỆT NAM"}
10 | self.BAC_A_BANK = {"fullname": "Ngân hàng Bắc Á", "bankcode": "970409", "sub_name": "NGÂN HÀNG BẮC Á"}
11 | self.BANGKOK_BANK_TPHCM = {"fullname": "Ngân hàng Bangkok Bank - CN TP Hồ Chí Minh", "bankcode": "79612001", "sub_name": "NGÂN HÀNG BANGKOK BANK - CN TP HỒ CHÍ MINH"}
12 | self.BANGKOK_BANK_HN = {"fullname": "Ngân hàng Bangkok Bank - CN Hà Nội", "bankcode": "01667001", "sub_name": "NGÂN HÀNG BANGKOK BANK - CN HÀ NỘI"}
13 | self.BAO_VIET_BANK = {"fullname": "Ngân hàng Bảo Việt", "bankcode": "970438", "sub_name": "NGÂN HÀNG BẢO VIỆT"}
14 | self.BFCE = {"fullname": "Ngân hàng BPCE IOM", "bankcode": "79601001", "sub_name": "NGÂN HÀNG BPCE IOM"}
15 | self.BIDC_TPHCM = {"fullname": "Ðầu Tư và Phát Triển Campuchia - CN TP Hồ Chí Minh", "bankcode": "79648001", "sub_name": "ÐẦU TƯ VÀ PHÁT TRIỂN CAMPUCHIA - CN TP HỒ CHÍ MINH"}
16 | self.BIDC_HN = {"fullname": "Ðầu Tư và Phát Triển Campuchia - CN Hà Nội", "bankcode": "555666", "sub_name": "ÐẦU TƯ VÀ PHÁT TRIỂN CAMPUCHIA - CN HÀ NỘI"}
17 | self.BNK = {"fullname": "Ngân hàng Busan", "bankcode": "79660001", "sub_name": "NGÂN HÀNG BUSAN"}
18 | self.BNP_PARIBAS_HCM_TPHCM = {"fullname": "Ngân hàng BNP Paribas – CN TP Hồ Chí Minh", "bankcode": "963666", "sub_name": "NGÂN HÀNG BNP PARIBAS – CN TP HỒ CHÍ MINH"}
19 | self.BNP_PARIBAS_HN_HN = {"fullname": "Ngân hàng BNP Paribas – CN Hà Nội", "bankcode": "963668", "sub_name": "NGÂN HÀNG BNP PARIBAS – CN HÀ NỘI"}
20 | self.BOCHK_TPHCM = {"fullname": "Ngân hàng Bank of China (Hongkong) Limited – CN TP Hồ Chí Minh", "bankcode": "963688", "sub_name": "NGÂN HÀNG BANK OF CHINA (HONGKONG) LIMITED – CN TP HỒ CHÍ MINH"}
21 | self.BOCOM = {"fullname": "Ngân hàng Bank of Communications Co., Ltd", "bankcode": "79615001", "sub_name": "NGÂN HÀNG BANK OF COMMUNICATIONS CO., LTD"}
22 | self.BOI_BANK_OF_INDIA = {"fullname": "Ngân hàng Bank of India", "bankcode": "79659001", "sub_name": "NGÂN HÀNG BANK OF INDIA"}
23 | self.BSP = {"fullname": "Ngân hàng SINOPAC", "bankcode": "79632001", "sub_name": "NGÂN HÀNG SINOPAC"}
24 | self.BVBANK_TIMO = {"fullname": "Ngân hàng số Timo - Ngân hàng TMCP Bản Việt", "bankcode": "963388", "sub_name": "NGÂN HÀNG SỐ TIMO - NGÂN HÀNG TMCP BẢN VIỆT"}
25 | self.CAKE = {"fullname": "Ngân hàng CAKE BY VPBANK", "bankcode": "546034", "sub_name": "NGÂN HÀNG CAKE BY VPBANK"}
26 | self.VCBNEO = {"fullname": "Ngân hàng TM TNHH Một Thành Viên Ngoại thương Công nghệ số", "bankcode": "970444", "sub_name": "NGÂN HÀNG TM TNHH MỘT THÀNH VIÊN NGOẠI THƯƠNG CÔNG NGHỆ SỐ"}
27 | self.CCB = {"fullname": "Ngân hàng Xây dựng Trung Quốc", "bankcode": "79611001", "sub_name": "NGÂN HÀNG XÂY DỰNG TRUNG QUỐC"}
28 | self.CIMB = {"fullname": "Ngân hàng TNHH MTV CIMB Việt\xa0Nam", "bankcode": "422589", "sub_name": "NGÂN HÀNG TNHH MTV CIMB VIỆT\xa0NAM"}
29 | self.CITIBANK_HN = {"fullname": "Ngân hàng Citibank Việt Nam - CN Hà Nội", "bankcode": "01605001", "sub_name": "NGÂN HÀNG CITIBANK VIỆT NAM - CN HÀ NỘI"}
30 | self.CITIBANK = {"fullname": "Ngân hàng Citibank Việt Nam", "bankcode": "533948", "sub_name": "NGÂN HÀNG CITIBANK VIỆT NAM"}
31 | self.CO_OPBANK = {"fullname": "Ngân hàng Hợp tác xã Việt Nam", "bankcode": "970446", "sub_name": "NGÂN HÀNG HỢP TÁC XÃ VIỆT NAM"}
32 | self.CREDIT_AGRICOLE = {"fullname": "Ngân hàng Crédit Agricole Corporate and Investment - CN TP Hồ Chí Minh", "bankcode": "79621001", "sub_name": "NGÂN HÀNG CRÉDIT AGRICOLE CORPORATE AND INVESTMENT - CN TP HỒ CHÍ MINH"}
33 | self.CTBC = {"fullname": "Ngân hàng TNHH CTBC", "bankcode": "79629001", "sub_name": "NGÂN HÀNG TNHH CTBC"}
34 | self.CUB_CL = {"fullname": "Ngân hàng Cathay United - CN Chu Lai", "bankcode": "49634001", "sub_name": "NGÂN HÀNG CATHAY UNITED - CN CHU LAI"}
35 | self.CUB_HCM_TPHCM = {"fullname": "Cathay United Bank – CN TP Hồ Chí Minh", "bankcode": "168999", "sub_name": "CATHAY UNITED BANK – CN TP HỒ CHÍ MINH"}
36 | self.DBS = {"fullname": "Ngân hàng DBS - CN Hồ Chí Minh", "bankcode": "796500", "sub_name": "NGÂN HÀNG DBS - CN HỒ CHÍ MINH"}
37 | self.DEUTSCHE_BANK = {"fullname": "Ngân hàng DEUTSCHE BANK", "bankcode": "79619001", "sub_name": "NGÂN HÀNG DEUTSCHE BANK"}
38 | self.DGB_DAEGU_CN_HCM_TPHCM = {"fullname": "Ngân hàng Daegu - CN TP Hồ Chí Minh", "bankcode": "79668001", "sub_name": "NGÂN HÀNG DAEGU - CN TP HỒ CHÍ MINH"}
39 | self.VIKKI_BANK = {"fullname": "Ngân hàng Trách nhiệm hữu hạn Một thành viên số Vikki", "bankcode": "970406", "sub_name": "NGÂN HÀNG TRÁCH NHIỆM HỮU HẠN MỘT THÀNH VIÊN SỐ VIKKI"}
40 | self.ESB = {"fullname": "Ngân hàng E.SUN BANK", "bankcode": "75658001", "sub_name": "NGÂN HÀNG E.SUN BANK"}
41 | self.EXIMBANK = {"fullname": "Ngân hàng Xuất Nhập khẩu", "bankcode": "970431", "sub_name": "NGÂN HÀNG XUẤT NHẬP KHẨU"}
42 | self.FIRSTBANK_HN = {"fullname": "Ngân hàng First Commercial Bank - CN Hà Nội", "bankcode": "01608001", "sub_name": "NGÂN HÀNG FIRST COMMERCIAL BANK - CN HÀ NỘI"}
43 | self.FIRSTBANK_TPHCM = {"fullname": "Ngân hàng First Commercial Bank - CN TP Hồ Chí Minh", "bankcode": "79630001", "sub_name": "NGÂN HÀNG FIRST COMMERCIAL BANK - CN TP HỒ CHÍ MINH"}
44 | self.GPBANK = {"fullname": "Ngân hàng Dầu khí toàn cầu", "bankcode": "970408", "sub_name": "NGÂN HÀNG DẦU KHÍ TOÀN CẦU"}
45 | self.HANOI_ABC_HN = {"fullname": "Ngân hàng Agricultural Bank of China Limited - CN Hà Nội", "bankcode": "01664001", "sub_name": "NGÂN HÀNG AGRICULTURAL BANK OF CHINA LIMITED - CN HÀ NỘI"}
46 | self.HD_BANK = {"fullname": "Ngân hàng Phát triển TP.HCM", "bankcode": "970437", "sub_name": "NGÂN HÀNG PHÁT TRIỂN TP.HCM"}
47 | self.HLBVN = {"fullname": "Ngân hàng HongLeong Việt Nam", "bankcode": "970442", "sub_name": "NGÂN HÀNG HONGLEONG VIỆT NAM"}
48 | self.HSBC = {"fullname": "Ngân hàng TNHH MTV HSBC Việt Nam", "bankcode": "458761", "sub_name": "NGÂN HÀNG TNHH MTV HSBC VIỆT NAM"}
49 | self.HUA_NAN_TPHCM = {"fullname": "Ngân hàng Hua Nam Commercial Bank, Ltd - CN TP Hồ Chí Minh", "bankcode": "79640001", "sub_name": "NGÂN HÀNG HUA NAM COMMERCIAL BANK, LTD - CN TP HỒ CHÍ MINH"}
50 | self.IBK_BANK_HN_HN = {"fullname": "Ngân hàng Công nghiệp Hàn Quốc - CN Hà Nội", "bankcode": "970455", "sub_name": "NGÂN HÀNG CÔNG NGHIỆP HÀN QUỐC - CN HÀ NỘI"}
51 | self.IBK_HCM_TPHCM = {"fullname": "Ngân hàng Công nghiệp Hàn Quốc - CN TP Hồ Chí Minh", "bankcode": "970456", "sub_name": "NGÂN HÀNG CÔNG NGHIỆP HÀN QUỐC - CN TP HỒ CHÍ MINH"}
52 | self.ICBC = {"fullname": "Ngân hàng Công Thương Trung Quốc", "bankcode": "01649001", "sub_name": "NGÂN HÀNG CÔNG THƯƠNG TRUNG QUỐC"}
53 | self.IVB = {"fullname": "\xa0Ngân hàng TNHH Indovina", "bankcode": "970434", "sub_name": "\xa0NGÂN HÀNG TNHH INDOVINA"}
54 | self.J_P_MORGAN = {"fullname": "Ngân hàng JPMorgan Chase, N.A.", "bankcode": "79627001", "sub_name": "NGÂN HÀNG JPMORGAN CHASE, N.A."}
55 | self.KB_HCM_TPHCM = {"fullname": "Ngân hàng Ngân hàng Kookmin - CN TP Hồ Chí Minh", "bankcode": "970463", "sub_name": "NGÂN HÀNG NGÂN HÀNG KOOKMIN - CN TP HỒ CHÍ MINH"}
56 | self.KB_HN_HN = {"fullname": "Ngân hàng Kookmin - CN Hà Nội", "bankcode": "970462", "sub_name": "NGÂN HÀNG KOOKMIN - CN HÀ NỘI"}
57 | self.KBANK_TPHCM = {"fullname": "Ngân hàng đại chúng TNHH KASIKORNBANK - CN TP Hồ Chí Minh", "bankcode": "79669001", "sub_name": "NGÂN HÀNG ĐẠI CHÚNG TNHH KASIKORNBANK - CN TP HỒ CHÍ MINH"}
58 | self.KEB_HANA_HN = {"fullname": "Ngân hàng KEB HANA – CN Hà Nội", "bankcode": "970467", "sub_name": "NGÂN HÀNG KEB HANA – CN HÀ NỘI"}
59 | self.KEB_HANA_TPHCM = {"fullname": "Ngân hàng KEB HANA - CN TP Hồ Chí Minh", "bankcode": "79656001", "sub_name": "NGÂN HÀNG KEB HANA - CN TP HỒ CHÍ MINH"}
60 | self.KIEN_LONG_BANK = {"fullname": "Ngân hàng Kiên Long", "bankcode": "970452", "sub_name": "NGÂN HÀNG KIÊN LONG"}
61 | self.LPBANK = {"fullname": "Ngân hàng TMCP Lộc Phát Việt Nam", "bankcode": "970449", "sub_name": "NGÂN HÀNG TMCP LỘC PHÁT VIỆT NAM"}
62 | self.LIOBANK = {"fullname": "Ngân hàng số Liobank – Ngân hàng TMCP Phương Đông", "bankcode": "963369", "sub_name": "NGÂN HÀNG SỐ LIOBANK – NGÂN HÀNG TMCP PHƯƠNG ĐÔNG"}
63 | self.MAFC = {"fullname": "Công ty Tài chính TNHH MTV Mirae Asset Việt Nam", "bankcode": "977777", "sub_name": "CÔNG TY TÀI CHÍNH TNHH MTV MIRAE ASSET VIỆT NAM"}
64 | self.MB = {"fullname": "Ngân hàng Quân Đội", "bankcode": "970422", "sub_name": "NGÂN HÀNG QUÂN ĐỘI"}
65 | self.MAYBANK_HANOI_HN = {"fullname": "Ngân hàng Malayan Banking Berhad - CN Hà Nội", "bankcode": "01609001", "sub_name": "NGÂN HÀNG MALAYAN BANKING BERHAD - CN HÀ NỘI"}
66 | self.MAYBANK_HCMC = {"fullname": "Ngân hàng Malayan Banking Berhad TP. Hồ Chí Minh", "bankcode": "79635001", "sub_name": "NGÂN HÀNG MALAYAN BANKING BERHAD TP. HỒ CHÍ MINH"}
67 | self.MEGA_ICBC_TPHCM = {"fullname": "Ngân hàng Mega International Commercial Bank Co., Ltd - CN TP Hồ Chí Minh", "bankcode": "79623001", "sub_name": "NGÂN HÀNG MEGA INTERNATIONAL COMMERCIAL BANK CO., LTD - CN TP HỒ CHÍ MINH"}
68 | self.MIZUHO_HN = {"fullname": "Ngân hàng Mizuho Bank, LTd - Chi nhánh Hà Nội", "bankcode": "01613001", "sub_name": "NGÂN HÀNG MIZUHO BANK, LTD - CHI NHÁNH HÀ NỘI"}
69 | self.MIZUHO = {"fullname": "Ngân hàng Mizuho Bank, LTd", "bankcode": "79639001", "sub_name": "NGÂN HÀNG MIZUHO BANK, LTD"}
70 | self.MSB = {"fullname": "Ngân hàng Hàng Hải", "bankcode": "970426", "sub_name": "NGÂN HÀNG HÀNG HẢI"}
71 | self.MUFG_TPHCM = {"fullname": "Ngân hàng MUFG Bank. Ltd - CN TP Hồ Chí Minh", "bankcode": "79622001", "sub_name": "NGÂN HÀNG MUFG BANK. LTD - CN TP HỒ CHÍ MINH"}
72 | self.MUFG_HN = {"fullname": "Ngân hàng MUFG Bank. Ltd – Chi Nhánh Hà Nội", "bankcode": "01653001", "sub_name": "NGÂN HÀNG MUFG BANK. LTD – CHI NHÁNH HÀ NỘI"}
73 | self.NAM_A_BANK = {"fullname": "Ngân hàng Nam Á", "bankcode": "970428", "sub_name": "NGÂN HÀNG NAM Á"}
74 | self.NAPAS = {"fullname": "Công ty Cổ phần Thanh toán Quốc gia Việt Nam", "bankcode": "971100", "sub_name": "CÔNG TY CỔ PHẦN THANH TOÁN QUỐC GIA VIỆT NAM"}
75 | self.NCB = {"fullname": "Ngân hàng Quốc Dân", "bankcode": "970419", "sub_name": "NGÂN HÀNG QUỐC DÂN"}
76 | self.NONGHUYP_HN = {"fullname": "Ngân hàng Nonghuyp - Chi nhánh Hà Nội", "bankcode": "801011", "sub_name": "NGÂN HÀNG NONGHUYP - CHI NHÁNH HÀ NỘI"}
77 | self.OCB = {"fullname": "Ngân hàng Phương Đông", "bankcode": "970448", "sub_name": "NGÂN HÀNG PHƯƠNG ĐÔNG"}
78 | self.OCBC_TPHCM = {"fullname": "Ngân hàng Oversea-Chinese Banking Corporation LTD - CN TP Hồ Chí Minh", "bankcode": "79625001", "sub_name": "NGÂN HÀNG OVERSEA-CHINESE BANKING CORPORATION LTD - CN TP HỒ CHÍ MINH"}
79 | self.MBV = {"fullname": "NH TNHH MTV Việt Nam Hiện Đại", "bankcode": "970414", "sub_name": "NH TNHH MTV VIỆT NAM HIỆN ĐẠI"}
80 | self.PBVN = {"fullname": "Ngân hàng TNHH MTV Public Viet Nam", "bankcode": "970439", "sub_name": "NGÂN HÀNG TNHH MTV PUBLIC VIET NAM"}
81 | self.PGBANK = {"fullname": "Ngân hàng TMCP Thịnh vượng và Phát triển (PGBANK)", "bankcode": "970430", "sub_name": "NGÂN HÀNG TMCP THỊNH VƯỢNG VÀ PHÁT TRIỂN (PGBANK)"}
82 | self.PVCOMBANK = {"fullname": "Ngân hàng Đại chúng", "bankcode": "970412", "sub_name": "NGÂN HÀNG ĐẠI CHÚNG"}
83 | self.SACOMBANK = {"fullname": "Ngân hàng Sài Gòn thương tín", "bankcode": "970403", "sub_name": "NGÂN HÀNG SÀI GÒN THƯƠNG TÍN"}
84 | self.SAIGONBANK = {"fullname": "Ngân hàng Sài Gòn Công Thương", "bankcode": "970400", "sub_name": "NGÂN HÀNG SÀI GÒN CÔNG THƯƠNG"}
85 | self.SCB = {"fullname": "Ngân hàng Sài Gòn", "bankcode": "970429", "sub_name": "NGÂN HÀNG SÀI GÒN"}
86 | self.SCB_TPHCM = {"fullname": "Ngân hàng The Siam Commercial Bank Public Company Limited - CN TP Hồ Chí Minh", "bankcode": "79600001", "sub_name": "NGÂN HÀNG THE SIAM COMMERCIAL BANK PUBLIC COMPANY LIMITED - CN TP HỒ CHÍ MINH"}
87 | self.SCVN = {"fullname": "Ngân hàng TNHH MTV Standard Chartered Việt Nam", "bankcode": "970410", "sub_name": "NGÂN HÀNG TNHH MTV STANDARD CHARTERED VIỆT NAM"}
88 | self.SEABANK = {"fullname": "Ngân hàng Đông Nam Á", "bankcode": "970440", "sub_name": "NGÂN HÀNG ĐÔNG NAM Á"}
89 | self.SHB_HN = {"fullname": "Ngân hàng Sài Gòn Hà Nội", "bankcode": "970443", "sub_name": "NGÂN HÀNG SÀI GÒN HÀ NỘI"}
90 | self.SHINHAN = {"fullname": "Ngân hàng Shinhan Bank Việt Nam", "bankcode": "970424", "sub_name": "NGÂN HÀNG SHINHAN BANK VIỆT NAM"}
91 | self.SMBC = {"fullname": "Ngân hàng Sumitomo Mitsui banking corporation", "bankcode": "01636001", "sub_name": "NGÂN HÀNG SUMITOMO MITSUI BANKING CORPORATION"}
92 | self.SVFC = {"fullname": "Công ty Tài chính TNHH MTV Shinhan Việt Nam", "bankcode": "963368", "sub_name": "CÔNG TY TÀI CHÍNH TNHH MTV SHINHAN VIỆT NAM"}
93 | self.TAIPEI_FUBON = {"fullname": "Ngân hàng Hua Nam Commercial Bank, Ltd – Chi nhánh Bình Dương", "bankcode": "74655001", "sub_name": "NGÂN HÀNG HUA NAM COMMERCIAL BANK, LTD – CHI NHÁNH BÌNH DƯƠNG"}
94 | self.TAIPEI_FUBON_TPHCM = {"fullname": "Ngân hàng Taipei Fubon Commercial Bank Co., Ltd - CN TP Hồ Chí Minh", "bankcode": "79651001", "sub_name": "NGÂN HÀNG TAIPEI FUBON COMMERCIAL BANK CO., LTD - CN TP HỒ CHÍ MINH"}
95 | self.TAIPEI_FUBONC_B = {"fullname": "Ngân hàng TAIPEI FUBON", "bankcode": "01642001", "sub_name": "NGÂN HÀNG TAIPEI FUBON"}
96 | self.TECHCOMBANK = {"fullname": "Ngân hàng Kỹ thương Việt Nam", "bankcode": "970407", "sub_name": "NGÂN HÀNG KỸ THƯƠNG VIỆT NAM"}
97 | self.TPBANK = {"fullname": "Ngân hàng Tiên phong", "bankcode": "970423", "sub_name": "NGÂN HÀNG TIÊN PHONG"}
98 | self.UBANK = {"fullname": "Ngân hàng UBANK BY VPBANK", "bankcode": "546035", "sub_name": "NGÂN HÀNG UBANK BY VPBANK"}
99 | self.UMEE = {"fullname": "Ngân hàng số UMEE by Kienlongbank", "bankcode": "963399", "sub_name": "NGÂN HÀNG SỐ UMEE BY KIENLONGBANK"}
100 | self.UOB_VIETNAM = {"fullname": "Ngân hàng TNHH MTV UOB Việt Nam", "bankcode": "970458", "sub_name": "NGÂN HÀNG TNHH MTV UOB VIỆT NAM"}
101 | self.VBSP = {"fullname": "Ngân hàng Chính sách Xã hội VBSP", "bankcode": "999888", "sub_name": "NGÂN HÀNG CHÍNH SÁCH XÃ HỘI VBSP"}
102 | self.VDB = {"fullname": "Ngân hàng Phát triển Việt Nam", "bankcode": "01208001", "sub_name": "NGÂN HÀNG PHÁT TRIỂN VIỆT NAM"}
103 | self.VIB = {"fullname": "Ngân hàng Quốc tế", "bankcode": "970441", "sub_name": "NGÂN HÀNG QUỐC TẾ"}
104 | self.VIET_A_BANK = {"fullname": "Ngân hàng Việt Á", "bankcode": "970427", "sub_name": "NGÂN HÀNG VIỆT Á"}
105 | self.VIET_CAPITAL_BANK = {"fullname": "Ngân hàng Bản Việt", "bankcode": "970454", "sub_name": "NGÂN HÀNG BẢN VIỆT"}
106 | self.VIETBANK = {"fullname": "Ngân hàng Việt Nam Thương Tín", "bankcode": "970433", "sub_name": "NGÂN HÀNG VIỆT NAM THƯƠNG TÍN"}
107 | self.VIETTEL_MONEY = {"fullname": "Viettel Money", "bankcode": "971005", "sub_name": "VIETTEL MONEY"}
108 | self.VINASIAM_BANK = {"fullname": "Ngân hàng liên doanh Việt Thái - Chi nhánh Đồng Nai", "bankcode": "75504001", "sub_name": "NGÂN HÀNG LIÊN DOANH VIỆT THÁI - CHI NHÁNH ĐỒNG NAI"}
109 | self.VNPT_MONEY = {"fullname": "VNPT Money", "bankcode": "971011", "sub_name": "VNPT MONEY"}
110 | self.VPBANK = {"fullname": "Ngân hàng Việt Nam Thịnh Vượng", "bankcode": "970432", "sub_name": "NGÂN HÀNG VIỆT NAM THỊNH VƯỢNG"}
111 | self.WOORI_BANK = {"fullname": "Ngân hàng Woori Việt Nam", "bankcode": "970457", "sub_name": "NGÂN HÀNG WOORI VIỆT NAM"}
112 | self.WOORI_BANK_VIETNAM_TPHCM = {"fullname": "Ngân hàng TNHH MTV Woori Việt Nam - CN TP Hồ Chí Minh", "bankcode": "79637001", "sub_name": "NGÂN HÀNG TNHH MTV WOORI VIỆT NAM - CN TP HỒ CHÍ MINH"}
113 | self.SCSB = {"fullname": "Ngân hàng The Shanghai Commercial & Savings Bank. Ltđ – CN Đồng Nai", "bankcode": "75606001", "sub_name": "NGÂN HÀNG THE SHANGHAI COMMERCIAL & SAVINGS BANK. LTĐ – CN ĐỒNG NAI"}
114 | self.VIKKI_BY_HDBANK = {"fullname": "Kênh số hóa Vikki – trực thuộc HDBank", "bankcode": "963311", "sub_name": "KÊNH SỐ HÓA VIKKI – TRỰC THUỘC HDBANK"}
115 | self.PVCOMBANK_PAY = {"fullname": "Ngân hàng số PVcombank", "bankcode": "971133", "sub_name": "NGÂN HÀNG SỐ PVCOMBANK"}
116 |
117 |
--------------------------------------------------------------------------------
/utils/captcha_solving.py:
--------------------------------------------------------------------------------
1 | from ._algorithm import *
2 |
3 | def gen_captcha_token(e: int) -> str:
4 | t = ""
5 | for _ in range(e):
6 | rand = int(65536 * (1 + os.urandom(1)[0] / 255))
7 | hex_str = hex(rand)[2:].zfill(4)[1:]
8 | t += hex_str
9 | return t
10 |
11 | def create_guid() -> str:
12 | return "-".join([
13 | gen_captcha_token(2),
14 | gen_captcha_token(1),
15 | gen_captcha_token(1),
16 | gen_captcha_token(1),
17 | gen_captcha_token(3)
18 | ])
19 |
20 |
21 |
22 | class Captcha:
23 | def __init__(self, captcha_guid: str, proxies: dict = {}) -> None:
24 | self.guid = captcha_guid
25 | self.session = requests.Session()
26 | if proxies:
27 | self.session.proxies.update(
28 | {
29 | "http": proxies.get("http"),
30 | "https": proxies.get("https")
31 | }
32 | )
33 | self.session.headers.update(self._headers())
34 |
35 | def _headers(self) -> dict:
36 | return {
37 | 'accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
38 | 'accept-language': 'vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5',
39 | 'cache-control': 'no-cache',
40 | 'dnt': '1',
41 | 'pragma': 'no-cache',
42 | 'priority': 'u=1, i',
43 | 'referer': 'https://vcbdigibank.vietcombank.com.vn/',
44 | 'sec-ch-ua': '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
45 | 'sec-ch-ua-mobile': '?0',
46 | 'sec-ch-ua-platform': '"Windows"',
47 | 'sec-fetch-dest': 'image',
48 | 'sec-fetch-mode': 'no-cors',
49 | 'sec-fetch-site': 'same-site',
50 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
51 | }
52 |
53 | def solving(self):
54 | url = "https://hngl2808-predict.hf.space/predict"
55 | images = self.session.get(
56 | f"https://digiapp.vietcombank.com.vn/utility-service/v2/captcha/MASS/{self.guid}"
57 | ).content
58 | b64encimg = base64.b64encode(images).decode("utf-8")
59 | js = {"data": f"data:image/jpeg;base64,{b64encimg}"}
60 | response = self.session.post(url, json=js)
61 | if response.status_code == 200:
62 | return response.json()["result"]
63 | else:
64 | print("Error:", response.status_code, response.text)
65 | return None
66 |
--------------------------------------------------------------------------------