Files
goodgo-platform/docs/audits/PRICING_AUDIT_SUMMARY.md
Ho Ngoc Hai 11f2bf26e6
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
chore: update project documentation, audit reports, and initialize IDE configuration files
2026-04-19 03:12:54 +07:00

487 lines
17 KiB
Markdown

# Tóm Tắt Kiểm Tra Định Giá → Thanh Toán GoodGo
## 🎯 Tổng Quan Nhanh
| Khía cạnh | Trạng thái | Chi tiết chính |
|--------|--------|-------------|
| **Trang Định Giá** | ✅ Hoàn chỉnh | `/pricing` hiển thị 4 gói, chuyển đổi tháng/năm |
| **API Gói dịch vụ** | ✅ Hoàn chỉnh | `GET /subscriptions/plans` với dữ liệu dự phòng |
| **Backend Đăng ký** | ✅ Hoàn chỉnh | Mẫu CQRS, thực thể miền, kho lưu trữ |
| **Tích hợp Cổng Thanh toán** | ✅ Hoàn chỉnh | VNPay, MoMo, ZaloPay sẵn sàng sử dụng |
| **API Thanh toán** | ✅ Hoàn chỉnh | Tạo thanh toán, lấy trạng thái, xử lý callback |
| **Mô hình Cơ sở dữ liệu** | ✅ Hoàn chỉnh | Plan, Subscription, Payment, UsageRecord |
| **Luồng Thanh toán Frontend** | ❌ THIẾU | Không có modal/trang để khởi tạo thanh toán |
| **Trình xử lý Trả về Thanh toán** | ❌ THIẾU | Không có trang xử lý chuyển hướng từ cổng |
| **Tự động Tạo Đăng ký** | ❌ THIẾU | Quy trình thủ công sau khi thanh toán |
---
## 🏗️ Tổng Quan Kiến Trúc
### Stack Frontend
```
Trang Định Giá (/pricing)
↓ hook usePlans()
↓ React Query
API Client: subscriptionApi.getPlans()
↓ GET /subscriptions/plans
Backend (endpoint /subscriptions/plans)
```
### Luồng Thanh toán (Hiện bị Hỏng)
```
Trang Định Giá (Chọn Gói)
✅ Hiển thị gói, giá, tính năng
❌ Nút CTA trỏ đến /register thay vì thanh toán
[THIẾU] Modal/Trang Thanh toán
❌ Chưa triển khai
❌ Không có xác nhận gói
❌ Không có lựa chọn phương thức thanh toán
[THIẾU] Tạo Thanh toán
❌ Cần gọi POST /payments
❌ Cần chuyển hướng đến paymentUrl
Cổng Thanh toán (VNPay/MoMo/ZaloPay)
✅ Backend có triển khai createPaymentUrl
✅ Xác minh chữ ký đã sẵn sàng
❌ Chưa triển khai chuyển hướng frontend
[THIẾU] Trình xử lý Trả về
❌ Không có trang nhận callback từ cổng
❌ Không có cơ chế poll trạng thái thanh toán
❌ Không có tạo đăng ký
[THIẾU] Tạo Đăng ký
❌ Cần gọi POST /subscriptions
❌ Cần hiển thị thông báo thành công
Bảng điều khiển/Trang chủ
✅ Có trang thanh toán để xem lịch sử
❌ Không có giao diện quản lý đăng ký
```
---
## 📁 Cấu Trúc Tệp Frontend
```
apps/web/
├── app/[locale]/(public)/pricing/
│ └── page.tsx ✅ Trang định giá chính
├── lib/
│ ├── subscription-api.ts ✅ API client & types (PlanDto, CreateSubscriptionResult, v.v.)
│ ├── payment-api.ts ✅ API client & types (CreatePaymentResult, PaymentStatusDto, v.v.)
│ └── hooks/
│ ├── use-subscription.ts ✅ usePlans(), useBillingHistory(), useQuota()
│ └── use-payments.ts ✅ useTransactions(), usePaymentStatus()
├── app/[locale]/(dashboard)/dashboard/
│ └── payments/page.tsx ✅ Trình xem lịch sử giao dịch
└── components/
└── (cần thêm component mới cho thanh toán)
├── checkout-modal/ ❌ Thiếu
├── payment-provider-select/ ❌ Thiếu
└── subscription-status/ ❌ Thiếu
```
---
## 🔧 Cấu Trúc Tệp Backend
```
apps/api/src/modules/
├── subscriptions/
│ ├── presentation/
│ │ ├── controllers/subscriptions.controller.ts ✅ 8 endpoint
│ │ └── dto/
│ │ ├── create-subscription.dto.ts ✅ { planTier, billingCycle }
│ │ ├── upgrade-subscription.dto.ts ✅
│ │ ├── cancel-subscription.dto.ts ✅
│ │ └── meter-usage.dto.ts ✅
│ │
│ ├── application/
│ │ ├── commands/
│ │ │ ├── create-subscription/ ✅ Tạo đăng ký
│ │ │ ├── upgrade-subscription/ ✅
│ │ │ ├── cancel-subscription/ ✅
│ │ │ └── meter-usage/ ✅
│ │ └── queries/
│ │ ├── get-plan/ ✅ Trả về PlanDto[]
│ │ ├── check-quota/ ✅
│ │ └── get-billing-history/ ✅
│ │
│ ├── domain/
│ │ ├── entities/subscription.entity.ts ✅ Aggregate CQRS
│ │ ├── events/ ✅ 5 sự kiện miền
│ │ └── repositories/subscription.repository.ts ✅ Interface
│ │
│ └── infrastructure/
│ ├── repositories/prisma-subscription.repository.ts ✅
│ └── event-handlers/listing-created-usage.handler.ts ✅
└── payments/
├── presentation/
│ ├── controllers/payments.controller.ts ✅ 5 endpoint
│ └── dto/
│ ├── create-payment.dto.ts ✅ { provider, type, amountVND, description, returnUrl }
│ ├── refund-payment.dto.ts ✅
│ └── list-transactions.dto.ts ✅
├── application/
│ ├── commands/
│ │ ├── create-payment/ ✅ Logic tạo thanh toán chính
│ │ ├── handle-callback/ ✅ Trình xử lý webhook
│ │ └── refund-payment/ ✅
│ └── queries/
│ ├── get-payment-status/ ✅ Poll trạng thái
│ └── list-transactions/ ✅
├── domain/
│ ├── entities/payment.entity.ts ✅ Aggregate CQRS
│ ├── events/ ✅ 4 sự kiện miền
│ ├── value-objects/money.vo.ts ✅
│ └── repositories/payment.repository.ts ✅ Interface
└── infrastructure/
├── repositories/prisma-payment.repository.ts ✅
└── services/
├── payment-gateway.interface.ts ✅ IPaymentGateway
├── payment-gateway.factory.ts ✅ Lấy cổng thanh toán phù hợp
├── vnpay.service.ts ✅ createPaymentUrl() + verifyCallback()
├── momo.service.ts ✅ createPaymentUrl() + verifyCallback()
└── zalopay.service.ts ✅ createPaymentUrl() + verifyCallback()
```
---
## 🔌 Tóm Tắt Endpoint API
### Endpoint Đăng ký
```
GET /subscriptions/plans → PlanDto[]
GET /subscriptions/plans/:tier → PlanDto
POST /subscriptions → CreateSubscriptionResult (yêu cầu xác thực)
PUT /subscriptions/upgrade → UpgradeSubscriptionResult (yêu cầu xác thực)
DELETE /subscriptions → CancelSubscriptionResult (yêu cầu xác thực)
POST /subscriptions/usage → MeterUsageResult (yêu cầu xác thực)
GET /subscriptions/quota/:metric → QuotaCheckResult (yêu cầu xác thực)
GET /subscriptions/billing → BillingHistoryDto (yêu cầu xác thực)
```
### Endpoint Thanh toán
```
POST /payments → CreatePaymentResult (yêu cầu xác thực)
POST /payments/callback/:provider → HandleCallbackResult (webhook)
GET /payments/:id → PaymentStatusDto (yêu cầu xác thực)
GET /payments → TransactionListDto (yêu cầu xác thực)
POST /payments/:id/refund → RefundPaymentResult (chỉ quản trị viên)
```
---
## 💰 Các Gói Định Giá
```javascript
const TIERS = [
{
tier: 'FREE',
monthlyVND: '0',
yearlyVND: '0',
maxListings: 3,
maxSearches: 5,
},
{
tier: 'AGENT_PRO',
monthlyVND: '499,000',
yearlyVND: '4,990,000',
maxListings: 50,
maxSearches: 30,
popular: true,
},
{
tier: 'INVESTOR',
monthlyVND: '999,000',
yearlyVND: '9,990,000',
maxListings: 20,
maxSearches: 100,
},
{
tier: 'ENTERPRISE',
monthlyVND: '4,990,000',
yearlyVND: '49,900,000',
maxListings: -1, // Không giới hạn
maxSearches: -1, // Không giới hạn
},
];
```
---
## 📊 Mô Hình Dữ Liệu (Prisma)
### Plan
```prisma
id: String @id
tier: PlanTier @unique (FREE, AGENT_PRO, INVESTOR, ENTERPRISE)
name: String
priceMonthlyVND: BigInt
priceYearlyVND: BigInt
maxListings: Int?
maxSavedSearches: Int?
maxAnalyticsQueries: Int?
maxMediaUploads: Int?
features: Json // { analytics: true, aiValuation: false, ... }
isActive: Boolean
```
### Subscription
```prisma
id: String @id
userId: String @unique
user: User
planId: String
plan: Plan
status: SubscriptionStatus (ACTIVE, PAST_DUE, CANCELLED, EXPIRED)
currentPeriodStart: DateTime
currentPeriodEnd: DateTime
cancelledAt: DateTime?
createdAt: DateTime
updatedAt: DateTime
```
### Payment
```prisma
id: String @id
userId: String
provider: PaymentProvider (VNPAY, MOMO, ZALOPAY, BANK_TRANSFER)
type: PaymentType (SUBSCRIPTION, LISTING_FEE, DEPOSIT, FEATURED_LISTING)
amountVND: BigInt
status: PaymentStatus (PENDING, PROCESSING, COMPLETED, FAILED, REFUNDED)
providerTxId: String?
callbackData: Json?
idempotencyKey: String? ← Ngăn chặn thanh toán trùng lặp
createdAt: DateTime
updatedAt: DateTime
```
---
## 🔑 Chi Tiết Triển Khai Quan Trọng
### Luồng Tạo Thanh toán (Backend)
```
Người dùng nhấn "Thanh toán ngay"
Frontend: POST /payments {
provider: 'VNPAY',
type: 'SUBSCRIPTION',
amountVND: 499000,
description: 'Agent Pro - Monthly',
returnUrl: 'https://goodgo.vn/payment-return',
idempotencyKey: UUID ← Duy nhất cho mỗi lần thanh toán
}
Backend CreatePaymentHandler:
1. Kiểm tra idempotencyKey (ngăn trùng lặp)
2. Xác thực số tiền (1 đến 100 tỷ VND)
3. Lấy cổng thanh toán (VNPay/MoMo/ZaloPay)
4. Gọi gateway.createPaymentUrl()
- Trả về paymentUrl: "https://gateway.com/pay?params..."
- Trả về providerTxId: "VNP-12345..."
5. Đánh dấu thanh toán là PROCESSING trong DB
6. Phát sự kiện PaymentCreatedEvent
7. Trả về cho client: { paymentId, paymentUrl, providerTxId }
Frontend:
window.location = paymentUrl ← Chuyển hướng đến cổng
Người dùng hoàn tất thanh toán tại cổng
Cổng chuyển hướng đến returnUrl với tham số callback
Backend webhook: POST /payments/callback/vnpay?params...
1. Xác minh chữ ký callback
2. Kiểm tra trạng thái thanh toán
3. Cập nhật trạng thái thanh toán trong DB
4. Phát sự kiện PaymentCompletedEvent
PaymentCompletedEvent kích hoạt:
- Gửi thông báo qua email
- Cập nhật liên kết gói của người dùng (cuối cùng)
Trình xử lý callback frontend (nếu được triển khai):
1. Lấy paymentId từ URL
2. Poll GET /payments/{paymentId}
3. Khi status = COMPLETED:
- POST /subscriptions { planTier, billingCycle }
- Hiển thị thông báo thành công
- Chuyển hướng đến bảng điều khiển
```
### Triển Khai Cổng Thanh toán
#### VNPay
```typescript
// Chữ ký: HMAC SHA-512
// Yêu cầu qua: Tham số URL
// Mã phản hồi: vnp_ResponseCode = '00' nghĩa là thành công
// Mã giao dịch: vnp_TransactionNo
```
#### MoMo
```typescript
// Chữ ký: HMAC SHA-256
// Yêu cầu qua: Body JSON POST
// Mã phản hồi: resultCode = 0 nghĩa là thành công
// Mã giao dịch: transId
```
#### ZaloPay
```typescript
// Chữ ký: HMAC SHA-256 (tương tự MoMo)
// Yêu cầu qua: Body JSON POST
// Mã phản hồi: return_code = 1 nghĩa là thành công
// Mã giao dịch: zp_trans_id
```
---
## 🚨 Những Thiếu Sót Nghiêm Trọng (Những Gì Còn Thiếu)
### 1. Modal/Trang Thanh toán ❌
**Chức năng cần có:**
- Hiển thị chi tiết gói đã chọn
- Hiện giá theo tháng và theo năm
- Cho phép chọn phương thức thanh toán (VNPay, MoMo, ZaloPay)
- Hiển thị điều khoản và điều kiện
- Xử lý tạo thanh toán và chuyển hướng
**Hiện tại:** Nút CTA trên trang định giá trỏ đến `/register` thay vì bắt đầu thanh toán
### 2. Trình xử lý Trả về Thanh toán ❌
**Chức năng cần có:**
- Nhận chuyển hướng từ cổng thanh toán
- Trích xuất trạng thái thanh toán từ URL/callback
- Poll trạng thái thanh toán qua GET /payments/:id
- Tạo đăng ký khi thanh toán thành công
- Hiển thị giao diện thành công/lỗi
**Hiện tại:** Không có trang nào cho luồng này
### 3. Tự động Tạo Đăng ký ❌
**Chức năng cần có:**
- Sau khi thanh toán thành công, gọi POST /subscriptions
- Truyền planTier và billingCycle
- Cập nhật trạng thái đăng ký của người dùng
- Chuyển hướng đến bảng điều khiển
**Hiện tại:** Quy trình thủ công, không có giao diện
### 4. Giao diện Quản lý Đăng ký ⚠️ Một phần
**Những gì đã có:**
- Trang thanh toán hiển thị lịch sử giao dịch
**Những gì còn thiếu:**
- Trang trạng thái/chi tiết đăng ký
- Giao diện nâng cấp/hạ cấp gói
- Giao diện hủy đăng ký
- Hiển thị mức sử dụng/hạn mức
---
## 📋 Lộ Trình Triển Khai
### Giai đoạn 1: Thanh toán Cơ bản (1-2 ngày)
```
✅ Trang định giá đã tồn tại
❌ Thêm component CheckoutModal
❌ Thêm bộ chọn nhà cung cấp thanh toán
❌ Tạo trang /payment-return
❌ Triển khai poll thanh toán
❌ Kết nối tạo đăng ký
```
### Giai đoạn 2: Tích hợp Đầy đủ (1-2 ngày)
```
✅ Tất cả endpoint backend đã sẵn sàng
❌ Xử lý các trường hợp ngoại lệ (hết thời gian, người dùng đóng cửa sổ, v.v.)
❌ Thêm luồng phục hồi lỗi
❌ Thêm giao diện đang tải/thành công
❌ Kiểm tra với cả 3 nhà cung cấp thanh toán
```
### Giai đoạn 3: Quản lý Đăng ký (1-2 ngày)
```
✅ Endpoint API nâng cấp/hạ cấp đã tồn tại
✅ API hủy đăng ký đã tồn tại
❌ Xây dựng trang chi tiết đăng ký
❌ Thêm giao diện nâng cấp/hạ cấp
❌ Thêm giao diện hủy với xác nhận
❌ Thêm hiển thị hạn mức sử dụng
```
### Giai đoạn 4: Kiểm tra & Hoàn thiện (1-2 ngày)
```
❌ Kiểm tra E2E cho tất cả nhà cung cấp thanh toán
❌ Xử lý lỗi & các trường hợp ngoại lệ
❌ Tối ưu hóa hiệu suất
❌ Tích hợp phân tích/theo dõi
```
---
## 🎯 Các Bước Tiếp Theo
1. **Hiểu UX thanh toán mong muốn** - Thanh toán nên bắt đầu ở đâu/như thế nào?
- Modal từ trang định giá?
- Trang thanh toán riêng?
- Trực tiếp trên trang định giá?
2. **Tạo component CheckoutModal** - Thiết kế phù hợp với trang định giá
- Tóm tắt gói
- Phân tích giá
- Bộ chọn nhà cung cấp thanh toán
- Nút "Tiến hành Thanh toán"
3. **Triển khai mutation tạo thanh toán** - Kết nối với React Query
- Hook `useCreatePayment()`
- Xử lý trạng thái đang tải/lỗi
- Chuyển hướng đến paymentUrl
4. **Xây dựng trang /payment-return** - Xử lý chuyển hướng từ cổng
- Phân tích tham số URL
- Poll trạng thái thanh toán
- Tạo đăng ký khi thành công
5. **Kiểm tra với cả 3 nhà cung cấp** - Đảm bảo tất cả tích hợp hoạt động
- Sử dụng thông tin đăng nhập sandbox/kiểm tra
- Xác minh callback
6. **Thêm giao diện quản lý đăng ký** - Cho phép người dùng quản lý gói
- Xem đăng ký hiện tại
- Nâng cấp/hạ cấp
- Hủy với xác nhận
---
## 📚 Tài Liệu Tham Khảo
Tài liệu kiểm tra đầy đủ: `PRICING_CHECKOUT_AUDIT.md`
Các tệp cần xem xét:
- Frontend: `/apps/web/app/[locale]/(public)/pricing/page.tsx`
- Backend thanh toán: `/apps/api/src/modules/payments/`
- Backend đăng ký: `/apps/api/src/modules/subscriptions/`
- Prisma schema: `/prisma/schema.prisma` (dòng 451-514)
---
**Trạng thái:** Sẵn sàng triển khai thanh toán
**Ước tính công sức:** 4-6 ngày
**Độ phức tạp:** Trung bình (toàn bộ hạ tầng backend đã sẵn sàng)