mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 17:26:23 +02:00
docs: rewrite story 3.5 and epic 5 stories to match actual codebase
Keeps subscription SaaS vision from PRD while adding accurate as-is analysis of existing code. Each story now has an "As-Is" table showing what exists and where the gaps are. Key points: - Story 3.5: Transition from BYOK to system-managed models with token billing. BYOK stays for self-hosted mode (deployment_mode=self-hosted), system models + subscription quota for hosted mode. - Story 5.1: Pricing UI exists (Free/PAYG/Enterprise) but needs redesign to subscription tiers (Free/Pro) with monthly/yearly toggle. - Story 5.2: PAYG checkout exists (mode=payment), need NEW subscription endpoint (mode=subscription) with stripe_customer_id binding. - Story 5.3: Webhook infrastructure exists (signature verify, PAYG handlers). Need subscription event handlers (customer.subscription.*) alongside. - Story 5.4: PageLimitService fully implemented. Gap is HTTP-layer pre-check, plan-based limits, frontend quota indicator, and 402 error handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a368dbcb08
commit
e7382b26de
5 changed files with 268 additions and 149 deletions
|
|
@ -1,9 +1,7 @@
|
|||
# Story 5.2: Tích hợp Stripe Checkout (Stripe Payment Integration)
|
||||
# Story 5.2: Tích hợp Stripe Subscription Checkout (Stripe Payment Integration)
|
||||
|
||||
Status: ready-for-dev
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a Người dùng,
|
||||
|
|
@ -12,35 +10,56 @@ so that tôi có thể điền thông tin thẻ tín dụng mà không sợ bị
|
|||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Khi User bấm thanh toán, BE gọi API Stripe lấy `sessionId` của Stripe Checkout (Chế độ Subscription / Recurring Mode, không phải Chế độ Mua đứt OTP).
|
||||
2. Hệ thống redirect User an toàn qua cổng Stripe được Hosted trực tiếp bởi Stripe Server.
|
||||
1. Khi User bấm thanh toán, BE gọi API Stripe lấy `sessionId` với **`mode='subscription'`** (recurring billing, không phải one-time payment).
|
||||
2. Hệ thống redirect User an toàn qua cổng Stripe Hosted Checkout.
|
||||
3. Sau thanh toán thành công, user được redirect về app với subscription activated.
|
||||
|
||||
## As-Is (Code hiện tại)
|
||||
|
||||
| Component | Hiện trạng | File |
|
||||
|-----------|-----------|------|
|
||||
| Checkout Endpoint | **Đã tồn tại** nhưng chỉ hỗ trợ `mode='payment'` (one-time page packs) | `surfsense_backend/app/routes/stripe_routes.py` line ~205 |
|
||||
| Checkout Request Schema | `CreateCheckoutSessionRequest(search_space_id, quantity)` — cho page packs | `stripe_routes.py` |
|
||||
| Stripe Client | **Đã tồn tại** — `get_stripe_client()`, config từ env vars | `stripe_routes.py` |
|
||||
| Success/Cancel URLs | **Đã tồn tại** — `_get_checkout_urls()` | `stripe_routes.py` |
|
||||
| Stripe Config | `STRIPE_SECRET_KEY`, `STRIPE_PRICE_ID`, `STRIPE_WEBHOOK_SECRET` đã có | `config.py` |
|
||||
| User ↔ Stripe mapping | **Không có** `stripe_customer_id` trên User model | `db.py` |
|
||||
|
||||
**Gap:** Cần thêm endpoint subscription checkout **mới** (giữ endpoint page purchase cũ nếu muốn). Cần `stripe_customer_id` để Stripe quản lý recurring billing.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] Task 1: Nâng cấp `stripe_routes.py`
|
||||
- [ ] Subtask 1.1: Bổ sung Endpoint POST `/api/v1/stripe/create-subscription-checkout`.
|
||||
- [ ] Subtask 1.2: Phân tích `plan_id` từ Request body hoặc User, cấu hình `mode='subscription'` trong dict truyền cho thư viện Stripe (thay vì chế độ `payment` cũ).
|
||||
- [ ] Task 2: Liên kết Action Nút ở UI
|
||||
- [ ] Subtask 2.1: Ở `Pricing` Component, xử lý `onClick` bằng cách submit POST form request tới API mới, bắt `checkout_url` và route trình duyệt tới URL đó bằng thẻ A hoặc JS `window.location.href`.
|
||||
- [ ] Task 1: Thêm `stripe_customer_id` vào User (Backend DB)
|
||||
- [ ] Subtask 1.1: Alembic migration thêm column `stripe_customer_id` (String, nullable, unique, indexed) vào `User`.
|
||||
- [ ] Subtask 1.2: Tạo helper function `get_or_create_stripe_customer(user)` — nếu `stripe_customer_id` null → gọi `stripe.customers.create(email=user.email)` → lưu ID vào DB.
|
||||
|
||||
- [ ] Task 2: Tạo Subscription Checkout Endpoint (Backend)
|
||||
- [ ] Subtask 2.1: Tạo endpoint `POST /api/v1/stripe/create-subscription-checkout`.
|
||||
- [ ] Subtask 2.2: Request body: `{ "plan_id": "pro_monthly" | "pro_yearly" }`.
|
||||
- [ ] Subtask 2.3: Map `plan_id` → Stripe Price ID từ env vars (`STRIPE_PRO_MONTHLY_PRICE_ID`, `STRIPE_PRO_YEARLY_PRICE_ID`). Tuyệt đối **không nhận price từ frontend** — phòng tránh giả mạo giá.
|
||||
- [ ] Subtask 2.4: Gọi `stripe.checkout.sessions.create(mode='subscription', customer=stripe_customer_id, ...)`.
|
||||
- [ ] Subtask 2.5: Trả về `{ "checkout_url": "https://checkout.stripe.com/..." }`.
|
||||
|
||||
- [ ] Task 3: Kết nối Frontend với Endpoint mới
|
||||
- [ ] Subtask 3.1: Từ `pricing-section.tsx`, nút "Upgrade to Pro" gọi `POST /api/v1/stripe/create-subscription-checkout` với `plan_id`.
|
||||
- [ ] Subtask 3.2: Redirect đến `checkout_url`.
|
||||
- [ ] Subtask 3.3: Xử lý success return URL — hiển thị toast "Subscription activated!"
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Relevant Architecture Patterns & Constraints
|
||||
- Codebase hiện có `surfsense_backend/app/routes/stripe_routes.py` nhưng ĐANG GẮN code Payment Intent cho Token "Page Purchase" One-time 1 lần. DEV cần lưu ý CẤU HÌNH LẠI để route mới sinh này phục vụ riêng cho gói Subscription hàng tháng (Gửi theo CustomerID nếu User đã bind).
|
||||
- Security: Giá `stripe_price_id` bắt buộc phải map và define ở Back-End environment variable (Vd `STRIPE_PRO_PLAN_ID`), tuyệt đối không chấp nhận param `price` từ Header gửi lên (Phòng tránh giả mạo giá tiền).
|
||||
### Giữ song song PAYG và Subscription?
|
||||
Endpoint page purchase cũ (`create-checkout-session` với `mode='payment'`) có thể giữ nguyên. Endpoint subscription mới chạy song song. Quyết định business logic.
|
||||
|
||||
### Project Structure Notes
|
||||
- Module thay đổi:
|
||||
- `surfsense_backend/app/routes/stripe_routes.py`
|
||||
- `surfsense_web/src/pages/pricing/page.tsx`
|
||||
### Security
|
||||
- Giá `stripe_price_id` map ở **backend env vars** (`STRIPE_PRO_MONTHLY_PRICE_ID`, `STRIPE_PRO_YEARLY_PRICE_ID`).
|
||||
- Frontend chỉ gửi `plan_id` (string enum), backend resolve ra Stripe Price ID.
|
||||
|
||||
### Stripe Customer
|
||||
Khi tạo subscription checkout, **bắt buộc** phải có `customer` parameter. Stripe dùng customer ID để quản lý recurring billing, invoices, payment methods. Nên create Stripe customer ngay khi user đăng ký hoặc lần đầu checkout.
|
||||
|
||||
### Webhook (xem Story 5.3)
|
||||
Sau checkout, Stripe sẽ gửi `checkout.session.completed` → webhook handler cần detect `mode='subscription'` và activate subscription trên DB.
|
||||
|
||||
### References
|
||||
- [Epic 5.2 - Subscriptions]
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
Antigravity Claude 3.5 Sonnet Engine
|
||||
|
||||
### File List
|
||||
- `surfsense_backend/app/routes/stripe_routes.py`
|
||||
- `surfsense_backend/app/routes/stripe_routes.py` — endpoint PAYG hiện tại (tham khảo pattern)
|
||||
- Stripe Subscription Checkout docs: https://stripe.com/docs/billing/subscriptions/build-subscriptions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue