'use client' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { getStripeSettings, saveStripeSettings } from '@/lib/actions' const TIERS = ['plus', 'basic', 'associate'] as const const input = 'w-full bg-background border border-border text-foreground font-sans text-sm rounded px-3 py-2 focus:outline-none focus:border-primary' type Settings = { mode: 'test' | 'live' publishable_key: string grace_days: number prices: Record hasSecretKey: boolean hasWebhookSecret: boolean configured: boolean } export function StripeBillingPanel({ spaceId }: { spaceId: string }) { const [s, setS] = useState(null) const [secretKey, setSecretKey] = useState('') const [webhookSecret, setWebhookSecret] = useState('') const [busy, setBusy] = useState(false) const [webhookUrl, setWebhookUrl] = useState('') useEffect(() => { setWebhookUrl(`${window.location.origin}/api/stripe/webhook/${spaceId}`) getStripeSettings().then(r => { if ('data' in r) setS(r.data as Settings) }) }, [spaceId]) if (!s) return null async function save() { if (!s) return setBusy(true) const res = await saveStripeSettings({ mode: s.mode, publishable_key: s.publishable_key || null, secret_key: secretKey || null, webhook_secret: webhookSecret || null, grace_days: s.grace_days, prices: s.prices, }) setBusy(false) if ('error' in res && res.error) return toast.error(res.error) setSecretKey('') setWebhookSecret('') const r = await getStripeSettings() if ('data' in r) setS(r.data as Settings) toast.success('Stripe settings saved') } return (

Stripe recurring dues

{s.configured ? 'configured' : 'not configured'}

Uses your space’s own Stripe account. Keys are stored encrypted and never shown again. After saving, add this webhook endpoint in your Stripe Dashboard and paste back its signing secret, and activate the Customer Portal there.

setS({ ...s, grace_days: Number(e.target.value) })} placeholder="Grace days before 'late'" /> setS({ ...s, publishable_key: e.target.value })} placeholder="Publishable key (pk_…)" /> setSecretKey(e.target.value)} placeholder={s.hasSecretKey ? 'Secret key — set (blank = keep)' : 'Secret key (sk_…)'} /> setWebhookSecret(e.target.value)} placeholder={s.hasWebhookSecret ? 'Webhook secret — set (blank = keep)' : 'Webhook signing secret (whsec_…)'} />

Tier → Stripe Price

{TIERS.map(t => ( setS({ ...s, prices: { ...s.prices, [t]: e.target.value } })} placeholder={`${t} price_…`} /> ))}

Webhook endpoint (add in Stripe → Developers → Webhooks):

{webhookUrl}

Subscribe to: checkout.session.completed, customer.subscription.*, invoice.paid, invoice.payment_failed.

) }