StripePaymentConnection.tsx89 lines · main
1import { ExternalLink } from 'lucide-react'
2import { Button } from 'ui'
3import { Admonition } from 'ui-patterns/admonition'
4
5import PartnerIcon from '@/components/ui/PartnerIcon'
6import { MANAGED_BY } from '@/lib/constants/infrastructure'
7
8export type StripeTokenStatus = 'connected' | 'attention' | 'unknown'
9
10export const STRIPE_DASHBOARD_URL = 'https://dashboard.stripe.com'
11export const STRIPE_PROJECTS_DOCS_URL = 'https://docs.stripe.com/projects'
12
13interface StripePaymentConnectionProps {
14 status?: StripeTokenStatus
15 tokenLast4?: string | null
16 tokenExpiresAt?: number | null
17}
18
19const formatStripeTokenExpiry = (expiresAt?: number | null) => {
20 if (!expiresAt) return undefined
21 return new Date(expiresAt * 1000).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })
22}
23
24export function StripePaymentConnection({
25 status = 'connected',
26 tokenLast4,
27 tokenExpiresAt,
28}: StripePaymentConnectionProps) {
29 const tokenExpiry = formatStripeTokenExpiry(tokenExpiresAt)
30 const hasTokenSummary =
31 tokenLast4 !== null && tokenLast4 !== undefined && tokenExpiry !== undefined
32
33 if (status === 'attention') {
34 return (
35 <Admonition
36 type="warning"
37 title="Stripe payment connection needs attention"
38 description="The payment token linked to this organisation may be invalid or expired. To keep billing active, review and update it in your Stripe Dashboard."
39 actions={
40 <Button asChild type="warning" iconRight={<ExternalLink size={14} />}>
41 <a href={STRIPE_DASHBOARD_URL} target="_blank" rel="noopener noreferrer">
42 Review in Stripe Dashboard
43 </a>
44 </Button>
45 }
46 />
47 )
48 }
49
50 if (status === 'unknown') {
51 return (
52 <Admonition
53 type="default"
54 title="Stripe payment setup in progress"
55 description="Your Stripe payment connection is being configured. Check back shortly."
56 />
57 )
58 }
59
60 return (
61 <div className="flex flex-col items-center gap-y-3 py-4 text-center">
62 <PartnerIcon
63 organization={{ managed_by: MANAGED_BY.STRIPE_PROJECTS }}
64 showTooltip={false}
65 size="large"
66 />
67 <div className="space-y-1">
68 <p className="text-sm text-foreground">Payment managed through Stripe</p>
69 <p className="text-sm text-foreground-light max-w-sm text-balance">
70 Billing for this organisation is handled via a connected Stripe payment token.
71 </p>
72 {hasTokenSummary && (
73 <p className="text-xs text-foreground-light">
74 Token ending in {tokenLast4} expires {tokenExpiry}.
75 </p>
76 )}
77 </div>
78 <Button asChild type="default" iconRight={<ExternalLink size={14} />}>
79 <a
80 href={`${STRIPE_PROJECTS_DOCS_URL}#manage-billing`}
81 target="_blank"
82 rel="noopener noreferrer"
83 >
84 Manage via Stripe CLI
85 </a>
86 </Button>
87 </div>
88 )
89}