@getjack/jack 0.1.19 → 0.1.22
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.
- package/package.json +5 -2
- package/src/commands/down.ts +11 -1
- package/src/commands/init.ts +19 -6
- package/src/commands/new.ts +56 -4
- package/src/commands/publish.ts +1 -1
- package/src/lib/agents.ts +3 -1
- package/src/lib/auth/ensure-auth.test.ts +3 -3
- package/src/lib/control-plane.ts +15 -1
- package/src/lib/deploy-upload.ts +26 -1
- package/src/lib/hooks.ts +232 -1
- package/src/lib/managed-deploy.ts +13 -6
- package/src/lib/managed-down.ts +66 -45
- package/src/lib/progress.ts +76 -5
- package/src/lib/project-list.ts +6 -1
- package/src/lib/project-operations.ts +21 -31
- package/src/lib/project-resolver.ts +1 -1
- package/src/lib/zip-packager.ts +36 -7
- package/src/templates/index.ts +1 -1
- package/src/templates/types.ts +16 -0
- package/templates/CLAUDE.md +172 -5
- package/templates/miniapp/.jack.json +1 -3
- package/templates/saas/.jack.json +154 -0
- package/templates/saas/AGENTS.md +333 -0
- package/templates/saas/bun.lock +925 -0
- package/templates/saas/components.json +21 -0
- package/templates/saas/index.html +12 -0
- package/templates/saas/package.json +75 -0
- package/templates/saas/public/icon.png +0 -0
- package/templates/saas/public/og.png +0 -0
- package/templates/saas/schema.sql +73 -0
- package/templates/saas/src/auth.ts +77 -0
- package/templates/saas/src/client/App.tsx +63 -0
- package/templates/saas/src/client/components/ProtectedRoute.tsx +29 -0
- package/templates/saas/src/client/components/ThemeToggle.tsx +32 -0
- package/templates/saas/src/client/components/ui/accordion.tsx +62 -0
- package/templates/saas/src/client/components/ui/alert-dialog.tsx +133 -0
- package/templates/saas/src/client/components/ui/alert.tsx +60 -0
- package/templates/saas/src/client/components/ui/aspect-ratio.tsx +9 -0
- package/templates/saas/src/client/components/ui/avatar.tsx +39 -0
- package/templates/saas/src/client/components/ui/badge.tsx +39 -0
- package/templates/saas/src/client/components/ui/breadcrumb.tsx +102 -0
- package/templates/saas/src/client/components/ui/button-group.tsx +78 -0
- package/templates/saas/src/client/components/ui/button.tsx +60 -0
- package/templates/saas/src/client/components/ui/card.tsx +75 -0
- package/templates/saas/src/client/components/ui/carousel.tsx +228 -0
- package/templates/saas/src/client/components/ui/chart.tsx +326 -0
- package/templates/saas/src/client/components/ui/checkbox.tsx +29 -0
- package/templates/saas/src/client/components/ui/collapsible.tsx +19 -0
- package/templates/saas/src/client/components/ui/command.tsx +159 -0
- package/templates/saas/src/client/components/ui/context-menu.tsx +224 -0
- package/templates/saas/src/client/components/ui/dialog.tsx +127 -0
- package/templates/saas/src/client/components/ui/drawer.tsx +124 -0
- package/templates/saas/src/client/components/ui/dropdown-menu.tsx +226 -0
- package/templates/saas/src/client/components/ui/empty.tsx +94 -0
- package/templates/saas/src/client/components/ui/field.tsx +232 -0
- package/templates/saas/src/client/components/ui/form.tsx +152 -0
- package/templates/saas/src/client/components/ui/hover-card.tsx +38 -0
- package/templates/saas/src/client/components/ui/input-group.tsx +158 -0
- package/templates/saas/src/client/components/ui/input-otp.tsx +68 -0
- package/templates/saas/src/client/components/ui/input.tsx +21 -0
- package/templates/saas/src/client/components/ui/item.tsx +172 -0
- package/templates/saas/src/client/components/ui/kbd.tsx +28 -0
- package/templates/saas/src/client/components/ui/label.tsx +21 -0
- package/templates/saas/src/client/components/ui/menubar.tsx +250 -0
- package/templates/saas/src/client/components/ui/navigation-menu.tsx +161 -0
- package/templates/saas/src/client/components/ui/pagination.tsx +106 -0
- package/templates/saas/src/client/components/ui/popover.tsx +42 -0
- package/templates/saas/src/client/components/ui/progress.tsx +26 -0
- package/templates/saas/src/client/components/ui/radio-group.tsx +45 -0
- package/templates/saas/src/client/components/ui/resizable.tsx +46 -0
- package/templates/saas/src/client/components/ui/scroll-area.tsx +56 -0
- package/templates/saas/src/client/components/ui/select.tsx +173 -0
- package/templates/saas/src/client/components/ui/separator.tsx +28 -0
- package/templates/saas/src/client/components/ui/sheet.tsx +128 -0
- package/templates/saas/src/client/components/ui/sidebar.tsx +694 -0
- package/templates/saas/src/client/components/ui/skeleton.tsx +13 -0
- package/templates/saas/src/client/components/ui/slider.tsx +58 -0
- package/templates/saas/src/client/components/ui/sonner.tsx +38 -0
- package/templates/saas/src/client/components/ui/spinner.tsx +16 -0
- package/templates/saas/src/client/components/ui/switch.tsx +28 -0
- package/templates/saas/src/client/components/ui/table.tsx +90 -0
- package/templates/saas/src/client/components/ui/tabs.tsx +54 -0
- package/templates/saas/src/client/components/ui/textarea.tsx +18 -0
- package/templates/saas/src/client/components/ui/toggle-group.tsx +80 -0
- package/templates/saas/src/client/components/ui/toggle.tsx +44 -0
- package/templates/saas/src/client/components/ui/tooltip.tsx +57 -0
- package/templates/saas/src/client/hooks/use-mobile.ts +19 -0
- package/templates/saas/src/client/hooks/useAuth.ts +14 -0
- package/templates/saas/src/client/hooks/useSubscription.ts +86 -0
- package/templates/saas/src/client/index.css +165 -0
- package/templates/saas/src/client/lib/auth-client.ts +7 -0
- package/templates/saas/src/client/lib/plans.ts +82 -0
- package/templates/saas/src/client/lib/utils.ts +6 -0
- package/templates/saas/src/client/main.tsx +15 -0
- package/templates/saas/src/client/pages/DashboardPage.tsx +394 -0
- package/templates/saas/src/client/pages/ForgotPasswordPage.tsx +153 -0
- package/templates/saas/src/client/pages/HomePage.tsx +285 -0
- package/templates/saas/src/client/pages/LoginPage.tsx +169 -0
- package/templates/saas/src/client/pages/PricingPage.tsx +467 -0
- package/templates/saas/src/client/pages/ResetPasswordPage.tsx +200 -0
- package/templates/saas/src/client/pages/SignupPage.tsx +192 -0
- package/templates/saas/src/index.ts +208 -0
- package/templates/saas/tsconfig.json +18 -0
- package/templates/saas/vite.config.ts +14 -0
- package/templates/saas/wrangler.jsonc +20 -0
package/templates/CLAUDE.md
CHANGED
|
@@ -119,16 +119,18 @@ Templates can define hooks in `.jack.json` that run at specific lifecycle points
|
|
|
119
119
|
| `url` | `url` | Prints label + URL |
|
|
120
120
|
| `clipboard` | `text` | Prints text |
|
|
121
121
|
| `pause` | _(none)_ | Skipped |
|
|
122
|
-
| `require` | `source`, `key` | Validates, prints setup if provided |
|
|
122
|
+
| `require` | `source`, `key` | Validates, prints setup if provided. Supports `onMissing: "prompt" \| "generate"` |
|
|
123
123
|
| `shell` | `command` | Runs with stdin ignored |
|
|
124
|
-
| `prompt` | `message` | Skipped
|
|
124
|
+
| `prompt` | `message` | Skipped. Supports `secret: true` for masked input, `validate`, `writeJson`, `deployAfter` |
|
|
125
125
|
| `writeJson` | `path`, `set` | Runs (safe in CI) |
|
|
126
|
+
| `stripe-setup` | `plans` | Creates Stripe products/prices, saves price IDs to secrets |
|
|
126
127
|
|
|
127
128
|
### Hook Lifecycle
|
|
128
129
|
|
|
129
130
|
```json
|
|
130
131
|
{
|
|
131
132
|
"hooks": {
|
|
133
|
+
"preCreate": [...], // During project creation (secret collection, auto-generation)
|
|
132
134
|
"preDeploy": [...], // Before wrangler deploy (validation)
|
|
133
135
|
"postDeploy": [...] // After successful deploy (notifications, testing)
|
|
134
136
|
}
|
|
@@ -145,9 +147,10 @@ Templates can define hooks in `.jack.json` that run at specific lifecycle points
|
|
|
145
147
|
| `clipboard` | Copy text to clipboard | `{"action": "clipboard", "text": "{{url}}", "message": "Copied!"}` |
|
|
146
148
|
| `shell` | Execute shell command | `{"action": "shell", "command": "curl {{url}}/health"}` |
|
|
147
149
|
| `pause` | Wait for Enter key | `{"action": "pause", "message": "Press Enter..."}` |
|
|
148
|
-
| `require` | Verify secret or
|
|
149
|
-
| `prompt` | Prompt for input
|
|
150
|
+
| `require` | Verify secret/env, optionally prompt or generate | `{"action": "require", "source": "secret", "key": "API_KEY", "onMissing": "prompt"}` |
|
|
151
|
+
| `prompt` | Prompt for input, optionally masked | `{"action": "prompt", "message": "Secret:", "secret": true, "writeJson": {...}}` |
|
|
150
152
|
| `writeJson` | Update JSON file with template vars | `{"action": "writeJson", "path": "public/data.json", "set": {"siteUrl": "{{url}}"}}` |
|
|
153
|
+
| `stripe-setup` | Create Stripe products/prices | `{"action": "stripe-setup", "plans": [{"name": "Pro", "priceKey": "STRIPE_PRO_PRICE_ID", "amount": 1900, "interval": "month"}]}` |
|
|
151
154
|
|
|
152
155
|
### Non-Interactive Mode
|
|
153
156
|
|
|
@@ -169,7 +172,7 @@ These variables are substituted at runtime (different from template placeholders
|
|
|
169
172
|
|----------|-------|--------------|
|
|
170
173
|
| `{{url}}` | Full deployed URL | postDeploy |
|
|
171
174
|
| `{{domain}}` | Domain without protocol | postDeploy |
|
|
172
|
-
| `{{name}}` | Project name | preDeploy, postDeploy |
|
|
175
|
+
| `{{name}}` | Project name | preCreate, preDeploy, postDeploy |
|
|
173
176
|
|
|
174
177
|
### Example: API Template Hooks
|
|
175
178
|
|
|
@@ -205,6 +208,170 @@ These variables are substituted at runtime (different from template placeholders
|
|
|
205
208
|
}
|
|
206
209
|
```
|
|
207
210
|
|
|
211
|
+
### Advanced Hook Features
|
|
212
|
+
|
|
213
|
+
These features support complex setup wizards (like the SaaS template with Stripe):
|
|
214
|
+
|
|
215
|
+
#### 1. `require` + `onMissing: "prompt" | "generate"`
|
|
216
|
+
|
|
217
|
+
The `require` action supports automatic secret collection when a secret is missing:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"action": "require",
|
|
222
|
+
"source": "secret",
|
|
223
|
+
"key": "STRIPE_SECRET_KEY",
|
|
224
|
+
"onMissing": "prompt",
|
|
225
|
+
"promptMessage": "Enter your Stripe Secret Key (sk_test_...):",
|
|
226
|
+
"setupUrl": "https://dashboard.stripe.com/apikeys"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Behavior:**
|
|
231
|
+
- If secret exists → continue (shows "Using saved KEY")
|
|
232
|
+
- If secret missing + interactive → prompt user, save to `.secrets.json`
|
|
233
|
+
- If secret missing + non-interactive → fail with setup instructions
|
|
234
|
+
|
|
235
|
+
**Auto-generate secrets with `onMissing: "generate"`:**
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"action": "require",
|
|
240
|
+
"source": "secret",
|
|
241
|
+
"key": "BETTER_AUTH_SECRET",
|
|
242
|
+
"message": "Generating authentication secret...",
|
|
243
|
+
"onMissing": "generate",
|
|
244
|
+
"generateCommand": "openssl rand -base64 32"
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
This runs the command, captures stdout, and saves it as the secret automatically.
|
|
249
|
+
|
|
250
|
+
#### 2. `stripe-setup` Action
|
|
251
|
+
|
|
252
|
+
Automatically creates Stripe products and prices, saving the price IDs as secrets:
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"action": "stripe-setup",
|
|
257
|
+
"message": "Setting up Stripe subscription plans...",
|
|
258
|
+
"plans": [
|
|
259
|
+
{
|
|
260
|
+
"name": "Pro",
|
|
261
|
+
"priceKey": "STRIPE_PRO_PRICE_ID",
|
|
262
|
+
"amount": 1900,
|
|
263
|
+
"interval": "month",
|
|
264
|
+
"description": "Pro monthly subscription"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"name": "Enterprise",
|
|
268
|
+
"priceKey": "STRIPE_ENTERPRISE_PRICE_ID",
|
|
269
|
+
"amount": 9900,
|
|
270
|
+
"interval": "month"
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Behavior:**
|
|
277
|
+
- Requires `STRIPE_SECRET_KEY` to be set first
|
|
278
|
+
- Checks for existing prices by lookup key (`jack_pro_month`)
|
|
279
|
+
- Creates product + price if not found
|
|
280
|
+
- Saves price IDs to secrets
|
|
281
|
+
|
|
282
|
+
#### 3. `prompt` with `secret` Flag
|
|
283
|
+
|
|
284
|
+
Mask sensitive input (like API keys):
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"action": "prompt",
|
|
289
|
+
"message": "Paste your webhook signing secret (whsec_...):",
|
|
290
|
+
"secret": true,
|
|
291
|
+
"writeJson": {
|
|
292
|
+
"path": ".secrets.json",
|
|
293
|
+
"set": { "STRIPE_WEBHOOK_SECRET": { "from": "input" } }
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
#### 4. `prompt` with `deployAfter`
|
|
299
|
+
|
|
300
|
+
Automatically redeploy after user provides input:
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"action": "prompt",
|
|
305
|
+
"message": "Paste webhook signing secret:",
|
|
306
|
+
"secret": true,
|
|
307
|
+
"deployAfter": true,
|
|
308
|
+
"deployMessage": "Deploying with webhook support...",
|
|
309
|
+
"writeJson": {
|
|
310
|
+
"path": ".secrets.json",
|
|
311
|
+
"set": { "STRIPE_WEBHOOK_SECRET": { "from": "input" } }
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Design Principles
|
|
317
|
+
|
|
318
|
+
When extending the hook system:
|
|
319
|
+
|
|
320
|
+
1. **Extend existing actions** - prefer `require+onMissing` over a new `requireOrPrompt` action
|
|
321
|
+
2. **Non-interactive fallback** - every interactive feature must degrade gracefully in CI/MCP
|
|
322
|
+
3. **Secrets via `.secrets.json`** - use `writeJson` with `.secrets.json` for secret storage
|
|
323
|
+
|
|
324
|
+
### Example: SaaS Template Setup Wizard
|
|
325
|
+
|
|
326
|
+
The `saas` template uses `preCreate` hooks for a complete setup wizard:
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"hooks": {
|
|
331
|
+
"preCreate": [
|
|
332
|
+
{
|
|
333
|
+
"action": "require",
|
|
334
|
+
"source": "secret",
|
|
335
|
+
"key": "STRIPE_SECRET_KEY",
|
|
336
|
+
"message": "Stripe API key required for payments",
|
|
337
|
+
"setupUrl": "https://dashboard.stripe.com/apikeys",
|
|
338
|
+
"onMissing": "prompt",
|
|
339
|
+
"promptMessage": "Enter your Stripe Secret Key (sk_test_... or sk_live_...):"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"action": "require",
|
|
343
|
+
"source": "secret",
|
|
344
|
+
"key": "BETTER_AUTH_SECRET",
|
|
345
|
+
"message": "Generating authentication secret...",
|
|
346
|
+
"onMissing": "generate",
|
|
347
|
+
"generateCommand": "openssl rand -base64 32"
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"action": "stripe-setup",
|
|
351
|
+
"message": "Setting up Stripe subscription plans...",
|
|
352
|
+
"plans": [
|
|
353
|
+
{"name": "Pro", "priceKey": "STRIPE_PRO_PRICE_ID", "amount": 1900, "interval": "month"},
|
|
354
|
+
{"name": "Enterprise", "priceKey": "STRIPE_ENTERPRISE_PRICE_ID", "amount": 9900, "interval": "month"}
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
"postDeploy": [
|
|
359
|
+
{"action": "box", "title": "Your SaaS is live!", "lines": ["{{url}}"]},
|
|
360
|
+
{"action": "clipboard", "text": "{{url}}/api/auth/stripe/webhook", "message": "Webhook URL copied"},
|
|
361
|
+
{"action": "prompt", "message": "Paste your webhook signing secret (whsec_...):", "secret": true, "deployAfter": true, "writeJson": {"path": ".secrets.json", "set": {"STRIPE_WEBHOOK_SECRET": {"from": "input"}}}}
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
This creates a guided wizard that:
|
|
368
|
+
1. Prompts for Stripe key (with setup URL)
|
|
369
|
+
2. Auto-generates auth secret
|
|
370
|
+
3. Creates Stripe products/prices automatically
|
|
371
|
+
4. Deploys the app
|
|
372
|
+
5. Guides through webhook setup
|
|
373
|
+
6. Re-deploys with webhook secret
|
|
374
|
+
|
|
208
375
|
## Farcaster Miniapp Embeds
|
|
209
376
|
|
|
210
377
|
When a cast includes a URL, Farcaster scrapes it for `fc:miniapp` meta tags to render a rich embed.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "saas",
|
|
3
|
+
"description": "SaaS starter (Auth + Payments + React)",
|
|
4
|
+
"secrets": ["STRIPE_SECRET_KEY", "BETTER_AUTH_SECRET", "STRIPE_PRO_PRICE_ID", "STRIPE_ENTERPRISE_PRICE_ID"],
|
|
5
|
+
"capabilities": ["db"],
|
|
6
|
+
"requires": ["DB"],
|
|
7
|
+
"intent": {
|
|
8
|
+
"keywords": ["saas", "subscription", "auth", "payment", "stripe", "membership"],
|
|
9
|
+
"examples": ["subscription app", "paid membership site", "saas product"]
|
|
10
|
+
},
|
|
11
|
+
"agentContext": {
|
|
12
|
+
"summary": "A SaaS starter with Better Auth authentication, Stripe payments, React + Vite frontend, and Hono API on Cloudflare Workers with D1 SQLite database.",
|
|
13
|
+
"full_text": "## Project Structure\n\n- `src/index.ts` - Hono API entry point with auth and payment routes\n- `src/auth.ts` - Better Auth configuration with Stripe plugin\n- `src/client/App.tsx` - React application entry point\n- `src/client/lib/auth-client.ts` - Better Auth client\n- `src/client/hooks/` - useAuth, useSubscription hooks\n- `src/client/pages/` - Page components (HomePage, DashboardPage, etc.)\n- `src/client/components/ui/` - shadcn/ui components\n- `schema.sql` - D1 database schema (user, session, account, subscription)\n- `wrangler.jsonc` - Cloudflare Workers configuration\n\n## Authentication\n\nUses Better Auth with email/password. Auth routes are handled at `/api/auth/*`.\n\n### Client-side\n```tsx\nimport { authClient } from './lib/auth-client';\n\n// Sign up\nawait authClient.signUp.email({ email, password, name });\n\n// Sign in\nawait authClient.signIn.email({ email, password });\n\n// Get session\nconst { data: session } = await authClient.getSession();\n```\n\n## Payments (Stripe)\n\nUses Better Auth Stripe plugin for subscription management.\n\n### Upgrade Flow\n```tsx\nimport { authClient } from './lib/auth-client';\n\nasync function handleUpgrade(plan: 'pro' | 'enterprise') {\n const { data, error } = await authClient.subscription.upgrade({\n plan,\n successUrl: '/dashboard?upgraded=true',\n cancelUrl: '/pricing',\n });\n\n if (data?.url) {\n window.location.href = data.url; // Redirect to Stripe Checkout\n }\n}\n```\n\n### Check Subscription\n```tsx\nconst { data: subscriptions } = await authClient.subscription.list();\nconst activeSubscription = subscriptions?.find(s =>\n s.status === 'active' || s.status === 'trialing'\n);\nconst plan = activeSubscription?.plan || 'free';\n```\n\n## Webhook Setup\n\nStripe webhooks are handled at `/api/auth/stripe/webhook`. Required events:\n- `checkout.session.completed`\n- `customer.subscription.created`\n- `customer.subscription.updated`\n- `customer.subscription.deleted`\n\n## Database Schema\n\nUses Better Auth's default schema (singular table names, camelCase columns):\n- `user` - User accounts\n- `session` - Active sessions\n- `account` - OAuth/password credentials\n- `verification` - Email verification tokens\n- `subscription` - Stripe subscriptions\n\n## Environment Variables\n\n- `BETTER_AUTH_SECRET` - Random secret for auth tokens (generate with: openssl rand -base64 32)\n- `STRIPE_SECRET_KEY` - Stripe API secret key\n- `STRIPE_WEBHOOK_SECRET` - Stripe webhook signing secret (whsec_...)\n- `STRIPE_PRO_PRICE_ID` - Stripe price ID for Pro plan\n- `STRIPE_ENTERPRISE_PRICE_ID` - Stripe price ID for Enterprise plan\n\n## Resources\n\n- [Better Auth Docs](https://www.betterauth.com/docs)\n- [Better Auth Stripe Plugin](https://www.betterauth.com/docs/plugins/stripe)\n- [Stripe Subscriptions](https://docs.stripe.com/billing/subscriptions)\n- [Hono Documentation](https://hono.dev)\n- [Cloudflare D1 Docs](https://developers.cloudflare.com/d1)"
|
|
14
|
+
},
|
|
15
|
+
"hooks": {
|
|
16
|
+
"preCreate": [
|
|
17
|
+
{
|
|
18
|
+
"action": "require",
|
|
19
|
+
"source": "secret",
|
|
20
|
+
"key": "STRIPE_SECRET_KEY",
|
|
21
|
+
"message": "Stripe API key required for payments",
|
|
22
|
+
"setupUrl": "https://dashboard.stripe.com/apikeys",
|
|
23
|
+
"onMissing": "prompt",
|
|
24
|
+
"promptMessage": "Enter your Stripe Secret Key (sk_test_... or sk_live_...):"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"action": "require",
|
|
28
|
+
"source": "secret",
|
|
29
|
+
"key": "BETTER_AUTH_SECRET",
|
|
30
|
+
"message": "Generating authentication secret...",
|
|
31
|
+
"onMissing": "generate",
|
|
32
|
+
"generateCommand": "openssl rand -base64 32"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"action": "stripe-setup",
|
|
36
|
+
"message": "Setting up Stripe subscription plans...",
|
|
37
|
+
"plans": [
|
|
38
|
+
{
|
|
39
|
+
"name": "Pro",
|
|
40
|
+
"priceKey": "STRIPE_PRO_PRICE_ID",
|
|
41
|
+
"amount": 1900,
|
|
42
|
+
"interval": "month",
|
|
43
|
+
"description": "Pro monthly subscription"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "Enterprise",
|
|
47
|
+
"priceKey": "STRIPE_ENTERPRISE_PRICE_ID",
|
|
48
|
+
"amount": 9900,
|
|
49
|
+
"interval": "month",
|
|
50
|
+
"description": "Enterprise monthly subscription"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"preDeploy": [
|
|
56
|
+
{
|
|
57
|
+
"action": "require",
|
|
58
|
+
"source": "secret",
|
|
59
|
+
"key": "STRIPE_SECRET_KEY"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"postDeploy": [
|
|
63
|
+
{
|
|
64
|
+
"action": "box",
|
|
65
|
+
"title": "Your SaaS is live!",
|
|
66
|
+
"lines": ["{{url}}"]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"action": "clipboard",
|
|
70
|
+
"text": "{{url}}/api/auth/stripe/webhook",
|
|
71
|
+
"message": "Webhook URL copied to clipboard"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"action": "message",
|
|
75
|
+
"text": ""
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"action": "message",
|
|
79
|
+
"text": "━━━ Stripe Webhook Setup ━━━"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"action": "message",
|
|
83
|
+
"text": ""
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"action": "message",
|
|
87
|
+
"text": "1. Create webhook endpoint in Stripe"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"action": "message",
|
|
91
|
+
"text": "2. Set endpoint URL to: {{url}}/api/auth/stripe/webhook"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"action": "message",
|
|
95
|
+
"text": "3. Select these events:"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"action": "message",
|
|
99
|
+
"text": " • checkout.session.completed"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"action": "message",
|
|
103
|
+
"text": " • customer.subscription.created"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"action": "message",
|
|
107
|
+
"text": " • customer.subscription.updated"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"action": "message",
|
|
111
|
+
"text": " • customer.subscription.deleted"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"action": "message",
|
|
115
|
+
"text": ""
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"action": "url",
|
|
119
|
+
"url": "https://dashboard.stripe.com/webhooks",
|
|
120
|
+
"label": "Open Stripe webhooks",
|
|
121
|
+
"prompt": true
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"action": "prompt",
|
|
125
|
+
"message": "Paste your webhook signing secret (whsec_...):",
|
|
126
|
+
"required": false,
|
|
127
|
+
"secret": true,
|
|
128
|
+
"successMessage": "Webhook secret saved!",
|
|
129
|
+
"deployAfter": true,
|
|
130
|
+
"deployMessage": "Deploying with webhook support...",
|
|
131
|
+
"writeJson": {
|
|
132
|
+
"path": ".secrets.json",
|
|
133
|
+
"set": { "STRIPE_WEBHOOK_SECRET": { "from": "input" } }
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"action": "message",
|
|
138
|
+
"text": ""
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"action": "message",
|
|
142
|
+
"text": "━━━ Next Steps ━━━"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"action": "message",
|
|
146
|
+
"text": "• Test card: 4242 4242 4242 4242 (any future date, any CVC)"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"action": "message",
|
|
150
|
+
"text": "• Customize theme: https://ui.shadcn.com/create"
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
}
|