Implement subscription-based AI access with 250 generations/month at $5/month or $50/year. Changes: - Backend: Stripe service, payment routes, webhook handlers, generation tracking - Frontend: Upgrade page with pricing, payment success/cancel pages, UI prompts - Database: Add subscription fields to users, payments table, migrations - Config: Stripe env vars to .env.example, docker-compose.prod.yml, PRODUCTION.md - Tests: Payment route tests, component tests, subscription hook tests Users without AI access see upgrade prompts; subscribers see remaining generation count.
226 lines
7.6 KiB
Markdown
226 lines
7.6 KiB
Markdown
# Kaboot Payment Feature Implementation Plan
|
|
|
|
## Overview
|
|
|
|
Add Stripe subscription payments to allow users to pay for AI access (`kaboot-ai-access`). Users get 250 AI generations per month for $5/month (or yearly equivalent).
|
|
|
|
### Pricing Model
|
|
- **Monthly**: $5/month for 250 AI generations
|
|
- **Yearly**: $50/year for 250 AI generations/month (save ~17%)
|
|
- **Grace Period**: 1 day after failed payment before revoking access
|
|
- **Refund Policy**: 7-day money-back guarantee
|
|
|
|
---
|
|
|
|
## Implementation Checklist
|
|
|
|
### Phase 1: Backend Infrastructure
|
|
|
|
- [x] **1.1** Add Stripe dependency to server
|
|
- `npm install stripe` in server directory
|
|
- File: `server/package.json`
|
|
|
|
- [x] **1.2** Add environment variables
|
|
- `STRIPE_SECRET_KEY` - Stripe secret key
|
|
- `STRIPE_WEBHOOK_SECRET` - Webhook signing secret
|
|
- `STRIPE_PRICE_ID_MONTHLY` - Monthly price ID
|
|
- `STRIPE_PRICE_ID_YEARLY` - Yearly price ID
|
|
- Files: `.env.example`, `docs/PRODUCTION.md`
|
|
|
|
- [x] **1.3** Database migration - Add subscription and generation tracking
|
|
- Add `stripe_customer_id` to users table
|
|
- Add `subscription_status` (none, active, past_due, canceled)
|
|
- Add `subscription_id` for Stripe subscription ID
|
|
- Add `subscription_current_period_end` for billing cycle
|
|
- Add `generation_count` for current period usage
|
|
- Add `generation_reset_date` for when to reset count
|
|
- Create `payments` table for payment history
|
|
- File: `server/src/db/schema.sql`
|
|
|
|
- [x] **1.4** Create Stripe service
|
|
- Initialize Stripe client
|
|
- Create/retrieve customer helper
|
|
- Create checkout session helper
|
|
- Create customer portal session helper
|
|
- File: `server/src/services/stripe.ts`
|
|
|
|
- [x] **1.5** Create payments routes
|
|
- `POST /api/payments/checkout` - Create Stripe Checkout session
|
|
- `POST /api/payments/webhook` - Handle Stripe webhooks (raw body)
|
|
- `GET /api/payments/status` - Get subscription & generation status
|
|
- `POST /api/payments/portal` - Create customer portal session
|
|
- File: `server/src/routes/payments.ts`
|
|
|
|
- [x] **1.6** Implement webhook handlers
|
|
- `checkout.session.completed` - Activate subscription, set generation quota
|
|
- `customer.subscription.updated` - Sync status changes
|
|
- `customer.subscription.deleted` - Mark as canceled
|
|
- `invoice.payment_failed` - Set past_due status
|
|
- `invoice.paid` - Reset generation count on renewal
|
|
- File: `server/src/routes/payments.ts`
|
|
|
|
- [x] **1.7** Update AI access middleware
|
|
- Check subscription status OR existing group membership
|
|
- Check generation count against limit (250)
|
|
- Increment generation count on AI use
|
|
- Return remaining generations in response
|
|
- Files: `server/src/middleware/auth.ts`, `server/src/routes/ai.ts` (or equivalent)
|
|
|
|
- [x] **1.8** Register payments router in main app
|
|
- File: `server/src/index.ts`
|
|
|
|
### Phase 2: Frontend - Upgrade Page
|
|
|
|
- [x] **2.1** Create UpgradePage component
|
|
- Pricing card with monthly/yearly toggle
|
|
- Feature comparison (Free vs Pro)
|
|
- CTA button triggering Stripe Checkout
|
|
- Trust signals (secure payment, money-back guarantee)
|
|
- File: `components/UpgradePage.tsx`
|
|
|
|
- [x] **2.2** Create PaymentResult component
|
|
- Success state with confetti
|
|
- Cancel/return state
|
|
- File: `components/PaymentResult.tsx`
|
|
|
|
- [x] **2.3** Add routes to App.tsx
|
|
- `/upgrade` route
|
|
- `/payment/success` route
|
|
- `/payment/cancel` route
|
|
- File: `App.tsx`
|
|
|
|
- [x] **2.4** Create payments API service (integrated in UpgradePage)
|
|
- `createCheckoutSession(planType: 'monthly' | 'yearly')`
|
|
- `getSubscriptionStatus()`
|
|
- `createPortalSession()`
|
|
- File: `services/paymentsApi.ts`
|
|
|
|
- [x] **2.5** Update UI to show generation usage
|
|
- Show remaining generations in preferences/header
|
|
- Show upgrade CTA when generations low or user is free tier
|
|
- Files: Various components
|
|
|
|
- [x] **2.6** Add upgrade prompts in AI generation flow
|
|
- When user tries AI generation without access
|
|
- When user is low on generations
|
|
- Files: Components using AI generation
|
|
|
|
### Phase 3: Production Updates
|
|
|
|
- [x] **3.1** Update docker-compose.prod.yml
|
|
- Add Stripe environment variables to backend service
|
|
- File: `docker-compose.prod.yml`
|
|
|
|
- [x] **3.2** Update PRODUCTION.md documentation
|
|
- Add Stripe configuration section
|
|
- Add webhook setup instructions
|
|
- Add Stripe Dashboard product setup
|
|
- File: `docs/PRODUCTION.md`
|
|
|
|
- [x] **3.3** Update setup-prod.sh script (not needed - manual env config)
|
|
- Prompt for Stripe keys during setup
|
|
- File: `scripts/setup-prod.sh`
|
|
|
|
### Phase 4: Testing
|
|
|
|
- [ ] **4.1** Test with Stripe test mode
|
|
- Use test API keys
|
|
- Test card: 4242 4242 4242 4242
|
|
|
|
- [ ] **4.2** Test webhook locally
|
|
- Use Stripe CLI: `stripe listen --forward-to localhost:3001/api/payments/webhook`
|
|
|
|
- [ ] **4.3** Test full payment flow
|
|
- Checkout → Success → Access granted → Generations work
|
|
|
|
- [ ] **4.4** Test generation limits
|
|
- Verify count increments
|
|
- Verify block at 250
|
|
- Verify reset on renewal
|
|
|
|
---
|
|
|
|
## Database Schema Changes
|
|
|
|
```sql
|
|
-- Add subscription fields to users table
|
|
ALTER TABLE users ADD COLUMN stripe_customer_id TEXT UNIQUE;
|
|
ALTER TABLE users ADD COLUMN subscription_status TEXT DEFAULT 'none';
|
|
ALTER TABLE users ADD COLUMN subscription_id TEXT;
|
|
ALTER TABLE users ADD COLUMN subscription_current_period_end DATETIME;
|
|
ALTER TABLE users ADD COLUMN generation_count INTEGER DEFAULT 0;
|
|
ALTER TABLE users ADD COLUMN generation_reset_date DATETIME;
|
|
|
|
-- Payments log table
|
|
CREATE TABLE IF NOT EXISTS payments (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id),
|
|
stripe_payment_intent_id TEXT,
|
|
stripe_invoice_id TEXT,
|
|
amount INTEGER NOT NULL,
|
|
currency TEXT DEFAULT 'usd',
|
|
status TEXT NOT NULL,
|
|
description TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_payments_user ON payments(user_id);
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
# Stripe Configuration
|
|
STRIPE_SECRET_KEY=sk_test_... # or sk_live_... for production
|
|
STRIPE_WEBHOOK_SECRET=whsec_... # From Stripe Dashboard or CLI
|
|
STRIPE_PRICE_ID_MONTHLY=price_... # Monthly plan price ID
|
|
STRIPE_PRICE_ID_YEARLY=price_... # Yearly plan price ID
|
|
```
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
| Method | Endpoint | Auth | Description |
|
|
|--------|----------|------|-------------|
|
|
| `POST` | `/api/payments/checkout` | Required | Create Stripe Checkout session |
|
|
| `POST` | `/api/payments/webhook` | Stripe Sig | Handle Stripe webhook events |
|
|
| `GET` | `/api/payments/status` | Required | Get subscription & generation status |
|
|
| `POST` | `/api/payments/portal` | Required | Create Stripe Customer Portal session |
|
|
|
|
---
|
|
|
|
## Stripe Dashboard Setup
|
|
|
|
1. Create Product: "Kaboot AI Pro"
|
|
2. Add Monthly Price: $5.00/month
|
|
3. Add Yearly Price: $50.00/year
|
|
4. Copy Price IDs to environment variables
|
|
5. Set up Webhook endpoint: `https://your-domain.com/api/payments/webhook`
|
|
6. Subscribe to events:
|
|
- `checkout.session.completed`
|
|
- `customer.subscription.created`
|
|
- `customer.subscription.updated`
|
|
- `customer.subscription.deleted`
|
|
- `invoice.paid`
|
|
- `invoice.payment_failed`
|
|
|
|
---
|
|
|
|
## Generation Tracking Logic
|
|
|
|
1. On subscription activation: Set `generation_count = 0`, `generation_reset_date = period_end`
|
|
2. On each AI generation: Increment `generation_count`
|
|
3. Before AI generation: Check `generation_count < 250`
|
|
4. On `invoice.paid` (renewal): Reset `generation_count = 0`, update `generation_reset_date`
|
|
5. Return `remaining_generations = 250 - generation_count` in API responses
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- Existing `kaboot-ai-access` group users (via Authentik) get unlimited access (grandfathered)
|
|
- Subscription users get 250 generations/month regardless of Authentik group
|
|
- Both access methods are valid - check either condition in middleware
|