Xem gói package và quyền lợi hiện tại
Feature URL
Module
subscription-package
Status
shipped
Priority
P0
Platforms
fe · be
AC progress
9 / 9
Last reviewed
2026-06-08
Mục tiêu
Cho phép người dùng (kể cả khách chưa đăng nhập) xem danh sách các gói thuê bao đang bán để so sánh và quyết định mua, đồng thời cho user đã đăng nhập xem gói mình đang dùng cùng toàn bộ quyền lợi (entitlement) được resolve từ gói đó. Đây cũng là nơi định nghĩa master data của gói (Package) và bản ghi gói mà user đang sở hữu (UserPackage) — nền tảng cho các flow mua, đổi gói và gia hạn.
Phạm vi
Trong phạm vi (In scope):
- API public list gói đang
ACTIVE(GET /subscriptions/plans) — phục vụ pricing page, không cần đăng nhập. - API xem entitlement hiện tại của user đăng nhập (
GET /subscriptions/me/entitlement) — gói đang active, ngày hết hạn, các flag quyền lợi, quota. - Định nghĩa entity
Package(catalog master data) vàUserPackage(gói user đang dùng) làm nguồn dữ liệu cho cả module. - Hiển thị cho mỗi gói: tên, code, mô tả, danh sách
plans[]theocycleMonths, giá VND + giá V-Credits, bonus months, flag quyền lợi, trust score band, listing limit, daily free unlock limit, daily listing distribution limit. - Response entitlement bao gồm: gói đang dùng,
isRenewal,startedAt,paidExpiredAt,expiredAt,bonusMonths, các quyền lợi (clean data, AVM, owner likelihood, AI tools, masking), giá nâng cấp gói kế tiếp,dailyFreeUnlockLimit, trust range marketplace.
Ngoài phạm vi (Out of scope):
- Mua gói bằng V-Credits — xem feature package-purchase.
- Nâng cấp / hạ gói — xem feature package-change.
- Gia hạn và tự động gia hạn — xem feature package-renewal.
- Admin tạo / cập nhật gói — xem admin-package-create, admin-package-update.
- Endpoint admin để xem gói
INACTIVE— chưa có (sẽ bổ sung sau). - Gán FREEMIUM cho user mới đăng ký — chưa implement.
User Stories
- Là khách chưa đăng nhập, tôi muốn xem danh sách gói và giá để so sánh trước khi đăng ký.
- Là client app, tôi muốn lấy đầy đủ các plan của gói (1 tháng, 12 tháng + bonus months, giá VND và giá V-Credits) để render pricing card.
- Là user đã đăng nhập, tôi muốn xem mình đang ở gói nào, hết hạn khi nào, còn bao nhiêu quyền lợi để biết mình được dùng những tính năng gì.
- Là user, tôi muốn biết giá nâng cấp lên gói cao hơn (
priceCreditstrong entitlement) để cân nhắc nâng cấp. - Là hệ thống, tôi muốn entitlement luôn phản ánh đúng gói active hiện tại để các tính năng khác (masking, AI tools, marketplace) áp dụng quyền lợi chính xác.
Luồng chức năng
sequenceDiagram
actor Guest
actor User
participant App as FE/Mobile
participant BE
Note over Guest,BE: Public — xem catalog
Guest->>App: Mở pricing page
App->>BE: GET /subscriptions/plans
BE->>BE: Query packages WHERE status = ACTIVE
BE-->>App: List SubscriptionPackageResponse
Note over User,BE: Đã đăng nhập — xem gói của mình
User->>App: Mở trang "Gói của tôi"
App->>BE: GET /subscriptions/me/entitlement (Bearer token)
BE->>BE: Resolve UserPackage active của user
alt Không có gói active
BE-->>App: ApiResponse data = {} (rỗng)
else Có gói active
BE->>BE: Resolve quyền lợi từ Package + UserPackage
BE-->>App: EntitlementResponse (gói, hạn, flag quyền lợi, quota)
endAcceptance Criteria
- AC-1:
GET /subscriptions/planstrả về danh sách gói cóstatus = ACTIVE, không yêu cầu authentication. - AC-2: Chỉ gói có
status = ACTIVExuất hiện trong list public; góiINACTIVEbị ẩn (PackageStatusEnumchỉ cóACTIVEvàINACTIVE). - AC-3: Mỗi
SubscriptionPackageResponsecó đủ:id,name,code,description,priceCredits,monthlyPriceVnd,monthlyPriceCredits,annualPriceCredits,annualBonusMonths,plans[],packageTypes,listingLimit,dailyListingDistributionLimit,addressMasked,aiToolsEnabled,ownerLikelihoodEnabled,trustScoreMaxExclusive,status,createdAt,updatedAt,expiredAt. - AC-4: Mỗi entry trong
plans[]gồmcycleMonths,priceVnd,priceCredits,bonusMonths. - AC-5:
GET /subscriptions/me/entitlementyêu cầu authoritysubscription:read. - AC-6: Khi user không có gói active, response trả về
datarỗng ({}) thay vì lỗi. - AC-7: Khi user có gói active,
EntitlementResponsetrả về:userId,packageCode,isRenewal,startedAt,paidExpiredAt,expiredAt,bonusMonths,canViewCleanData,canUseAvm,canViewOwnerLikelihood,canUseAiTools,phoneMasked,addressMasked,priceCredits(giá nâng cấp gói kế tiếp),voucherOptions,upgradePromptAfterDays,dailyFreeUnlockLimit,marketplaceTrustRange. - AC-8:
paidExpiredAtlà mốc kết thúc kỳ trả phí;expiredAtlà mốc kết thúc toàn bộ quyền lợi (đã cộng bonus months) — hai mốc tách biệt. - AC-9:
priceCreditstrong entitlement = 0 khi user đang ở gói tier cao nhất (không còn gói để nâng cấp).
Quy tắc nghiệp vụ
- Catalog (
Package) tách rời với gói user đang dùng (UserPackage): thay đổi catalog không hồi tố lên gói đã mua. User snapshot giápurchasePriceCreditstại thời điểm mua. codelà machine-readable identifier viết hoa, unique trong catalog (PREMIUM, PRO, MAX, TEAM…). Dùng để tham chiếu từ business code.- Một gói có 1 hoặc nhiều
planstheocycleMonths(thường là 1 và 12). Mỗi plan có giá VND, giá V-Credits, và bonus months tuỳ chọn. - Giá có 2 đơn vị độc lập:
priceVnd(hiển thị, reconciliation với payment provider) vàpriceCredits(đơn vị thực tế trừ ví khi mua). MVP convention: 1 VND ≈ 1 V-Credit nhưng admin có thể tách rời. bonusMonthsmở rộng thời gian hưởng lợi (expiredAt) nhưng KHÔNG nằm trong kỳ trả phí (paidExpiredAt) và không tạo giá trị hoàn tiền khi nâng cấp.listingLimitnull= không giới hạn số listing active cùng lúc; số > 0 = giới hạn cứng.dailyFreeUnlockLimitlà số lần unlock số điện thoại miễn phí/ngày của gói;Integer.MAX_VALUEngầm hiểu là không giới hạn (Max/Team).dailyListingDistributionLimitlà số tin hệ thống tự phân phối cho broker mỗi ngày;0= tắt phân phối. Chi tiết phân phối xem broker-daily-listing-distribution.- Vòng đời
INACTIVE(R1): góiINACTIVEbị ẩn khỏi catalog công khai (GET /subscriptions/planschỉ trả góiACTIVE), nhưng user đang sở hữu gói đó vẫn giữ đầy đủ quyền lợi tớiexpiredAt. Lý do: entitlement chỉ kiểmuser_package.status = 'ACTIVE', KHÔNG kiểmpackage.status. trustScoreMaxInclusive(lưu DB) cùngtrustScoreMaxExclusive(input admin) định nghĩa band trust score mà gói được xem trên marketplace. Quan hệ:inclusive = exclusive - 0.01.- Entitlement chỉ tính trên đúng 1 gói active của user (MVP: mỗi user tối đa một gói active).
- Gói
FREEMIUMchưa được seed vào DB; user đăng ký mới hiện chưa được gán gói tự động.
Dữ liệu & Trạng thái
Entity nghiệp vụ:
Package: catalog gói —id,code,name,description,plans(JSONB),listingLimit,dailyFreeUnlockLimit,dailyListingDistributionLimit(số tin hệ thống tự phân phối cho broker mỗi ngày;0= tắt),phoneMasked,addressMasked,cleanDataEnabled,avmEnabled,aiToolsEnabled,ownerLikelihoodEnabled,smartPushEnabled,displayPriorityRank,trustScoreMaxInclusive,vipFlashDealEnabled,status,affiliateRate,createdAt,updatedAt,expiredAt.PackagePlan: không phải bảng riêng — lưu dạng JSONB array trong cộtplanscủa bảngpackages. Mỗi entry:cycleMonths,priceVnd,priceCredits,bonusMonths.UserPackage: bản ghi gói user đang/đã dùng (bảnguser_packages) —id,user,pkg,listingsUsed,startedAt,expiredAt,paidExpiredAt,status(ACTIVE/EXPIRED),renewal(cờ auto-renew),purchasePriceCredits(snapshot giá),cycleMonths,bonusMonths.SubscriptionPackageResponse: view 1 gói trong catalog.EntitlementResponse: view quyền lợi hiện tại của user.
Trạng thái Package:
ACTIVE— đang bán, hiển thị trên catalog public.INACTIVE— tạm ẩn, không bán, không hiện trên catalog public; user đang giữ gói này vẫn hưởng quyền lợi tớiexpiredAt(xem R1).
PackageStatusEnumbackend chỉ có đúng 2 giá trịACTIVEvàINACTIVE. Không có trạng tháiDEPRECATED.
Trạng thái UserPackage:
ACTIVE— gói đang hiệu lực, dùng để resolve entitlement.EXPIRED— gói đã hết hạn hoặc bị thay thế khi mua/đổi/gia hạn.
Catalog thực tế trong DB:
| Code | Status | Giá/tháng | Listing limit | Daily unlock | Trust score (inclusive) | Rank |
|---|---|---|---|---|---|---|
MAX | ACTIVE | 700.000 VND | unlimited | unlimited (2³¹ − 1) | ≤ 79.99 | 1 |
TEAM | INACTIVE | 0 (placeholder) | unlimited | unlimited (2³¹ − 1) | ≤ 79.99 | 1 |
PRO | ACTIVE | 350.000 VND | unlimited | 200 | ≤ 69.99 | 2 |
PREMIUM | ACTIVE | 150.000 VND | 10 listings | 100 | ≤ 59.99 | 3 |
Endpoint BE hiện có:
GET /api/subscriptions/plans— catalog public (ACTIVE).GET /api/subscriptions/me/entitlement— entitlement của user đăng nhập.
Liên quan
- Phụ thuộc: Chưa có (catalog là foundation của module).
- Ảnh hưởng: package-purchase, package-change, package-renewal
- Admin quản lý catalog: admin-package-create, admin-package-update