vibefast-cli 1.3.3 → 1.3.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 (53) hide show
  1. package/README.md +4 -0
  2. package/dist/commands/add.d.ts.map +1 -1
  3. package/dist/commands/add.js +317 -6
  4. package/dist/commands/add.js.map +1 -1
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +38 -10
  7. package/dist/commands/init.js.map +1 -1
  8. package/docs/commands.md +2 -0
  9. package/docs/quickstart.md +1 -1
  10. package/docs/recipes.md +2 -2
  11. package/package.json +1 -1
  12. package/recipes/audio-recorder-native@latest.zip +0 -0
  13. package/recipes/audio-recorder-supabase-native@latest.zip +0 -0
  14. package/recipes/chatbot-native@latest.zip +0 -0
  15. package/recipes/chatbot-supabase-native@latest.zip +0 -0
  16. package/recipes/glowing-button-native@latest.zip +0 -0
  17. package/recipes/image-analysis-native@latest.zip +0 -0
  18. package/recipes/image-analysis-supabase-native@latest.zip +0 -0
  19. package/recipes/image-generator-native@latest.zip +0 -0
  20. package/recipes/ios-widget-native@latest.zip +0 -0
  21. package/recipes/lemonsqueezy/apps/web/app/billing/page.tsx +48 -0
  22. package/recipes/lemonsqueezy/apps/web/app/pricing/page.tsx +22 -0
  23. package/recipes/lemonsqueezy/apps/web/components/payments/BillingSection.tsx +161 -0
  24. package/recipes/lemonsqueezy/apps/web/components/payments/PricingCard.tsx +87 -0
  25. package/recipes/lemonsqueezy/apps/web/components/payments/PricingSection.tsx +85 -0
  26. package/recipes/lemonsqueezy/apps/web/components/payments/index.ts +10 -0
  27. package/recipes/lemonsqueezy/apps/web/lib/plans.ts +84 -0
  28. package/recipes/lemonsqueezy/packages/backend/convex/payments/config.ts +127 -0
  29. package/recipes/lemonsqueezy/packages/backend/convex/payments/index.ts +52 -0
  30. package/recipes/lemonsqueezy/packages/backend/convex/payments/providers/lemonsqueezy.ts +317 -0
  31. package/recipes/lemonsqueezy/packages/backend/convex/payments/types.ts +89 -0
  32. package/recipes/lemonsqueezy/packages/backend/convex/payments/webhooks.ts +187 -0
  33. package/recipes/lemonsqueezy/packages/backend/convex/payments.ts +137 -0
  34. package/recipes/lemonsqueezy/recipe.json +120 -0
  35. package/recipes/lemonsqueezy-web@latest.zip +0 -0
  36. package/recipes/payments-native@latest.zip +0 -0
  37. package/recipes/payments-supabase-native@latest.zip +0 -0
  38. package/recipes/stripe/apps/web/app/billing/page.tsx +48 -0
  39. package/recipes/stripe/apps/web/app/pricing/page.tsx +22 -0
  40. package/recipes/stripe/apps/web/components/payments/BillingSection.tsx +161 -0
  41. package/recipes/stripe/apps/web/components/payments/PricingCard.tsx +87 -0
  42. package/recipes/stripe/apps/web/components/payments/PricingSection.tsx +85 -0
  43. package/recipes/stripe/apps/web/components/payments/index.ts +10 -0
  44. package/recipes/stripe/apps/web/lib/plans.ts +84 -0
  45. package/recipes/stripe/packages/backend/convex/payments/config.ts +127 -0
  46. package/recipes/stripe/packages/backend/convex/payments/index.ts +52 -0
  47. package/recipes/stripe/packages/backend/convex/payments/providers/stripe.ts +186 -0
  48. package/recipes/stripe/packages/backend/convex/payments/types.ts +89 -0
  49. package/recipes/stripe/packages/backend/convex/payments.ts +137 -0
  50. package/recipes/stripe/recipe.json +114 -0
  51. package/recipes/stripe-web@latest.zip +0 -0
  52. package/recipes/voice-bot-native@latest.zip +0 -0
  53. package/recipes/wake-word-native@latest.zip +0 -0
@@ -0,0 +1,161 @@
1
+ 'use client';
2
+
3
+ import { useAction, useQuery } from 'convex/react';
4
+ import { api } from '@vibefast/backend/_generated/api';
5
+ import { Button } from '@/components/ui/button';
6
+ import { CreditCard, ExternalLink, Settings, AlertCircle } from 'lucide-react';
7
+ import { useState } from 'react';
8
+
9
+ // ============================================================================
10
+ // Billing Section Component
11
+ // Displays user's subscription status and billing management options
12
+ // ============================================================================
13
+
14
+ export function BillingSection() {
15
+ const [loading, setLoading] = useState(false);
16
+ const [error, setError] = useState<string | null>(null);
17
+
18
+ const subscription = useQuery(api.payments.getSubscription);
19
+ const createPortalSession = useAction(api.payments.createPortalSession);
20
+
21
+ const cancelAt =
22
+ typeof subscription?.cancelAt === 'number' ? subscription.cancelAt : null;
23
+ const hasActiveSubscription =
24
+ subscription?.status === 'active' || subscription?.status === 'trialing';
25
+ const isCanceling = Boolean(subscription?.cancelAtPeriodEnd || cancelAt);
26
+
27
+ const handleManageBilling = async () => {
28
+ setLoading(true);
29
+ setError(null);
30
+
31
+ try {
32
+ const result = await createPortalSession({
33
+ returnUrl: window.location.href,
34
+ });
35
+
36
+ if (result.url) {
37
+ window.location.href = result.url;
38
+ }
39
+ } catch (err) {
40
+ console.error('Portal error:', err);
41
+ setError(
42
+ err instanceof Error ? err.message : 'Failed to open billing portal',
43
+ );
44
+ } finally {
45
+ setLoading(false);
46
+ }
47
+ };
48
+
49
+ const formatDate = (timestamp?: number) => {
50
+ if (!timestamp) return 'N/A';
51
+ return new Date(timestamp * 1000).toLocaleDateString(undefined, {
52
+ year: 'numeric',
53
+ month: 'long',
54
+ day: 'numeric',
55
+ });
56
+ };
57
+
58
+ return (
59
+ <div className="space-y-6">
60
+ {/* Current Subscription */}
61
+ <div className="rounded-lg border bg-card p-6">
62
+ <div className="flex items-center justify-between">
63
+ <div className="flex items-center gap-3">
64
+ <CreditCard className="h-5 w-5 text-muted-foreground" />
65
+ <h3 className="font-semibold">Subscription</h3>
66
+ </div>
67
+ <span
68
+ className={`rounded-full px-2.5 py-0.5 text-xs font-medium ${
69
+ hasActiveSubscription
70
+ ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'
71
+ : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300'
72
+ }`}
73
+ >
74
+ {hasActiveSubscription
75
+ ? subscription?.status === 'trialing'
76
+ ? 'Trial'
77
+ : isCanceling
78
+ ? 'Canceling'
79
+ : 'Active'
80
+ : 'No Plan'}
81
+ </span>
82
+ </div>
83
+
84
+ {subscription && hasActiveSubscription && (
85
+ <div className="mt-4 rounded-md bg-muted p-4">
86
+ <div className="grid gap-2 text-sm">
87
+ <div className="flex justify-between">
88
+ <span className="text-muted-foreground">Plan</span>
89
+ <span className="font-medium">
90
+ {subscription.planId || 'Custom Plan'}
91
+ </span>
92
+ </div>
93
+ <div className="flex justify-between">
94
+ <span className="text-muted-foreground">
95
+ {isCanceling ? 'Ends' : 'Renews'}
96
+ </span>
97
+ <span className="font-medium">
98
+ {formatDate(
99
+ isCanceling && cancelAt
100
+ ? cancelAt
101
+ : subscription.currentPeriodEnd,
102
+ )}
103
+ </span>
104
+ </div>
105
+ {isCanceling && (
106
+ <div className="mt-2 flex items-center gap-2 text-amber-600 dark:text-amber-400">
107
+ <AlertCircle className="h-4 w-4" />
108
+ <span className="text-xs">Scheduled to cancel</span>
109
+ </div>
110
+ )}
111
+ </div>
112
+ </div>
113
+ )}
114
+
115
+ {!hasActiveSubscription && (
116
+ <p className="mt-4 text-sm text-muted-foreground">
117
+ You don&apos;t have an active subscription. Choose a plan to get
118
+ started.
119
+ </p>
120
+ )}
121
+ </div>
122
+
123
+ {/* Actions */}
124
+ {error && (
125
+ <div className="rounded-lg border border-destructive bg-destructive/10 p-4 text-sm text-destructive">
126
+ {error}
127
+ </div>
128
+ )}
129
+
130
+ <div className="flex flex-col gap-3 sm:flex-row">
131
+ {hasActiveSubscription && (
132
+ <Button
133
+ onClick={handleManageBilling}
134
+ disabled={loading}
135
+ variant="outline"
136
+ className="flex-1"
137
+ >
138
+ {loading ? (
139
+ <span className="flex items-center gap-2">
140
+ <span className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
141
+ Opening...
142
+ </span>
143
+ ) : (
144
+ <>
145
+ <Settings className="mr-2 h-4 w-4" />
146
+ Manage Billing
147
+ </>
148
+ )}
149
+ </Button>
150
+ )}
151
+
152
+ <Button variant={hasActiveSubscription ? 'outline' : 'default'} asChild>
153
+ <a href="/pricing">
154
+ <ExternalLink className="mr-2 h-4 w-4" />
155
+ {hasActiveSubscription ? 'Change Plan' : 'View Plans'}
156
+ </a>
157
+ </Button>
158
+ </div>
159
+ </div>
160
+ );
161
+ }
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import { Check } from 'lucide-react';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ // ============================================================================
8
+ // Pricing Card Component
9
+ // Reusable card for displaying a single pricing plan
10
+ // ============================================================================
11
+
12
+ interface PricingCardProps {
13
+ name: string;
14
+ price: string;
15
+ description?: string;
16
+ features: string[];
17
+ popular?: boolean;
18
+ onSelect: () => void;
19
+ loading?: boolean;
20
+ buttonText?: string;
21
+ disabled?: boolean;
22
+ }
23
+
24
+ export function PricingCard({
25
+ name,
26
+ price,
27
+ description,
28
+ features,
29
+ popular,
30
+ onSelect,
31
+ loading,
32
+ buttonText = 'Get Started',
33
+ disabled,
34
+ }: PricingCardProps) {
35
+ return (
36
+ <div
37
+ className={cn(
38
+ 'relative flex flex-col rounded-2xl border bg-card p-6 shadow-sm transition-all',
39
+ popular && 'border-primary shadow-lg ring-1 ring-primary',
40
+ 'hover:shadow-md',
41
+ )}
42
+ >
43
+ {popular && (
44
+ <div className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-primary px-3 py-1 text-xs font-semibold text-primary-foreground">
45
+ Most Popular
46
+ </div>
47
+ )}
48
+
49
+ <div className="mb-4">
50
+ <h3 className="text-lg font-semibold">{name}</h3>
51
+ {description && (
52
+ <p className="mt-1 text-sm text-muted-foreground">{description}</p>
53
+ )}
54
+ </div>
55
+
56
+ <div className="mb-6">
57
+ <span className="text-3xl font-bold">{price}</span>
58
+ </div>
59
+
60
+ <ul className="mb-6 flex-1 space-y-3">
61
+ {features.map((feature, index) => (
62
+ <li key={index} className="flex items-start gap-2 text-sm">
63
+ <Check className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
64
+ <span>{feature}</span>
65
+ </li>
66
+ ))}
67
+ </ul>
68
+
69
+ <Button
70
+ onClick={onSelect}
71
+ disabled={loading || disabled}
72
+ className="w-full"
73
+ variant={popular ? 'default' : 'outline'}
74
+ size="lg"
75
+ >
76
+ {loading ? (
77
+ <span className="flex items-center gap-2">
78
+ <span className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
79
+ Processing...
80
+ </span>
81
+ ) : (
82
+ buttonText
83
+ )}
84
+ </Button>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,85 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { useAction } from 'convex/react';
5
+ import { api } from '@vibefast/backend/_generated/api';
6
+ import { PricingCard } from './PricingCard';
7
+ import { PLANS, type Plan } from '@/lib/plans';
8
+
9
+ // ============================================================================
10
+ // Pricing Section Component
11
+ // Displays all available pricing plans with checkout functionality
12
+ // Plans are imported from @/lib/plans.ts for single source of truth
13
+ // ============================================================================
14
+
15
+ interface PricingSectionProps {
16
+ plans?: Plan[];
17
+ title?: string;
18
+ subtitle?: string;
19
+ }
20
+
21
+ export function PricingSection({
22
+ plans = PLANS,
23
+ title = 'Simple, Transparent Pricing',
24
+ subtitle = 'Choose the plan that fits your needs. Cancel anytime.',
25
+ }: PricingSectionProps) {
26
+ const [loadingPlan, setLoadingPlan] = useState<string | null>(null);
27
+ const [error, setError] = useState<string | null>(null);
28
+
29
+ const createCheckout = useAction(api.payments.createCheckout);
30
+
31
+ const handleSelectPlan = async (plan: (typeof PLANS)[0]) => {
32
+ setLoadingPlan(plan.id);
33
+ setError(null);
34
+
35
+ try {
36
+ const result = await createCheckout({
37
+ priceId: plan.priceId,
38
+ successUrl: `${window.location.origin}/billing?success=true`,
39
+ cancelUrl: `${window.location.origin}/pricing?canceled=true`,
40
+ });
41
+
42
+ if (result.url) {
43
+ window.location.href = result.url;
44
+ } else {
45
+ throw new Error('No checkout URL returned');
46
+ }
47
+ } catch (err) {
48
+ console.error('Checkout error:', err);
49
+ setError(err instanceof Error ? err.message : 'Failed to start checkout');
50
+ } finally {
51
+ setLoadingPlan(null);
52
+ }
53
+ };
54
+
55
+ return (
56
+ <section className="py-16">
57
+ <div className="text-center">
58
+ <h2 className="text-3xl font-bold tracking-tight">{title}</h2>
59
+ <p className="mt-2 text-muted-foreground">{subtitle}</p>
60
+ </div>
61
+
62
+ {error && (
63
+ <div className="mx-auto mt-6 max-w-md rounded-lg border border-destructive bg-destructive/10 p-4 text-center text-sm text-destructive">
64
+ {error}
65
+ </div>
66
+ )}
67
+
68
+ <div className="mt-10 grid gap-6 md:grid-cols-3">
69
+ {plans.map((plan) => (
70
+ <PricingCard
71
+ key={plan.id}
72
+ name={plan.name}
73
+ description={plan.description}
74
+ price={plan.price}
75
+ features={plan.features}
76
+ popular={plan.popular}
77
+ loading={loadingPlan === plan.id}
78
+ disabled={loadingPlan !== null && loadingPlan !== plan.id}
79
+ onSelect={() => handleSelectPlan(plan)}
80
+ />
81
+ ))}
82
+ </div>
83
+ </section>
84
+ );
85
+ }
@@ -0,0 +1,10 @@
1
+ // ============================================================================
2
+ // Payment Components Barrel Export
3
+ // ============================================================================
4
+
5
+ export { PricingCard } from './PricingCard';
6
+ export { PricingSection } from './PricingSection';
7
+ export { BillingSection } from './BillingSection';
8
+
9
+ // Re-export plan config for convenience
10
+ export { PLANS, type Plan, getPlanById, getPlanByPriceId } from '@/lib/plans';
@@ -0,0 +1,84 @@
1
+ // ============================================================================
2
+ // Shared Plans Configuration (Frontend)
3
+ // Single source of truth for pricing plans in the web app
4
+ // ============================================================================
5
+
6
+ /**
7
+ * Plan definition for pricing display
8
+ */
9
+ export interface Plan {
10
+ id: string;
11
+ name: string;
12
+ description?: string;
13
+ price: string;
14
+ priceId: string;
15
+ features: string[];
16
+ popular?: boolean;
17
+ }
18
+
19
+ /**
20
+ * Pricing plans configuration
21
+ * Update these with your actual pricing plans from Stripe/LemonSqueezy
22
+ *
23
+ * Note: Uses NEXT_PUBLIC_ prefixed env vars for client-side access
24
+ * The backend config.ts uses non-prefixed versions for server-side
25
+ */
26
+ export const PLANS: Plan[] = [
27
+ {
28
+ id: 'starter',
29
+ name: 'Starter',
30
+ description: 'Perfect for individuals getting started',
31
+ price: '$9/mo',
32
+ priceId: process.env.NEXT_PUBLIC_PLAN_STARTER_PRICE_ID ?? 'price_starter',
33
+ features: [
34
+ '100 AI credits per month',
35
+ 'Basic support',
36
+ 'Single project',
37
+ 'Community access',
38
+ ],
39
+ },
40
+ {
41
+ id: 'pro',
42
+ name: 'Pro',
43
+ description: 'Best for professionals and growing teams',
44
+ price: '$29/mo',
45
+ priceId: process.env.NEXT_PUBLIC_PLAN_PRO_PRICE_ID ?? 'price_pro',
46
+ features: [
47
+ 'Unlimited AI credits',
48
+ 'Priority support',
49
+ 'Unlimited projects',
50
+ 'API access',
51
+ 'Advanced analytics',
52
+ ],
53
+ popular: true,
54
+ },
55
+ {
56
+ id: 'enterprise',
57
+ name: 'Enterprise',
58
+ description: 'For large teams with custom needs',
59
+ price: '$99/mo',
60
+ priceId:
61
+ process.env.NEXT_PUBLIC_PLAN_ENTERPRISE_PRICE_ID ?? 'price_enterprise',
62
+ features: [
63
+ 'Everything in Pro',
64
+ 'Dedicated support',
65
+ 'Custom integrations',
66
+ 'SLA guarantee',
67
+ 'SSO & advanced security',
68
+ ],
69
+ },
70
+ ];
71
+
72
+ /**
73
+ * Get a plan by its ID
74
+ */
75
+ export const getPlanById = (planId: string): Plan | undefined => {
76
+ return PLANS.find((plan) => plan.id === planId);
77
+ };
78
+
79
+ /**
80
+ * Get a plan by its provider-specific price ID
81
+ */
82
+ export const getPlanByPriceId = (priceId: string): Plan | undefined => {
83
+ return PLANS.find((plan) => plan.priceId === priceId);
84
+ };
@@ -0,0 +1,127 @@
1
+ import type { Plan } from './types';
2
+
3
+ // ============================================================================
4
+ // Payment Configuration
5
+ // Define your pricing plans and provider settings here
6
+ // ============================================================================
7
+
8
+ /**
9
+ * Supported payment providers
10
+ */
11
+ export type PaymentProvider = 'stripe' | 'lemonsqueezy';
12
+
13
+ /**
14
+ * Get the active payment provider from environment
15
+ */
16
+ export const getPaymentProvider = (): PaymentProvider => {
17
+ const provider = process.env.PAYMENT_PROVIDER as PaymentProvider;
18
+ if (!provider) {
19
+ throw new Error(
20
+ 'PAYMENT_PROVIDER environment variable must be set to "stripe" or "lemonsqueezy"',
21
+ );
22
+ }
23
+ if (!['stripe', 'lemonsqueezy'].includes(provider)) {
24
+ throw new Error(
25
+ `Invalid PAYMENT_PROVIDER: "${provider}". Must be "stripe" or "lemonsqueezy"`,
26
+ );
27
+ }
28
+ return provider;
29
+ };
30
+
31
+ /**
32
+ * Get the site URL for redirects
33
+ */
34
+ export const getSiteUrl = (): string => {
35
+ const url = process.env.SITE_URL;
36
+ if (!url) {
37
+ throw new Error('SITE_URL environment variable must be set');
38
+ }
39
+ return url.replace(/\/$/, ''); // Remove trailing slash
40
+ };
41
+
42
+ /**
43
+ * Default success/cancel URLs for checkout
44
+ */
45
+ export const getCheckoutUrls = (
46
+ customSuccess?: string,
47
+ customCancel?: string,
48
+ ) => {
49
+ const siteUrl = getSiteUrl();
50
+ return {
51
+ successUrl: customSuccess ?? `${siteUrl}/billing?success=true`,
52
+ cancelUrl: customCancel ?? `${siteUrl}/pricing?canceled=true`,
53
+ };
54
+ };
55
+
56
+ // ============================================================================
57
+ // Pricing Plans Configuration
58
+ // Update these with your actual plan details and provider-specific price IDs
59
+ // ============================================================================
60
+
61
+ /**
62
+ * Define your pricing plans here.
63
+ * The `priceId` should be:
64
+ * - For Stripe: The Stripe Price ID (e.g., "price_1234...")
65
+ * - For LemonSqueezy: The Variant ID (e.g., "123456")
66
+ */
67
+ export const PLANS: Plan[] = [
68
+ {
69
+ id: 'starter',
70
+ name: 'Starter',
71
+ description: 'Perfect for individuals getting started',
72
+ price: '$9/mo',
73
+ priceId: process.env.PLAN_STARTER_PRICE_ID ?? 'price_starter',
74
+ interval: 'month',
75
+ features: [
76
+ '100 AI credits per month',
77
+ 'Basic support',
78
+ 'Single project',
79
+ 'Community access',
80
+ ],
81
+ },
82
+ {
83
+ id: 'pro',
84
+ name: 'Pro',
85
+ description: 'Best for professionals and growing teams',
86
+ price: '$29/mo',
87
+ priceId: process.env.PLAN_PRO_PRICE_ID ?? 'price_pro',
88
+ interval: 'month',
89
+ features: [
90
+ 'Unlimited AI credits',
91
+ 'Priority support',
92
+ 'Unlimited projects',
93
+ 'API access',
94
+ 'Advanced analytics',
95
+ ],
96
+ popular: true,
97
+ },
98
+ {
99
+ id: 'enterprise',
100
+ name: 'Enterprise',
101
+ description: 'For large teams with custom needs',
102
+ price: '$99/mo',
103
+ priceId: process.env.PLAN_ENTERPRISE_PRICE_ID ?? 'price_enterprise',
104
+ interval: 'month',
105
+ features: [
106
+ 'Everything in Pro',
107
+ 'Dedicated support',
108
+ 'Custom integrations',
109
+ 'SLA guarantee',
110
+ 'SSO & advanced security',
111
+ ],
112
+ },
113
+ ];
114
+
115
+ /**
116
+ * Get a plan by its ID
117
+ */
118
+ export const getPlanById = (planId: string): Plan | undefined => {
119
+ return PLANS.find((plan) => plan.id === planId);
120
+ };
121
+
122
+ /**
123
+ * Get a plan by its provider-specific price ID
124
+ */
125
+ export const getPlanByPriceId = (priceId: string): Plan | undefined => {
126
+ return PLANS.find((plan) => plan.priceId === priceId);
127
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Payment Functions Entry Point
3
+ *
4
+ * This file exports the active payment provider's functions.
5
+ * To switch providers, change the import below from './providers/stripe'
6
+ * to './providers/lemonsqueezy'. CLI tools modify this import line.
7
+ *
8
+ * All providers export the same interface:
9
+ * - createCheckout: Create a checkout session
10
+ * - createPortalSession: Create billing portal session
11
+ * - getSubscription: Get user's active subscription
12
+ * - cancelSubscription: Cancel a subscription
13
+ * - getPayments: Get payment history
14
+ */
15
+
16
+ // ============================================================================
17
+ // Provider Selection
18
+ // CLI tools modify this line to switch providers.
19
+ // Options: './providers/stripe' or './providers/lemonsqueezy'
20
+ // ============================================================================
21
+ export type PaymentProvider = 'stripe' | 'lemonsqueezy';
22
+
23
+ // ACTIVE_PROVIDER: Change to 'lemonsqueezy' when switching providers
24
+ export const ACTIVE_PROVIDER: PaymentProvider = 'stripe';
25
+
26
+ // ============================================================================
27
+ // Provider Exports
28
+ // Change this import to switch providers:
29
+ // - './providers/stripe' for Stripe
30
+ // - './providers/lemonsqueezy' for LemonSqueezy
31
+ // ============================================================================
32
+ export {
33
+ createCheckout,
34
+ createPortalSession,
35
+ getSubscription,
36
+ cancelSubscription,
37
+ getPayments,
38
+ } from './providers/stripe';
39
+
40
+ // Re-export shared config and types for frontend use
41
+ export {
42
+ PLANS,
43
+ getPlanById,
44
+ getPlanByPriceId,
45
+ getPaymentProvider,
46
+ } from './config';
47
+ export type {
48
+ Plan,
49
+ Subscription,
50
+ SubscriptionStatus,
51
+ BillingInterval,
52
+ } from './types';