Files

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
03-01
packages/portal/app/(dashboard)/billing/page.tsx
packages/portal/components/subscription-card.tsx
packages/portal/components/billing-status.tsx
packages/portal/lib/queries.ts
false
PRTA-05
truths artifacts key_links
Operator can subscribe to a per-agent monthly plan via Stripe Checkout
Operator can upgrade (add agents) and downgrade (remove agents) their subscription
Operator can cancel their subscription
Operator can manage payment methods and view invoices via Stripe Billing Portal
Feature limits are enforced based on subscription state (agents deactivated on cancellation)
14-day free trial with full access is available
path provides
packages/portal/app/(dashboard)/billing/page.tsx Billing management page with subscription status and actions
path provides
packages/portal/components/subscription-card.tsx Card showing current plan, agent count, status, trial info
path provides
packages/portal/components/billing-status.tsx Status badge for subscription state (trialing, active, past_due, canceled)
from to via pattern
packages/portal/app/(dashboard)/billing/page.tsx /api/portal/billing/checkout POST to create Checkout Session, then redirect to Stripe billing/checkout
from to via pattern
packages/portal/app/(dashboard)/billing/page.tsx /api/portal/billing/portal POST to create Billing Portal session, then redirect billing/portal
Billing management page with Stripe subscription integration -- subscribe, upgrade, downgrade, cancel, and manage payment via Stripe Billing Portal.

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.md

From 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
Task 1: Billing page with subscription management packages/portal/app/(dashboard)/billing/page.tsx, packages/portal/components/subscription-card.tsx, packages/portal/components/billing-status.tsx, packages/portal/lib/queries.ts 1. Create `packages/portal/components/billing-status.tsx`: - Badge component showing subscription status with color coding: - "trialing" -> blue badge with trial end date - "active" -> green badge - "past_due" -> amber badge with "Payment required" text - "canceled" -> red badge - "none" -> gray badge "No subscription" - Uses shadcn/ui Badge component
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>
After completion, create `.planning/phases/03-operator-experience/03-03-SUMMARY.md`