Hệ thống mô phỏng thanh toán học phí theo kiến trúc hướng dịch vụ (SOA).
Sinh viên A có thể thanh toán cho chính mình hoặc cho người khác (C).
Frontend là các trang HTML/JS thuần; backend là các service Node.js + SQL Server (MSSQL).
[Browser] --(HTTP)-> [API Gateway/Reverse Proxy (optional)]
| |
| +--> [Auth-Service] -> SQL Server (auth schema)
| +--> [Tuition-Service] -> SQL Server (students schema)
| +--> [Payment-Service] -> SQL Server (payments schema)
| +--> [Account-Ledger-Service] -> SQL Server (ledger schema)
| +--> [Email-Service] (SMTP)
Điểm mấu chốt về bảo mật phiên:
- Frontend mặc định không gửi cookie (
credentials: "omit") và chỉ dùngAuthorization: Bearer <token>. - “Khoá danh tính người nộp” bằng
payerSnapshottrongsessionStorageđể tránh “đánh tráo phiên” sau khi A thanh toán cho C.
- Node.js (Express)
- SQL Server (MSSQL) — chạy qua Docker hoặc local
- DBeaver / SSMS — quản trị cơ sở dữ liệu
- SMTP (Gmail/any) — gửi OTP / log email (tuỳ cấu hình)
- Vanilla HTML/CSS/JS — các trang
payment.html,login.html, …
.
├─ services/
│ ├─ auth-service/
│ ├─ tuition-service/
│ ├─ payment-service/
│ ├─ account-ledger-service/
│ └─ email-service/
├─ public/ # các trang HTML, css, js (payment.js, login.js, ...)
├─ scripts/ # seed, migrate, util
├─ docker-compose.yml
├─ .env # giá trị thật (không commit)
├─ .env.example # mẫu biến môi trường (được commit)
└─ README.md
Tạo file .env từ mẫu .env.example:
cp .env.example .envCác biến tối thiểu:
PORT,NODE_ENV,APP_URLDB_SERVER,DB_USER,DB_PASS,DB_NAME,DB_PORTJWT_SECRET,JWT_EXPIRES_INCORS_ORIGIN(ví dụ:http://localhost:4000)- SMTP (nếu dùng Email-Service)
Gợi ý: tạo
JWT_SECRETbằngopenssl rand -hex 32.
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=YourStrong!Passw0rd" \
-p 1433:1433 --name mssql -d mcr.microsoft.com/mssql/server:2019-latest- Host:
localhost - Port:
1433 - User:
sa - Pass:
YourStrong!Passw0rd
Import script schema/seed (nếu có) tại scripts/.
Mỗi service một terminal:
cd services/auth-service && npm i && npm run dev
cd services/tuition-service && npm i && npm run dev
cd services/payment-service && npm i && npm run dev
cd services/account-ledger-service && npm i && npm run dev
cd services/email-service && npm i && npm run devHoặc dùng docker-compose (nếu đã cấu hình sẵn):
docker compose up -d --buildDùng bất kì static server nào (hoặc service “web” của bạn).
Ví dụ:
npx serve public -l 4000
# Mở: http://localhost:4000/pages/login.htmlA đăng nhập → Payment
GET /api/auth/me→ tạopayerSnapshot(lock người nộp = A).- Nhập MSSV của C →
GET /api/tuition/{mssv}/semesters→GET /api/tuition/{mssv}/{semester}. POST /api/payments/intent→ tạopayment_intent(payer = A, payee = C).POST /api/payments/verify-otp(nếu có OTP).POST /api/payments/confirm→ trừ tiền A, cập nhật hoá đơn C, ghi ledger.- Dọn biến tạm của C, giữ
payerSnapshotcủa A → quay lạipayment.htmlvẫn là A.
Tất cả request kèm Bearer token của A.
mssvcủa C chỉ là tham số nghiệp vụ, không “đăng nhập hộ C”.
- Auth-Service
POST /api/auth/login— trả JWTGET /api/auth/me— thông tin người đang đăng nhập
- Tuition-Service
GET /api/tuition/:mssv/semestersGET /api/tuition/:mssv/:semester— hoá đơn, môn nợ
- Payment-Service
POST /api/payments/intentPOST /api/payments/verify-otpPOST /api/payments/confirmPOST /api/payments/cancel— huỷ khi OTP hết hạn
- Ledger-Service
GET /api/ledger/history?account_id=...— lịch sử bút toán
- Email-Service
POST /api/email/send(nếu cần)
- Bearer-only fetch: wrapper
authFetchmặc địnhcredentials: "omit", chỉ bậtuseCookies: truekhi thật sự cần cookie cũ. - Khoá danh tính người nộp:
- Lúc vào
payment.html: gọi/api/auth/me→setPayerSnapshot(userA). - Trước khi tạo intent/verify/confirm:
ensurePayerIdentity(meNow); mismatch ⇒ ép quay lại login. - Sau khi confirm thành công:
cleanupPaymentTemp()(giữ nguyên snapshot A).
- Lúc vào
- Backend: Các endpoint của Tuition/Payment/OTP không được
Set-Cookie: token=.... Chỉ Auth-Service mới phát hành cookie (nếu dùng cookie), hoặc tốt nhất chỉ dùng JWT Bearer.
ENOTFOUNDkhi proxy tớiauth-service:4001
→ Kiểm tradocker-composenetwork name, service name; hoặc dùng URL tuyệt đốihttp://localhost:4001.ports are not available ... 0.0.0.0:4004
→ Port đã bị chiếm. Đổi port hoặc stop tiến trình đang dùng (netstat/lsof), sửadocker-compose.yml.Cannot find module '../db/mssql'
→ Sai đường dẫnrequire. Đặt moduledb/mssql.jsđúng vị trí, hoặc sửarequirecho phù hợp.bcrypt.compareluôn false
→ Lệch lib (bcryptvsbcryptjs) hoặc salt/round khác. Dùng một lib thống nhất & re-hash dữ liệu seed cho đúng.- Sau khi A thanh toán cho C, quay lại bị thành C
→ Đảm bảo frontend đã Bearer-only + payerSnapshot; backend không set cookie ở các API thanh toán.
- Seed/migrate (tuỳ dự án):
node scripts/seed.jshoặc.sqltrongscripts/ - Lint/format:
npm run lint/npm run format - Dev:
npm run devcho từng service
- A đăng nhập → vào
payment.htmllần đầu: snapshot được tạo. - A thanh toán cho C thành công → quay lại
payment.html: UI vẫn là A. - Giả lập đổi phiên (sửa token thành C) trước khi confirm → hệ thống chặn, điều hướng về login.
- Kiểm tra Ledger: bút toán debit A, credit tuition/invoice của C chính xác.
- Kiểm tra Email/OTP (nếu bật): log gửi thành công, không leak token.
Internal coursework / learning project.