8.1 KiB
8.1 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-operator-experience | 03 | execute | 2 |
|
|
false |
|
|
Purpose: Operators can self-serve their subscription lifecycle through the portal, with per-agent monthly pricing that matches the "hire an employee" metaphor. Output: Billing page with subscription card, Checkout redirect, Billing Portal redirect, status display.
<execution_context> @/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md @/home/adelorenzo/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-operator-experience/03-CONTEXT.md @.planning/phases/03-operator-experience/03-RESEARCH.md @.planning/phases/03-operator-experience/03-01-SUMMARY.mdFrom packages/shared/shared/api/billing.py (created in Plan 01):
# POST /api/portal/billing/checkout -> { "checkout_url": "https://checkout.stripe.com/..." }
# Body: { "tenant_id": str, "agent_count": int }
# POST /api/portal/billing/portal -> { "portal_url": "https://billing.stripe.com/..." }
# Body: { "tenant_id": str }
# POST /api/webhooks/stripe -> webhook handler (no portal UI interaction)
From packages/shared/shared/models/tenant.py (updated in Plan 01):
class Tenant(Base):
# New billing fields:
stripe_customer_id: Mapped[str | None]
stripe_subscription_id: Mapped[str | None]
stripe_subscription_item_id: Mapped[str | None]
subscription_status: Mapped[str] # "none" | "trialing" | "active" | "past_due" | "canceled" | "unpaid"
trial_ends_at: Mapped[datetime | None]
agent_quota: Mapped[int]
Established portal patterns:
- shadcn/ui components (Card, Badge, Button, Dialog)
- TanStack Query for data fetching
- API client in lib/api.ts
2. Create `packages/portal/components/subscription-card.tsx`:
- shadcn/ui Card displaying:
- BillingStatus badge (top right)
- Plan name: "AI Employee Plan" (per-agent monthly)
- Price: "$49/agent/month" (from user decision)
- Current agent count vs quota
- Trial info: if trialing, show "Trial ends {date}" with days remaining
- Agent count adjuster: +/- buttons to change quantity (triggers Stripe subscription item update)
- Action buttons:
- If status="none": "Subscribe" button -> creates Checkout Session -> redirects to Stripe
- If status="trialing" or "active": "Manage Billing" button -> creates Billing Portal session -> redirects to Stripe hosted portal
- If status="past_due": "Update Payment" button -> Billing Portal redirect
- If status="canceled": "Resubscribe" button -> new Checkout Session
3. Create `packages/portal/app/(dashboard)/billing/page.tsx`:
- Reads tenant_id from query or session
- Fetches tenant data (includes billing fields) via existing useTenant() hook
- Renders SubscriptionCard
- Handles ?session_id= searchParam (Stripe Checkout success redirect): show success toast, refetch tenant
- If subscription_status is "past_due": show a top banner warning about payment failure
- Per user decision: per-agent monthly pricing ($49/agent/month), 14-day free trial with full access, credit card required upfront
4. Add TanStack Query hooks to `packages/portal/lib/queries.ts`:
- useCreateCheckoutSession() — mutation POST /billing/checkout, returns { checkout_url }
- useCreateBillingPortalSession() — mutation POST /billing/portal, returns { portal_url }
- useUpdateSubscriptionQuantity() — mutation (if needed for +/- agent count)
5. Add "Billing" link to dashboard navigation/sidebar.
cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20
- Billing page renders subscription card with current status
- Subscribe button creates Checkout Session and redirects to Stripe
- Manage Billing button redirects to Stripe Billing Portal
- Status badges show correct colors for all subscription states
- Trial info displayed when status is "trialing"
- Past due banner shown when payment has failed
- Portal builds without errors
Task 2: Verify billing page and subscription UI
n/a
Human verifies the billing management page:
1. Start the portal: `cd packages/portal && npm run dev`
2. Navigate to /billing — verify subscription card renders
3. Verify status badge shows "No subscription" for new tenant
4. Verify "Subscribe" button is present
5. Verify pricing shows "$49/agent/month" and "14-day free trial"
6. Verify "Billing" link appears in dashboard navigation
7. Check that agent count +/- controls are present
Human visual inspection of billing page
Operator confirms billing page renders correctly with subscription card, pricing, and action buttons
- Portal builds: `cd packages/portal && npx next build`
- Billing page renders without errors
- All subscription states display correctly
- Navigation to billing page works
<success_criteria>
- Operator can see subscription status and pricing on billing page
- Subscribe flow initiates Stripe Checkout redirect
- Billing Portal accessible for managing payment/invoices
- Status badges accurately reflect subscription_status field
- Portal builds successfully </success_criteria>