├── .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 | --------------------------------------------------------------------------------