Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
19 KiB
19 KiB
Kiểm Tra Bảo Mật Module Thanh Toán GoodGo Platform - Danh Sách File
Tổng Quan
Danh sách file toàn diện cho đợt kiểm tra bảo mật các thực thể Order & Escrow trong module thanh toán.
Vị trí: /Users/velikho/Desktop/WORKING/goodgo-platform-ai/apps/api/src/modules/payments/
1. TẦNG DOMAIN - CÁC THỰC THỂ
Các Thực Thể Cốt Lõi
| File | Mô tả |
|---|---|
domain/entities/order.entity.ts |
THỰC THỂ ORDER - Quản lý vòng đời đơn hàng với máy trạng thái (CREATED→PAYMENT_PENDING→PAYMENT_CONFIRMED→ESCROW_HELD→SHIPPED→DELIVERED→ESCROW_RELEASED→COMPLETED). Xác thực các chuyển trạng thái. Phát sự kiện: OrderCreatedEvent, OrderPaidEvent, OrderCancelledEvent. Các trường quan trọng: buyerId, sellerId, listingId, amount (Money VO), platformFee, sellerPayout. |
domain/entities/escrow.entity.ts |
THỰC THỂ ESCROW - Quản lý vòng đời escrow (PENDING→HELD→RELEASED/DISPUTED/REFUNDED). Lưu trữ số tiền escrow, phí và netPayout đã tính. Phát: EscrowHeldEvent, EscrowReleasedEvent, EscrowDisputedEvent. Xác thực chuyển trạng thái. |
domain/entities/payment.entity.ts |
THỰC THỂ PAYMENT - Quản lý giao dịch thanh toán (PENDING→PROCESSING→COMPLETED/FAILED/REFUNDED). Lưu trữ userId, provider (VNPAY/MOMO/ZALOPAY), type, amount, callbackData, idempotencyKey. Phát: PaymentCreatedEvent, PaymentCompletedEvent, PaymentFailedEvent, PaymentRefundedEvent. |
Các Value Object
| File | Mô tả |
|---|---|
domain/value-objects/money.vo.ts |
VALUE OBJECT MONEY - Bọc các số tiền dưới dạng bigint theo VND. Xác thực: amount > 0, giới hạn tối đa 999_999_999_999. Dùng cho tất cả các số tiền tài chính. |
domain/value-objects/platform-fee.vo.ts |
VALUE OBJECT PHÍ NỀN TẢNG - Tính phí nền tảng 5%. Các phương thức: fromOrderAmount() (tự động tính 5%), create() (số tiền tường minh). Xác thực fee >= 0. |
2. TẦNG DOMAIN - REPOSITORY (Giao Diện)
| File | Mô tả |
|---|---|
domain/repositories/order.repository.ts |
GIAO DIỆN ORDER REPOSITORY - Định nghĩa các phương thức CRUD + truy vấn: findById, findByIdempotencyKey, findByBuyerId, findBySellerId, save, update. Bảo vệ idempotency. |
domain/repositories/escrow.repository.ts |
GIAO DIỆN ESCROW REPOSITORY - Định nghĩa: findById, findByOrderId, save, update. Quan hệ một escrow trên một order. |
domain/repositories/payment.repository.ts |
GIAO DIỆN PAYMENT REPOSITORY - Định nghĩa: findById, findByProviderTxId, findByIdempotencyKey, findByUserId, save, update, updateIfStatus (cập nhật có điều kiện nguyên tử để xử lý race condition). |
3. TẦNG DOMAIN - SỰ KIỆN
| File | Mô tả |
|---|---|
domain/events/order-created.event.ts |
Phát khi tạo order - chứa orderId, buyerId, sellerId, listingId, amount |
domain/events/order-paid.event.ts |
Phát khi thanh toán được xác nhận - chứa orderId, buyerId, amount |
domain/events/order-cancelled.event.ts |
Phát khi order bị hủy - chứa orderId, buyerId, sellerId |
domain/events/escrow-held.event.ts |
Phát khi escrow được giữ - chứa escrowId, orderId, amount |
domain/events/escrow-released.event.ts |
Phát khi escrow được giải phóng - chứa escrowId, orderId, netPayout |
domain/events/escrow-disputed.event.ts |
Phát khi escrow bị tranh chấp - chứa escrowId, orderId, reason |
domain/events/payment-created.event.ts |
Phát khi tạo thanh toán - chứa paymentId, userId, provider, amount |
domain/events/payment-completed.event.ts |
Phát khi thanh toán hoàn tất - chứa paymentId, userId, provider |
domain/events/payment-failed.event.ts |
Phát khi thanh toán thất bại - chứa paymentId, userId, provider |
domain/events/payment-refunded.event.ts |
Phát khi thanh toán được hoàn tiền - chứa paymentId, userId, provider, amount |
4. TẦNG CƠ SỞ HẠ TẦNG - REPOSITORY (Triển Khai)
| File | Mô tả |
|---|---|
infrastructure/repositories/prisma-order.repository.ts |
TRIỂN KHAI ORDER REPOSITORY - Triển khai Prisma ORM. Lưu trữ: id, buyerId, sellerId, listingId, status, amountVND, platformFeeVND, sellerPayoutVND, idempotencyKey, metadata. Xử lý lưu trữ order. |
infrastructure/repositories/prisma-escrow.repository.ts |
TRIỂN KHAI ESCROW REPOSITORY - Triển khai Prisma ORM. Lưu trữ: id, orderId, amountVND, feeVND, status, heldAt, releasedAt, disputeReason, disputedAt. Xử lý lưu trữ escrow. |
infrastructure/repositories/prisma-payment.repository.ts |
TRIỂN KHAI PAYMENT REPOSITORY - Prisma ORM. Lưu trữ: id, userId, transactionId, provider, type, amountVND, status, providerTxId, callbackData, idempotencyKey. QUAN TRỌNG: updateIfStatus() sử dụng mệnh đề WHERE có điều kiện để ngăn race condition nguyên tử (Dòng 84-109). |
5. TẦNG CƠ SỞ HẠ TẦNG - DỊCH VỤ CỔNG THANH TOÁN
Giao Diện Cổng Thanh Toán
| File | Mô tả |
|---|---|
infrastructure/services/payment-gateway.interface.ts |
GIAO DIỆN GATEWAY - Định nghĩa hợp đồng IPaymentGateway: createPaymentUrl(), verifyCallback(), refund(). CallbackVerifyResult bao gồm: isValid, orderId, providerTxId, isSuccess, rawData. Nhạy cảm về bảo mật. |
Dịch Vụ VNPay
| File | Mô tả |
|---|---|
infrastructure/services/vnpay.service.ts |
CỔNG THANH TOÁN VNPAY - Triển khai IPaymentGateway. XÁC MINH CALLBACK (Dòng 72-105): Trích xuất secure hash, loại bỏ khỏi dữ liệu, sắp xếp các tham số, tạo HMAC-SHA512, dùng crypto.timingSafeEqual() để so sánh thời gian cố định. Số tiền nhân 100 cho VND cents. Trả về isValid, orderId (vnp_TxnRef), providerTxId (vnp_TransactionNo), isSuccess (responseCode === '00'). Hỗ trợ hoàn tiền. |
Dịch Vụ MoMo
| File | Mô tả |
|---|---|
infrastructure/services/momo.service.ts |
CỔNG THANH TOÁN MOMO - Triển khai IPaymentGateway. XÁC MINH CALLBACK (Dòng 102-147): Trích xuất chữ ký từ dữ liệu, xây dựng lại chữ ký thô với accessKey, amount, extraData, IPN/redirect URLs, orderId, v.v. Dùng HMAC-SHA256, so sánh thời gian cố định qua crypto.timingSafeEqual(). Kiểm tra thành công: resultCode === '0'. Hỗ trợ hoàn tiền. Amount dưới dạng Number (không phải bigint trong API). |
Dịch Vụ ZaloPay
| File | Mô tả |
|---|---|
infrastructure/services/zalopay.service.ts |
CỔNG THANH TOÁN ZALOPAY - Triển khai IPaymentGateway. XÁC MINH CALLBACK (Dòng 98-144): Dữ liệu được truyền dưới dạng chuỗi JSON trong trường 'data'. MAC được xác minh qua HMAC-SHA256 với key2. Phân tích dữ liệu JSON để trích xuất app_trans_id và zp_trans_id. LƯU Ý BẢO MẬT: Xử lý lỗi phân tích JSON một cách duyên dáng. Dùng so sánh thời gian cố định. Hỗ trợ hoàn tiền (key1). |
Factory Cổng Thanh Toán
| File | Mô tả |
|---|---|
infrastructure/services/payment-gateway.factory.ts |
GATEWAY FACTORY - Trả về instance gateway phù hợp (VNPay/MoMo/ZaloPay) dựa trên enum provider. |
6. TẦNG ỨNG DỤNG - COMMAND
Các Order Command
| File | Mô tả |
|---|---|
application/commands/create-order/create-order.command.ts |
CREATE ORDER COMMAND - Đầu vào: buyerId, sellerId, listingId, amountVND, idempotencyKey. Đối tượng payload. |
application/commands/create-order/create-order.handler.ts |
CREATE ORDER HANDLER - Kiểm tra idempotency qua findByIdempotencyKey. Xác thực amount (Money VO). Tính phí nền tảng (5%) và seller payout. Tạo OrderEntity + EscrowEntity (trạng thái PENDING). Lưu cả hai. Phát sự kiện. |
application/commands/cancel-order/cancel-order.command.ts |
CANCEL ORDER COMMAND - Đầu vào: orderId, userId, reason. |
application/commands/cancel-order/cancel-order.handler.ts |
CANCEL ORDER HANDLER - Xác minh người dùng sở hữu order, xác thực chuyển trạng thái qua entity.markCancelled(), lưu, phát sự kiện. |
Các Escrow Command
| File | Mô tả |
|---|---|
application/commands/hold-escrow/hold-escrow.command.ts |
HOLD ESCROW COMMAND - Đầu vào: orderId. Chỉ dành cho Admin. |
application/commands/hold-escrow/hold-escrow.handler.ts |
HOLD ESCROW HANDLER (Dòng 23-67) - Lấy order + escrow theo orderId. Gọi escrow.hold() để chuyển trạng thái. Cập nhật cả hai thực thể. Phát EscrowHeldEvent. LƯU Ý BẢO MẬT: Không có Redis lock - tiềm ẩn race condition nếu có nhiều yêu cầu đồng thời. |
application/commands/release-escrow/release-escrow.command.ts |
RELEASE ESCROW COMMAND - Đầu vào: orderId. Chỉ dành cho Admin. |
application/commands/release-escrow/release-escrow.handler.ts |
RELEASE ESCROW HANDLER (Dòng 24-45) - Lấy order + escrow theo orderId. Gọi escrow.release() để chuyển trạng thái. Cập nhật cả hai thực thể. Phát EscrowReleasedEvent với netPayout. LƯU Ý BẢO MẬT: Không có Redis lock - tiềm ẩn race condition. |
Các Payment Command
| File | Mô tả |
|---|---|
application/commands/create-payment/create-payment.command.ts |
CREATE PAYMENT COMMAND - Đầu vào: userId, provider, type, amountVND, description, returnUrl, ipAddress, transactionId, idempotencyKey. |
application/commands/create-payment/create-payment.handler.ts |
CREATE PAYMENT HANDLER - Kiểm tra idempotency. Xác thực amount (Money VO). Lấy payment gateway. Gọi createPaymentUrl(). Tạo PaymentEntity (trạng thái PENDING). Lưu. Phát PaymentCreatedEvent. Trả về paymentUrl để frontend chuyển hướng. |
application/commands/refund-payment/refund-payment.command.ts |
REFUND PAYMENT COMMAND - Đầu vào: paymentId, reason, userId. Lệnh dành cho Admin. |
application/commands/refund-payment/refund-payment.handler.ts |
REFUND PAYMENT HANDLER - Xác minh thanh toán tồn tại, gọi gateway.refund() với các tham số đặc thù nhà cung cấp, cập nhật trạng thái thanh toán thành REFUNDED, phát PaymentRefundedEvent. |
Handler Callback (QUAN TRỌNG)
| File | Mô tả |
|---|---|
application/commands/handle-callback/handle-callback.command.ts |
HANDLE CALLBACK COMMAND - Đầu vào: provider (enum PaymentProvider), callbackData (Record<string, string>). |
application/commands/handle-callback/handle-callback.handler.ts |
HANDLE CALLBACK HANDLER (Dòng 32-110) - FILE BẢO MẬT QUAN TRỌNG. Lấy gateway, gọi verifyCallback() (xác thực chữ ký). Nếu không hợp lệ: ném ValidationException. Nếu hợp lệ: Dùng paymentRepo.updateIfStatus() với WHERE có điều kiện ['PENDING', 'PROCESSING'] (Dòng 48-55) - cập nhật nguyên tử để ngăn xử lý trùng lặp. Nếu cập nhật trả về null: kiểm tra xem thanh toán có tồn tại không (đã xử lý - phản hồi idempotent). Nếu thành công: gọi payment.emitCompleted(), ngược lại payment.emitFailed(). Phát sự kiện. BẢO VỆ RACE CONDITION MẠNH qua cập nhật có điều kiện. |
7. TẦNG ỨNG DỤNG - QUERY
| File | Mô tả |
|---|---|
application/queries/get-order-status/get-order-status.query.ts |
Query: Đầu vào orderId, userId (để phân quyền). |
application/queries/get-order-status/get-order-status.handler.ts |
Lấy order, xác minh quyền sở hữu (buyer/seller), trả về trạng thái + chi tiết. |
application/queries/get-payment-status/get-payment-status.query.ts |
Query: Đầu vào paymentId, userId. |
application/queries/get-payment-status/get-payment-status.handler.ts |
Lấy thanh toán, xác minh quyền sở hữu, trả về trạng thái + chi tiết. |
application/queries/list-transactions/list-transactions.query.ts |
Query: Đầu vào userId, status (tùy chọn), limit, offset. |
application/queries/list-transactions/list-transactions.handler.ts |
Liệt kê các thanh toán của người dùng với phân trang, lọc theo status nếu được cung cấp. |
8. TẦNG TRÌNH BÀY - CONTROLLER
| File | Mô tả |
|---|---|
presentation/controllers/orders.controller.ts |
ORDERS CONTROLLER - Các route: POST / (tạo order), GET /:id (trạng thái), POST /:id/cancel (hủy), POST /:id/escrow/hold (admin), POST /:id/escrow/release (admin). Xác thực: JwtAuthGuard, RolesGuard cho các thao tác admin. Chuyển đổi DTO thành command. |
presentation/controllers/payments.controller.ts |
PAYMENTS CONTROLLER - Các route: POST / (tạo thanh toán), POST /callback/:provider (webhook - Throttle + EndpointRateLimit), GET /:id (trạng thái), GET (danh sách), POST /:id/refund (admin hoàn tiền). QUAN TRỌNG: Endpoint callback có giới hạn tốc độ (Throttle + EndpointRateLimitGuard) - ngăn chặn lũ lụt callback. |
9. TẦNG TRÌNH BÀY - DTO
| File | Mô tả |
|---|---|
presentation/dto/create-order.dto.ts |
DTO: sellerId, listingId, amountVND (string), idempotencyKey (tùy chọn). |
presentation/dto/cancel-order.dto.ts |
DTO: reason (string). |
presentation/dto/create-payment.dto.ts |
DTO: provider (enum), type (enum), amountVND (string), description, returnUrl, transactionId (tùy chọn), idempotencyKey (tùy chọn). |
presentation/dto/refund-payment.dto.ts |
DTO: reason (string). |
presentation/dto/list-transactions.dto.ts |
DTO: status (tùy chọn), limit, offset. |
10. MODULE & CÁC FILE KIỂM THỬ
| File | Mô tả |
|---|---|
payments.module.ts |
CÀI ĐẶT MODULE - Đăng ký các repository, service, handler, controller. |
index.ts (cấp module) |
Xuất API công khai. |
infrastructure/repositories/index.ts |
Xuất các triển khai repository. |
infrastructure/services/index.ts |
Xuất các dịch vụ gateway. |
application/index.ts |
Xuất các handler command/query. |
domain/repositories/index.ts |
Xuất các giao diện repository. |
domain/entities/index.ts |
Xuất các thực thể. |
domain/value-objects/index.ts |
Xuất các VO. |
domain/events/index.ts |
Xuất các sự kiện domain. |
presentation/controllers/index.ts |
Xuất các controller. |
presentation/dto/index.ts |
Xuất các DTO. |
Các File Kiểm Thử
| File | Mô tả |
|---|---|
domain/__tests__/order.entity.spec.ts |
Kiểm thử đơn vị thực thể Order - máy trạng thái, các chuyển trạng thái |
domain/__tests__/escrow.entity.spec.ts |
Kiểm thử đơn vị thực thể Escrow - hold, release, dispute, refund |
domain/__tests__/payment.entity.spec.ts |
Kiểm thử đơn vị thực thể Payment |
domain/__tests__/money.vo.spec.ts |
Kiểm thử xác thực Money VO |
domain/__tests__/platform-fee.vo.spec.ts |
Kiểm thử tính toán phí nền tảng |
domain/__tests__/payment-events.spec.ts |
Kiểm thử phát sự kiện domain |
application/__tests__/create-order.handler.spec.ts |
Kiểm thử handler tạo order |
application/__tests__/create-payment.handler.spec.ts |
Kiểm thử handler tạo thanh toán |
application/__tests__/handle-callback.handler.spec.ts |
Kiểm thử xử lý callback |
application/__tests__/handle-callback-edge-cases.handler.spec.ts |
Kiểm thử các trường hợp biên callback (race condition, idempotency) |
application/__tests__/get-payment-status.handler.spec.ts |
Kiểm thử query trạng thái thanh toán |
application/__tests__/refund-payment.handler.spec.ts |
Kiểm thử lệnh hoàn tiền |
application/__tests__/list-transactions.handler.spec.ts |
Kiểm thử query danh sách giao dịch |
infrastructure/__tests__/vnpay.service.spec.ts |
Kiểm thử gateway VNPay - xác minh chữ ký |
infrastructure/__tests__/momo.service.spec.ts |
Kiểm thử gateway MoMo - xác minh HMAC-SHA256 |
infrastructure/__tests__/zalopay.service.spec.ts |
Kiểm thử gateway ZaloPay - phân tích JSON + xác minh MAC |
infrastructure/__tests__/payment-gateway.factory.spec.ts |
Kiểm thử mẫu factory |
TÓM TẮT KẾT QUẢ BẢO MẬT
✅ CÁC BIỆN PHÁP BẢO MẬT MẠNH
- Xác Minh Chữ Ký Callback: Cả 3 nhà cung cấp (VNPay, MoMo, ZaloPay) đều xác minh chữ ký HMAC sử dụng
crypto.timingSafeEqual()để so sánh thời gian cố định - Ngăn Chặn Race Condition Nguyên Tử:
paymentRepo.updateIfStatus()sử dụng mệnh đề WHERE có điều kiện để cập nhật nguyên tử chỉ khi ở trạng thái PENDING/PROCESSING - Bảo Vệ Idempotency: Order + Payment kiểm tra idempotencyKey để ngăn chặn các thao tác trùng lặp
- Giới Hạn Tốc Độ: Endpoint callback có decorator Throttle + EndpointRateLimit
- Phân Quyền: Tất cả endpoint yêu cầu JwtAuthGuard; các thao tác admin yêu cầu RolesGuard
- Xác Thực Số Tiền: Money VO xác thực: 0 < amount ≤ 999_999_999_999 VND
- Xác Thực Máy Trạng Thái: Order + Escrow thực thi các chuyển trạng thái hợp lệ
⚠️ VẤN ĐỀ BẢO MẬT (CẦN KIỂM TRA)
- Race Condition Hold/Release Escrow: Không có Redis lock trên các handler hold-escrow/release-escrow - các yêu cầu đồng thời có thể gây ra sự không nhất quán trạng thái
- Không Có Cơ Chế Khóa Phân Tán: Các thao tác escrow không được bảo vệ trước các yêu cầu đồng thời từ các máy chủ khác nhau
- Idempotency Xử Lý Callback: Mặc dù paymentRepo.updateIfStatus() ngăn xử lý hai lần, kiểm tra idempotency không xác minh tính nhất quán của chữ ký callback
- Bí Mật Nhà Cung Cấp Thanh Toán: Các khóa được tải từ ConfigService - xác minh mã hóa biến môi trường khi lưu trữ
- Phân Quyền Hoàn Tiền: Chỉ kiểm tra vai trò ADMIN - không có xác thực logic nghiệp vụ (ví dụ: thời hạn hoàn tiền, số tiền hoàn tiền tối đa)
- Race Update Order/Escrow: Mặc dù hold là nguyên tử đối với thanh toán, cập nhật order + escrow trong handler được thực hiện tuần tự (2 lần gọi DB), không nguyên tử
CÁC FILE KHÔNG TÌM THẤY / NGOÀI PHẠM VI
- ❌ Sử Dụng Redis Lock: Không tìm thấy Redis lock trong module thanh toán. MỐI LO NGẠI: Quan trọng cho escrow hold/release.
- ❌ Tiện Ích Thanh Toán Dùng Chung: Không có module tiện ích thanh toán bên ngoài nào được tham chiếu
- ❌ Mã Hóa Dữ Liệu Thanh Toán: Không có mã hóa cấp trường cho dữ liệu thanh toán nhạy cảm (mặc dù dịch vụ field-encryption tồn tại trong module shared)