page.tsx78 lines · main
1import { headers } from 'next/headers';
2import { redirect } from 'next/navigation';
3
4import { apiFetch } from '../../../../../lib/api';
5import { requireUser } from '../../../../../lib/session';
6
7export const metadata = { title: 'upgrade' };
8export const dynamic = 'force-dynamic';
9
10interface CheckoutBody {
11 tier: 'pro' | 'team';
12 successURL: string;
13}
14
15/**
16 * Server-action bounce: landing-page "get pro" links point here with
17 * ?tier=pro|team. We require auth (signin handles the next= redirect back),
18 * POST /v1/billing/checkout, and forward the user to the Polar-hosted URL.
19 */
20export default async function UpgradePage({
21 searchParams,
22}: {
23 searchParams: Promise<{ tier?: string }>;
24}) {
25 await requireUser();
26 const { tier } = await searchParams;
27
28 if (tier !== 'pro' && tier !== 'team') {
29 return (
30 <main className="mx-auto max-w-lg px-6 py-16 font-mono text-sm">
31 <h1 className="text-xl">unknown plan</h1>
32 <p className="mt-2 text-[var(--color-text-muted)]">
33 pick a plan from the{' '}
34 <a href="/#pricing" className="text-[var(--color-text-link)]">
35 pricing section
36 </a>
37 .
38 </p>
39 </main>
40 );
41 }
42
43 const h = await headers();
44 const proto = h.get('x-forwarded-proto') ?? 'https';
45 const host = h.get('x-forwarded-host') ?? h.get('host') ?? 'briven.tech';
46 const origin = `${proto}://${host}`;
47
48 const body: CheckoutBody = {
49 tier,
50 successURL: `${origin}/dashboard/billing?checkout=success`,
51 };
52
53 const res = await apiFetch('/v1/billing/checkout', {
54 method: 'POST',
55 headers: { 'content-type': 'application/json' },
56 body: JSON.stringify(body),
57 });
58
59 if (!res.ok) {
60 const errBody = (await res.json().catch(() => ({}))) as { message?: string; code?: string };
61 return (
62 <main className="mx-auto max-w-lg px-6 py-16 font-mono text-sm">
63 <h1 className="text-xl">couldn&apos;t start checkout</h1>
64 <p className="mt-2 text-[var(--color-text-muted)]">
65 {errBody.message ?? errBody.code ?? `http ${res.status}`}
66 </p>
67 <p className="mt-6">
68 <a href="/dashboard/settings" className="text-[var(--color-text-link)]">
69 back to settings
70 </a>
71 </p>
72 </main>
73 );
74 }
75
76 const { url } = (await res.json()) as { url: string };
77 redirect(url);
78}