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