SurfSense/_bmad-output/implementation-artifacts/5-1-pricing-plan-selection-ui.md
Vonic 04fb9eec0f docs: rewrite story 3.5 and epic 5 stories to match actual codebase
Stories were written for a subscription SaaS model, but SurfSense is a
self-hosted product with BYOK + optional PAYG page packs via Stripe.

Key corrections:
- Story 3.5: Not "remove BYOK + token billing" → actual gap is adding
  HTTP-layer quota pre-check before document upload enqueue
- Story 5.1: Pricing UI already exists (Free/PAYG/Enterprise) → gap is
  wiring "Get Started" button to existing Stripe checkout endpoint
- Story 5.2: mode=payment PAYG already works → needs verification/hardening
  not a subscription checkout rewrite
- Story 5.3: Webhook already handles checkout.session.completed correctly
  → no subscription events needed, gap is idempotency test + purchase history UI
- Story 5.4: PageLimitService + enforcement in tasks/connectors already exists
  → gap is HTTP-layer pre-check, quota UI indicator, and 402 frontend handling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 14:08:55 +07:00

3.7 KiB
Raw Blame History

Story 5.1: Kết nối Pricing UI với Stripe Checkout

Status: ready-for-dev

Context / Correction Note

⚠️ Story gốc bị sai hướng. Story gốc mô tả tạo mới pricing page với Free/Pro/Team subscription tiers. Thực tế, pricing page đã tồn tại với mô hình PAYG (Pay-As-You-Go page packs), không phải subscription. Stripe checkout endpoint cũng đã tồn tại. Task thực tế là wire up nút "Get Started" của PAYG tier với endpoint hiện có.

Story

As a Người dùng đã đăng nhập, I want bấm "Get Started" trên trang Pricing để mua page packs, so that tôi có thể tiếp tục upload tài liệu sau khi hết quota.

Actual Architecture (as-is)

Đã tồn tại và hoạt động:

  • surfsense_web/app/(home)/pricing/page.tsx — pricing page route
  • surfsense_web/components/pricing/pricing-section.tsx — UI với 3 tiers:
    • FREE: 500 pages included, button href="/login"
    • PAY AS YOU GO: $1/1,000 pages, button href="/login" ← cần sửa
    • ENTERPRISE: Contact Sales, button href="/contact"
  • surfsense_backend/app/routes/stripe_routes.py:create_checkout_session — endpoint POST /api/v1/stripe/create-checkout-session đã implement, mode=payment, yêu cầu search_space_idquantity

Chưa làm:

  • Nút "Get Started" của PAYG tier chỉ link đến /login, chưa gọi Stripe checkout
  • Không có flow chọn số lượng page packs (quantity)

Acceptance Criteria

  1. Khi user đã đăng nhập bấm "Get Started" ở PAYG tier, hiện modal/form cho phép chọn số lượng pack (1, 5, 10, etc.).
  2. Sau khi confirm, gọi POST /api/v1/stripe/create-checkout-session với quantitysearch_space_id, nhận checkout_url.
  3. Redirect user đến checkout_url (Stripe-hosted checkout page).
  4. Nếu user chưa đăng nhập, redirect đến /login trước (behavior hiện tại giữ nguyên cho FREE tier).

Tasks / Subtasks

  • Task 1: Cập nhật nút PAYG trong pricing-section.tsx
    • Subtask 1.1: Thay href="/login" bằng onClick handler. Nếu user chưa authenticated, redirect /login. Nếu đã authenticated, mở modal chọn quantity.
    • Subtask 1.2: Tạo PurchasePagesModal component với dropdown/input chọn số pack (110), hiển thị tổng tiền (quantity × $1).
  • Task 2: Gọi Stripe checkout API
    • Subtask 2.1: Khi user confirm trong modal, gọi POST /api/v1/stripe/create-checkout-session với { quantity, search_space_id }.
    • Subtask 2.2: Nhận checkout_url và redirect bằng window.location.href = checkout_url.
  • Task 3: Xử lý return URL sau checkout
    • Subtask 3.1: Kiểm tra success/cancel URL config hiện tại trong stripe_routes.py (_get_checkout_urls).
    • Subtask 3.2: Sau purchase thành công, hiển thị toast "Mua thành công! X pages đã được thêm vào tài khoản."

Dev Notes

Stripe Checkout Request Schema (hiện tại)

class CreateCheckoutSessionRequest(BaseModel):
    search_space_id: int
    quantity: int  # số pack, mỗi pack = STRIPE_PAGES_PER_UNIT pages

API Endpoint

POST /api/v1/stripe/create-checkout-session
Authorization: Bearer <token>
Body: { "search_space_id": 1, "quantity": 2 }
Response: { "checkout_url": "https://checkout.stripe.com/..." }

References

  • surfsense_web/components/pricing/pricing-section.tsx
  • surfsense_backend/app/routes/stripe_routes.py (lines ~204271)
  • surfsense_web/app/dashboard/[search_space_id]/purchase-cancel/page.tsx

Dev Agent Record

Agent Model Used

TBD

File List

  • surfsense_web/components/pricing/pricing-section.tsx
  • surfsense_web/components/pricing/PurchasePagesModal.tsx (new)