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

17 KiB

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á

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

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

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

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

// 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

// 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

// 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)