Add Stripe payment integration for AI subscriptions
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.
This commit is contained in:
parent
3c54a0f4d9
commit
2e12edc249
22 changed files with 2866 additions and 21 deletions
226
docs/PAYMENT_FEATURE_PLAN.md
Normal file
226
docs/PAYMENT_FEATURE_PLAN.md
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# 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
|
||||
|
|
@ -204,6 +204,45 @@ The frontend is built inside Docker using the `KABOOT_DOMAIN` and `AUTH_DOMAIN`
|
|||
|
||||
The `setup-prod.sh` script sets these domain variables automatically.
|
||||
|
||||
### Stripe Payments Configuration (Optional)
|
||||
|
||||
To enable paid AI access subscriptions, configure Stripe:
|
||||
|
||||
```env
|
||||
# Stripe API Keys (get from https://dashboard.stripe.com/apikeys)
|
||||
STRIPE_SECRET_KEY=sk_live_... # Use sk_test_... for testing
|
||||
STRIPE_WEBHOOK_SECRET=whsec_... # From webhook endpoint configuration
|
||||
STRIPE_PRICE_ID_MONTHLY=price_... # Monthly subscription price ID
|
||||
STRIPE_PRICE_ID_YEARLY=price_... # Yearly subscription price ID
|
||||
```
|
||||
|
||||
#### Stripe Dashboard Setup
|
||||
|
||||
1. **Create a Product** in [Stripe Dashboard](https://dashboard.stripe.com/products):
|
||||
- Name: "Kaboot AI Pro"
|
||||
- Description: "250 AI quiz generations per month"
|
||||
|
||||
2. **Add Pricing**:
|
||||
- Monthly: $5.00/month (recurring)
|
||||
- Yearly: $50.00/year (recurring)
|
||||
- Copy the Price IDs (start with `price_`)
|
||||
|
||||
3. **Configure Webhook**:
|
||||
- Go to [Developers > Webhooks](https://dashboard.stripe.com/webhooks)
|
||||
- Add endpoint: `https://your-domain.com/api/payments/webhook`
|
||||
- Select events:
|
||||
- `checkout.session.completed`
|
||||
- `customer.subscription.updated`
|
||||
- `customer.subscription.deleted`
|
||||
- `invoice.paid`
|
||||
- `invoice.payment_failed`
|
||||
- Copy the Signing Secret (starts with `whsec_`)
|
||||
|
||||
4. **Test with Stripe CLI** (optional, for local development):
|
||||
```bash
|
||||
stripe listen --forward-to localhost:3001/api/payments/webhook
|
||||
```
|
||||
|
||||
## Docker Compose Files
|
||||
|
||||
The project includes pre-configured compose files:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue