Bỏ qua nội dung

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[] theo cycleMonths, 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):

User Stories

  • 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ý.
  • 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.
  • 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ì.
  • user, tôi muốn biết giá nâng cấp lên gói cao hơn (priceCredits trong entitlement) để cân nhắc nâng cấp.
  • 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)
    end

Acceptance Criteria

  • AC-1: GET /subscriptions/plans trả về danh sách gói có status = ACTIVE, không yêu cầu authentication.
  • AC-2: Chỉ gói có status = ACTIVE xuất hiện trong list public; gói INACTIVE bị ẩn (PackageStatusEnum chỉ có ACTIVEINACTIVE).
  • AC-3: Mỗi SubscriptionPackageResponse có đủ: 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ồm cycleMonths, priceVnd, priceCredits, bonusMonths.
  • AC-5: GET /subscriptions/me/entitlement yêu cầu authority subscription:read.
  • AC-6: Khi user không có gói active, response trả về data rỗng ({}) thay vì lỗi.
  • AC-7: Khi user có gói active, EntitlementResponse trả 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: paidExpiredAt là mốc kết thúc kỳ trả phí; expiredAt là 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: priceCredits trong 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á purchasePriceCredits tại thời điểm mua.
  • code là 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 plans theo cycleMonths (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.
  • bonusMonths mở 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.
  • listingLimit null = không giới hạn số listing active cùng lúc; số > 0 = giới hạn cứng.
  • dailyFreeUnlockLimit là số lần unlock số điện thoại miễn phí/ngày của gói; Integer.MAX_VALUE ngầm hiểu là không giới hạn (Max/Team).
  • dailyListingDistributionLimit là 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ói INACTIVE bị ẩn khỏi catalog công khai (GET /subscriptions/plans chỉ trả gói ACTIVE), nhưng user đang sở hữu gói đó vẫn giữ đầy đủ quyền lợi tới expiredAt. Lý do: entitlement chỉ kiểm user_package.status = 'ACTIVE', KHÔNG kiểm package.status.
  • trustScoreMaxInclusive (lưu DB) cùng trustScoreMaxExclusive (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 FREEMIUM chư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ột plans của bảng packages. Mỗi entry: cycleMonths, priceVnd, priceCredits, bonusMonths.
  • UserPackage: bản ghi gói user đang/đã dùng (bảng user_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ới expiredAt (xem R1).

PackageStatusEnum backend chỉ có đúng 2 giá trị ACTIVEINACTIVE. Không có trạng thái DEPRECATED.

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:

CodeStatusGiá/thángListing limitDaily unlockTrust score (inclusive)Rank
MAXACTIVE700.000 VNDunlimitedunlimited (2³¹ − 1)≤ 79.991
TEAMINACTIVE0 (placeholder)unlimitedunlimited (2³¹ − 1)≤ 79.991
PROACTIVE350.000 VNDunlimited200≤ 69.992
PREMIUMACTIVE150.000 VND10 listings100≤ 59.993

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