@codaijs/keel 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/__tests__/sail-installer.test.js +25 -25
  2. package/dist/sail-installer.js +174 -174
  3. package/dist/scaffold.js +68 -68
  4. package/package.json +58 -58
  5. package/sails/_template/addon.json +20 -20
  6. package/sails/_template/install.ts +402 -402
  7. package/sails/admin-dashboard/README.md +117 -117
  8. package/sails/admin-dashboard/addon.json +28 -28
  9. package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -34
  10. package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -243
  11. package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -40
  12. package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -240
  13. package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -149
  14. package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -173
  15. package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -203
  16. package/sails/admin-dashboard/install.ts +305 -305
  17. package/sails/analytics/README.md +178 -178
  18. package/sails/analytics/addon.json +27 -27
  19. package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -58
  20. package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -64
  21. package/sails/analytics/files/frontend/lib/analytics.ts +103 -103
  22. package/sails/analytics/install.ts +297 -297
  23. package/sails/file-uploads/addon.json +30 -30
  24. package/sails/file-uploads/files/backend/routes/files.ts +198 -198
  25. package/sails/file-uploads/files/backend/schema/files.ts +36 -36
  26. package/sails/file-uploads/files/backend/services/file-storage.ts +128 -128
  27. package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -248
  28. package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -147
  29. package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -106
  30. package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -118
  31. package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -37
  32. package/sails/file-uploads/install.ts +466 -466
  33. package/sails/gdpr/README.md +174 -174
  34. package/sails/gdpr/addon.json +27 -27
  35. package/sails/gdpr/files/backend/routes/gdpr.ts +140 -140
  36. package/sails/gdpr/files/backend/services/gdpr.ts +293 -293
  37. package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -97
  38. package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -192
  39. package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -75
  40. package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -186
  41. package/sails/gdpr/install.ts +756 -756
  42. package/sails/google-oauth/README.md +121 -121
  43. package/sails/google-oauth/addon.json +22 -22
  44. package/sails/google-oauth/files/GoogleButton.tsx +50 -50
  45. package/sails/google-oauth/install.ts +252 -252
  46. package/sails/i18n/README.md +193 -193
  47. package/sails/i18n/addon.json +30 -30
  48. package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -108
  49. package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -31
  50. package/sails/i18n/files/frontend/lib/i18n.ts +32 -32
  51. package/sails/i18n/files/frontend/locales/de/common.json +44 -44
  52. package/sails/i18n/files/frontend/locales/en/common.json +44 -44
  53. package/sails/i18n/install.ts +407 -407
  54. package/sails/push-notifications/README.md +163 -163
  55. package/sails/push-notifications/addon.json +31 -31
  56. package/sails/push-notifications/files/backend/routes/notifications.ts +153 -153
  57. package/sails/push-notifications/files/backend/schema/notifications.ts +31 -31
  58. package/sails/push-notifications/files/backend/services/notifications.ts +117 -117
  59. package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -12
  60. package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -154
  61. package/sails/push-notifications/install.ts +384 -384
  62. package/sails/r2-storage/addon.json +29 -29
  63. package/sails/r2-storage/files/backend/services/storage.ts +71 -71
  64. package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -167
  65. package/sails/r2-storage/install.ts +412 -412
  66. package/sails/rate-limiting/addon.json +20 -20
  67. package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -104
  68. package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -137
  69. package/sails/rate-limiting/install.ts +300 -300
  70. package/sails/registry.json +107 -107
  71. package/sails/stripe/README.md +214 -214
  72. package/sails/stripe/addon.json +24 -24
  73. package/sails/stripe/files/backend/routes/stripe.ts +154 -154
  74. package/sails/stripe/files/backend/schema/stripe.ts +74 -74
  75. package/sails/stripe/files/backend/services/stripe.ts +224 -224
  76. package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -135
  77. package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -86
  78. package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -116
  79. package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -226
  80. package/sails/stripe/install.ts +378 -378
@@ -1,107 +1,107 @@
1
- {
2
- "version": "1.0.0",
3
- "sails": [
4
- {
5
- "name": "google-oauth",
6
- "displayName": "Google OAuth",
7
- "description": "Google OAuth provider for BetterAuth — adds Google sign-in button",
8
- "category": "auth",
9
- "version": "1.0.0",
10
- "routes": [],
11
- "envVars": ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"],
12
- "conflicts": []
13
- },
14
- {
15
- "name": "stripe",
16
- "displayName": "Stripe Payments",
17
- "description": "Stripe subscriptions with checkout, webhooks, and customer portal",
18
- "category": "payments",
19
- "version": "1.0.0",
20
- "routes": ["/api/stripe"],
21
- "envVars": ["STRIPE_SECRET_KEY", "STRIPE_PUBLISHABLE_KEY", "STRIPE_WEBHOOK_SECRET"],
22
- "conflicts": []
23
- },
24
- {
25
- "name": "r2-storage",
26
- "displayName": "Cloudflare R2 Storage",
27
- "description": "File uploads via Cloudflare R2 with presigned URLs. Adds profile picture upload.",
28
- "category": "storage",
29
- "version": "1.0.0",
30
- "routes": [],
31
- "envVars": ["R2_ACCOUNT_ID", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY", "R2_BUCKET_NAME", "R2_PUBLIC_URL"],
32
- "conflicts": ["file-uploads"]
33
- },
34
- {
35
- "name": "push-notifications",
36
- "displayName": "Push Notifications",
37
- "description": "Push notifications via Capacitor + Firebase Cloud Messaging with device token management and server-side sending",
38
- "category": "mobile",
39
- "version": "1.0.0",
40
- "routes": ["/api/notifications"],
41
- "envVars": ["FIREBASE_PROJECT_ID", "FIREBASE_PRIVATE_KEY", "FIREBASE_CLIENT_EMAIL"],
42
- "conflicts": []
43
- },
44
- {
45
- "name": "analytics",
46
- "displayName": "PostHog Analytics",
47
- "description": "Privacy-friendly analytics with PostHog — auto page views, user identification, and custom event tracking",
48
- "category": "tracking",
49
- "version": "1.0.0",
50
- "routes": [],
51
- "envVars": ["VITE_POSTHOG_KEY", "VITE_POSTHOG_HOST"],
52
- "conflicts": []
53
- },
54
- {
55
- "name": "admin-dashboard",
56
- "displayName": "Admin Dashboard",
57
- "description": "Admin dashboard for user management, metrics, and basic analytics",
58
- "category": "admin",
59
- "version": "1.0.0",
60
- "routes": ["/api/admin"],
61
- "envVars": ["ADMIN_EMAILS"],
62
- "conflicts": []
63
- },
64
- {
65
- "name": "i18n",
66
- "displayName": "Internationalization",
67
- "description": "Multi-language support with i18next, react-i18next, and language detection",
68
- "category": "localization",
69
- "version": "1.0.0",
70
- "routes": [],
71
- "envVars": [],
72
- "conflicts": []
73
- },
74
- {
75
- "name": "rate-limiting",
76
- "displayName": "Rate Limiting",
77
- "description": "API rate limiting middleware",
78
- "category": "security",
79
- "version": "1.0.0",
80
- "status": "planned",
81
- "routes": [],
82
- "envVars": [],
83
- "conflicts": []
84
- },
85
- {
86
- "name": "file-uploads",
87
- "displayName": "File Uploads",
88
- "description": "Generic file upload system with R2/S3 storage",
89
- "category": "storage",
90
- "version": "1.0.0",
91
- "status": "planned",
92
- "routes": ["/api/files"],
93
- "envVars": ["S3_ENDPOINT", "S3_ACCESS_KEY_ID", "S3_SECRET_ACCESS_KEY", "S3_BUCKET_NAME", "S3_PUBLIC_URL", "S3_REGION"],
94
- "conflicts": ["r2-storage"]
95
- },
96
- {
97
- "name": "gdpr",
98
- "displayName": "GDPR/DSGVO Compliance",
99
- "description": "Full GDPR compliance: consent tracking, data export, account deletion (30-day grace period), privacy policy page",
100
- "category": "compliance",
101
- "version": "1.0.0",
102
- "routes": ["/api/gdpr"],
103
- "envVars": ["DELETION_CRON_SECRET"],
104
- "conflicts": []
105
- }
106
- ]
107
- }
1
+ {
2
+ "version": "1.0.0",
3
+ "sails": [
4
+ {
5
+ "name": "google-oauth",
6
+ "displayName": "Google OAuth",
7
+ "description": "Google OAuth provider for BetterAuth — adds Google sign-in button",
8
+ "category": "auth",
9
+ "version": "1.0.0",
10
+ "routes": [],
11
+ "envVars": ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"],
12
+ "conflicts": []
13
+ },
14
+ {
15
+ "name": "stripe",
16
+ "displayName": "Stripe Payments",
17
+ "description": "Stripe subscriptions with checkout, webhooks, and customer portal",
18
+ "category": "payments",
19
+ "version": "1.0.0",
20
+ "routes": ["/api/stripe"],
21
+ "envVars": ["STRIPE_SECRET_KEY", "STRIPE_PUBLISHABLE_KEY", "STRIPE_WEBHOOK_SECRET"],
22
+ "conflicts": []
23
+ },
24
+ {
25
+ "name": "r2-storage",
26
+ "displayName": "Cloudflare R2 Storage",
27
+ "description": "File uploads via Cloudflare R2 with presigned URLs. Adds profile picture upload.",
28
+ "category": "storage",
29
+ "version": "1.0.0",
30
+ "routes": [],
31
+ "envVars": ["R2_ACCOUNT_ID", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY", "R2_BUCKET_NAME", "R2_PUBLIC_URL"],
32
+ "conflicts": ["file-uploads"]
33
+ },
34
+ {
35
+ "name": "push-notifications",
36
+ "displayName": "Push Notifications",
37
+ "description": "Push notifications via Capacitor + Firebase Cloud Messaging with device token management and server-side sending",
38
+ "category": "mobile",
39
+ "version": "1.0.0",
40
+ "routes": ["/api/notifications"],
41
+ "envVars": ["FIREBASE_PROJECT_ID", "FIREBASE_PRIVATE_KEY", "FIREBASE_CLIENT_EMAIL"],
42
+ "conflicts": []
43
+ },
44
+ {
45
+ "name": "analytics",
46
+ "displayName": "PostHog Analytics",
47
+ "description": "Privacy-friendly analytics with PostHog — auto page views, user identification, and custom event tracking",
48
+ "category": "tracking",
49
+ "version": "1.0.0",
50
+ "routes": [],
51
+ "envVars": ["VITE_POSTHOG_KEY", "VITE_POSTHOG_HOST"],
52
+ "conflicts": []
53
+ },
54
+ {
55
+ "name": "admin-dashboard",
56
+ "displayName": "Admin Dashboard",
57
+ "description": "Admin dashboard for user management, metrics, and basic analytics",
58
+ "category": "admin",
59
+ "version": "1.0.0",
60
+ "routes": ["/api/admin"],
61
+ "envVars": ["ADMIN_EMAILS"],
62
+ "conflicts": []
63
+ },
64
+ {
65
+ "name": "i18n",
66
+ "displayName": "Internationalization",
67
+ "description": "Multi-language support with i18next, react-i18next, and language detection",
68
+ "category": "localization",
69
+ "version": "1.0.0",
70
+ "routes": [],
71
+ "envVars": [],
72
+ "conflicts": []
73
+ },
74
+ {
75
+ "name": "rate-limiting",
76
+ "displayName": "Rate Limiting",
77
+ "description": "API rate limiting middleware",
78
+ "category": "security",
79
+ "version": "1.0.0",
80
+ "status": "planned",
81
+ "routes": [],
82
+ "envVars": [],
83
+ "conflicts": []
84
+ },
85
+ {
86
+ "name": "file-uploads",
87
+ "displayName": "File Uploads",
88
+ "description": "Generic file upload system with R2/S3 storage",
89
+ "category": "storage",
90
+ "version": "1.0.0",
91
+ "status": "planned",
92
+ "routes": ["/api/files"],
93
+ "envVars": ["S3_ENDPOINT", "S3_ACCESS_KEY_ID", "S3_SECRET_ACCESS_KEY", "S3_BUCKET_NAME", "S3_PUBLIC_URL", "S3_REGION"],
94
+ "conflicts": ["r2-storage"]
95
+ },
96
+ {
97
+ "name": "gdpr",
98
+ "displayName": "GDPR/DSGVO Compliance",
99
+ "description": "Full GDPR compliance: consent tracking, data export, account deletion (30-day grace period), privacy policy page",
100
+ "category": "compliance",
101
+ "version": "1.0.0",
102
+ "routes": ["/api/gdpr"],
103
+ "envVars": ["DELETION_CRON_SECRET"],
104
+ "conflicts": []
105
+ }
106
+ ]
107
+ }
@@ -1,214 +1,214 @@
1
- # Stripe Payments Sail
2
-
3
- Adds subscription management to your keel application using Stripe Checkout, webhooks, and the Customer Portal.
4
-
5
- ## Features
6
-
7
- - Stripe Checkout for subscription payments
8
- - Webhook handling for subscription lifecycle events
9
- - Customer Portal for self-service subscription management
10
- - Pricing page with plan cards
11
- - Subscription status component for dashboards and settings
12
- - Drizzle ORM schema for customers and subscriptions
13
-
14
- ## Prerequisites
15
-
16
- - A Stripe account (https://stripe.com)
17
- - Stripe CLI (for local webhook testing)
18
-
19
- ## Installation
20
-
21
- ```bash
22
- npx tsx sails/stripe/install.ts
23
- ```
24
-
25
- The installer will prompt for your Stripe API keys and configure everything automatically.
26
-
27
- ## Manual Setup: Stripe Dashboard
28
-
29
- ### 1. Get API Keys
30
-
31
- 1. Go to https://dashboard.stripe.com/test/apikeys
32
- 2. Copy the **Publishable key** (pk_test_...)
33
- 3. Copy the **Secret key** (sk_test_...)
34
- 4. Add both to your `.env` file
35
-
36
- ### 2. Create Products and Prices
37
-
38
- 1. Go to https://dashboard.stripe.com/test/products
39
- 2. Click **Add product**
40
- 3. Create your subscription plans (e.g., "Pro" and "Enterprise")
41
- 4. For each product, add a **Recurring price** (e.g., $19/month)
42
- 5. Copy the Price IDs (price_...) and update `src/pages/Pricing.tsx`
43
-
44
- ### 3. Set Up Webhooks
45
-
46
- #### Development (using Stripe CLI)
47
-
48
- ```bash
49
- # Install Stripe CLI: https://stripe.com/docs/stripe-cli
50
- stripe login
51
- stripe listen --forward-to localhost:3000/api/stripe/webhook
52
- ```
53
-
54
- The CLI will output a webhook signing secret (whsec_...). Add it to your `.env` as `STRIPE_WEBHOOK_SECRET`.
55
-
56
- #### Production
57
-
58
- 1. Go to https://dashboard.stripe.com/webhooks
59
- 2. Click **Add endpoint**
60
- 3. Set the URL to `https://yourdomain.com/api/stripe/webhook`
61
- 4. Select events:
62
- - `checkout.session.completed`
63
- - `customer.subscription.updated`
64
- - `customer.subscription.deleted`
65
- 5. Click **Add endpoint**
66
- 6. Copy the **Signing secret** and set it as `STRIPE_WEBHOOK_SECRET`
67
-
68
- ### 4. Configure Customer Portal
69
-
70
- 1. Go to https://dashboard.stripe.com/test/settings/billing/portal
71
- 2. Enable the features you want (cancel, update payment method, etc.)
72
- 3. Save changes
73
-
74
- ### 5. Environment Variables
75
-
76
- ```env
77
- STRIPE_SECRET_KEY=sk_test_...
78
- STRIPE_PUBLISHABLE_KEY=pk_test_...
79
- STRIPE_WEBHOOK_SECRET=whsec_...
80
- ```
81
-
82
- ## Architecture
83
-
84
- ### Database Schema
85
-
86
- **stripe_customers**
87
- | Column | Type | Description |
88
- |--------|------|-------------|
89
- | id | uuid | Primary key |
90
- | user_id | text | FK to users table |
91
- | stripe_customer_id | text | Stripe Customer ID |
92
- | created_at | timestamp | Creation time |
93
- | updated_at | timestamp | Last update time |
94
-
95
- **stripe_subscriptions**
96
- | Column | Type | Description |
97
- |--------|------|-------------|
98
- | id | uuid | Primary key |
99
- | customer_id | uuid | FK to stripe_customers |
100
- | stripe_subscription_id | text | Stripe Subscription ID |
101
- | status | text | active, trialing, past_due, canceled, etc. |
102
- | stripe_price_id | text | The Stripe Price ID |
103
- | current_period_start | timestamp | Current billing period start |
104
- | current_period_end | timestamp | Current billing period end |
105
- | cancel_at_period_end | boolean | Whether sub cancels at period end |
106
- | created_at | timestamp | Creation time |
107
- | updated_at | timestamp | Last update time |
108
-
109
- ### API Routes
110
-
111
- | Method | Path | Auth | Description |
112
- |--------|------|------|-------------|
113
- | POST | /api/stripe/create-checkout-session | Yes | Create a Checkout session |
114
- | POST | /api/stripe/create-portal-session | Yes | Create a Customer Portal session |
115
- | POST | /api/stripe/webhook | No | Stripe webhook handler |
116
- | GET | /api/stripe/subscription | Yes | Get current subscription |
117
-
118
- ### Webhook Flow
119
-
120
- 1. Stripe fires an event (e.g., `checkout.session.completed`)
121
- 2. Express receives the raw body at `/api/stripe/webhook`
122
- 3. Signature is verified using `STRIPE_WEBHOOK_SECRET`
123
- 4. The event handler updates the database accordingly
124
- 5. Returns 200 to acknowledge receipt
125
-
126
- ### Important: Raw Body Middleware
127
-
128
- The webhook endpoint requires the raw request body for signature verification. Make sure your Express app has this middleware **before** `express.json()`:
129
-
130
- ```typescript
131
- app.use("/api/stripe/webhook", express.raw({ type: "application/json" }));
132
- ```
133
-
134
- The installer adds this automatically.
135
-
136
- ## Frontend Components
137
-
138
- ### Pricing Page (`/pricing`)
139
-
140
- Displays plan cards with features and subscribe buttons. Redirects to Stripe Checkout when a plan is selected.
141
-
142
- ### Checkout Page (`/checkout/success`, `/checkout/cancel`)
143
-
144
- Post-checkout landing pages for successful payments and cancellations.
145
-
146
- ### SubscriptionStatus Component
147
-
148
- Drop-in component showing the current subscription status with a "Manage" button that opens the Stripe Customer Portal. Use it in settings or dashboard pages:
149
-
150
- ```tsx
151
- import { SubscriptionStatus } from "@/components/stripe/SubscriptionStatus";
152
-
153
- function SettingsPage() {
154
- return (
155
- <div>
156
- <h2>Subscription</h2>
157
- <SubscriptionStatus />
158
- </div>
159
- );
160
- }
161
- ```
162
-
163
- ### useSubscription Hook
164
-
165
- ```tsx
166
- import { useSubscription, isSubscriptionActive } from "@/hooks/useSubscription";
167
-
168
- function MyComponent() {
169
- const { subscription, isLoading } = useSubscription();
170
-
171
- if (isSubscriptionActive(subscription)) {
172
- return <PremiumContent />;
173
- }
174
-
175
- return <UpgradePrompt />;
176
- }
177
- ```
178
-
179
- ## Testing
180
-
181
- ### Test Cards
182
-
183
- Use Stripe's test card numbers:
184
-
185
- | Card | Number | Scenario |
186
- |------|--------|----------|
187
- | Visa | 4242 4242 4242 4242 | Successful payment |
188
- | Visa (declined) | 4000 0000 0000 0002 | Card declined |
189
- | Visa (3D Secure) | 4000 0025 0000 3155 | Requires authentication |
190
-
191
- Use any future expiry date, any 3-digit CVC, and any ZIP code.
192
-
193
- ### Testing Webhooks Locally
194
-
195
- ```bash
196
- # Terminal 1: Start your dev server
197
- npm run dev
198
-
199
- # Terminal 2: Forward Stripe events
200
- stripe listen --forward-to localhost:3000/api/stripe/webhook
201
-
202
- # Terminal 3: Trigger test events
203
- stripe trigger checkout.session.completed
204
- stripe trigger customer.subscription.updated
205
- stripe trigger customer.subscription.deleted
206
- ```
207
-
208
- ## Going to Production
209
-
210
- 1. Switch from test keys to live keys in your environment
211
- 2. Create live products/prices and update Price IDs
212
- 3. Set up the production webhook endpoint
213
- 4. Configure the Customer Portal for live mode
214
- 5. Test the full flow with a real card
1
+ # Stripe Payments Sail
2
+
3
+ Adds subscription management to your keel application using Stripe Checkout, webhooks, and the Customer Portal.
4
+
5
+ ## Features
6
+
7
+ - Stripe Checkout for subscription payments
8
+ - Webhook handling for subscription lifecycle events
9
+ - Customer Portal for self-service subscription management
10
+ - Pricing page with plan cards
11
+ - Subscription status component for dashboards and settings
12
+ - Drizzle ORM schema for customers and subscriptions
13
+
14
+ ## Prerequisites
15
+
16
+ - A Stripe account (https://stripe.com)
17
+ - Stripe CLI (for local webhook testing)
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npx tsx sails/stripe/install.ts
23
+ ```
24
+
25
+ The installer will prompt for your Stripe API keys and configure everything automatically.
26
+
27
+ ## Manual Setup: Stripe Dashboard
28
+
29
+ ### 1. Get API Keys
30
+
31
+ 1. Go to https://dashboard.stripe.com/test/apikeys
32
+ 2. Copy the **Publishable key** (pk_test_...)
33
+ 3. Copy the **Secret key** (sk_test_...)
34
+ 4. Add both to your `.env` file
35
+
36
+ ### 2. Create Products and Prices
37
+
38
+ 1. Go to https://dashboard.stripe.com/test/products
39
+ 2. Click **Add product**
40
+ 3. Create your subscription plans (e.g., "Pro" and "Enterprise")
41
+ 4. For each product, add a **Recurring price** (e.g., $19/month)
42
+ 5. Copy the Price IDs (price_...) and update `src/pages/Pricing.tsx`
43
+
44
+ ### 3. Set Up Webhooks
45
+
46
+ #### Development (using Stripe CLI)
47
+
48
+ ```bash
49
+ # Install Stripe CLI: https://stripe.com/docs/stripe-cli
50
+ stripe login
51
+ stripe listen --forward-to localhost:3000/api/stripe/webhook
52
+ ```
53
+
54
+ The CLI will output a webhook signing secret (whsec_...). Add it to your `.env` as `STRIPE_WEBHOOK_SECRET`.
55
+
56
+ #### Production
57
+
58
+ 1. Go to https://dashboard.stripe.com/webhooks
59
+ 2. Click **Add endpoint**
60
+ 3. Set the URL to `https://yourdomain.com/api/stripe/webhook`
61
+ 4. Select events:
62
+ - `checkout.session.completed`
63
+ - `customer.subscription.updated`
64
+ - `customer.subscription.deleted`
65
+ 5. Click **Add endpoint**
66
+ 6. Copy the **Signing secret** and set it as `STRIPE_WEBHOOK_SECRET`
67
+
68
+ ### 4. Configure Customer Portal
69
+
70
+ 1. Go to https://dashboard.stripe.com/test/settings/billing/portal
71
+ 2. Enable the features you want (cancel, update payment method, etc.)
72
+ 3. Save changes
73
+
74
+ ### 5. Environment Variables
75
+
76
+ ```env
77
+ STRIPE_SECRET_KEY=sk_test_...
78
+ STRIPE_PUBLISHABLE_KEY=pk_test_...
79
+ STRIPE_WEBHOOK_SECRET=whsec_...
80
+ ```
81
+
82
+ ## Architecture
83
+
84
+ ### Database Schema
85
+
86
+ **stripe_customers**
87
+ | Column | Type | Description |
88
+ |--------|------|-------------|
89
+ | id | uuid | Primary key |
90
+ | user_id | text | FK to users table |
91
+ | stripe_customer_id | text | Stripe Customer ID |
92
+ | created_at | timestamp | Creation time |
93
+ | updated_at | timestamp | Last update time |
94
+
95
+ **stripe_subscriptions**
96
+ | Column | Type | Description |
97
+ |--------|------|-------------|
98
+ | id | uuid | Primary key |
99
+ | customer_id | uuid | FK to stripe_customers |
100
+ | stripe_subscription_id | text | Stripe Subscription ID |
101
+ | status | text | active, trialing, past_due, canceled, etc. |
102
+ | stripe_price_id | text | The Stripe Price ID |
103
+ | current_period_start | timestamp | Current billing period start |
104
+ | current_period_end | timestamp | Current billing period end |
105
+ | cancel_at_period_end | boolean | Whether sub cancels at period end |
106
+ | created_at | timestamp | Creation time |
107
+ | updated_at | timestamp | Last update time |
108
+
109
+ ### API Routes
110
+
111
+ | Method | Path | Auth | Description |
112
+ |--------|------|------|-------------|
113
+ | POST | /api/stripe/create-checkout-session | Yes | Create a Checkout session |
114
+ | POST | /api/stripe/create-portal-session | Yes | Create a Customer Portal session |
115
+ | POST | /api/stripe/webhook | No | Stripe webhook handler |
116
+ | GET | /api/stripe/subscription | Yes | Get current subscription |
117
+
118
+ ### Webhook Flow
119
+
120
+ 1. Stripe fires an event (e.g., `checkout.session.completed`)
121
+ 2. Express receives the raw body at `/api/stripe/webhook`
122
+ 3. Signature is verified using `STRIPE_WEBHOOK_SECRET`
123
+ 4. The event handler updates the database accordingly
124
+ 5. Returns 200 to acknowledge receipt
125
+
126
+ ### Important: Raw Body Middleware
127
+
128
+ The webhook endpoint requires the raw request body for signature verification. Make sure your Express app has this middleware **before** `express.json()`:
129
+
130
+ ```typescript
131
+ app.use("/api/stripe/webhook", express.raw({ type: "application/json" }));
132
+ ```
133
+
134
+ The installer adds this automatically.
135
+
136
+ ## Frontend Components
137
+
138
+ ### Pricing Page (`/pricing`)
139
+
140
+ Displays plan cards with features and subscribe buttons. Redirects to Stripe Checkout when a plan is selected.
141
+
142
+ ### Checkout Page (`/checkout/success`, `/checkout/cancel`)
143
+
144
+ Post-checkout landing pages for successful payments and cancellations.
145
+
146
+ ### SubscriptionStatus Component
147
+
148
+ Drop-in component showing the current subscription status with a "Manage" button that opens the Stripe Customer Portal. Use it in settings or dashboard pages:
149
+
150
+ ```tsx
151
+ import { SubscriptionStatus } from "@/components/stripe/SubscriptionStatus";
152
+
153
+ function SettingsPage() {
154
+ return (
155
+ <div>
156
+ <h2>Subscription</h2>
157
+ <SubscriptionStatus />
158
+ </div>
159
+ );
160
+ }
161
+ ```
162
+
163
+ ### useSubscription Hook
164
+
165
+ ```tsx
166
+ import { useSubscription, isSubscriptionActive } from "@/hooks/useSubscription";
167
+
168
+ function MyComponent() {
169
+ const { subscription, isLoading } = useSubscription();
170
+
171
+ if (isSubscriptionActive(subscription)) {
172
+ return <PremiumContent />;
173
+ }
174
+
175
+ return <UpgradePrompt />;
176
+ }
177
+ ```
178
+
179
+ ## Testing
180
+
181
+ ### Test Cards
182
+
183
+ Use Stripe's test card numbers:
184
+
185
+ | Card | Number | Scenario |
186
+ |------|--------|----------|
187
+ | Visa | 4242 4242 4242 4242 | Successful payment |
188
+ | Visa (declined) | 4000 0000 0000 0002 | Card declined |
189
+ | Visa (3D Secure) | 4000 0025 0000 3155 | Requires authentication |
190
+
191
+ Use any future expiry date, any 3-digit CVC, and any ZIP code.
192
+
193
+ ### Testing Webhooks Locally
194
+
195
+ ```bash
196
+ # Terminal 1: Start your dev server
197
+ npm run dev
198
+
199
+ # Terminal 2: Forward Stripe events
200
+ stripe listen --forward-to localhost:3000/api/stripe/webhook
201
+
202
+ # Terminal 3: Trigger test events
203
+ stripe trigger checkout.session.completed
204
+ stripe trigger customer.subscription.updated
205
+ stripe trigger customer.subscription.deleted
206
+ ```
207
+
208
+ ## Going to Production
209
+
210
+ 1. Switch from test keys to live keys in your environment
211
+ 2. Create live products/prices and update Price IDs
212
+ 3. Set up the production webhook endpoint
213
+ 4. Configure the Customer Portal for live mode
214
+ 5. Test the full flow with a real card