Pricing Table
Lumen provides UI components so you can speed up your SaaS app development.
We recommend using our shadcn component so you can easily customize the styling manually or by prompting cursor:
npx shadcn@latest add https://getlumen.dev/pricing-table.json
Frontend Integration
Import the PricingTable component and add it to your NextJS pricing page:
import { PricingTable } from "@/components/ui/pricing-table";
// OR if you don't want to use shadcn
// import { PricingTable } from '@getlumen/react'
export default function PricingPage() {
return (
<PricingTable
lumenPublishableKey={process.env.NEXT_PUBLIC_LUMEN_PUBLISHABLE_KEY}
userId={session?.user?.id}
loginRedirectUrl="/login"
paymentProvider="stripe" // 'stripe' (default) or 'dodo'
/>
);
}
Required Props
lumenPublishableKey
: Your Lumen publishable key from the environment variablesuserId
: The ID of the currently logged-in userloginRedirectUrl
: Clicking subscribe will redirect to this url ifuserId
isnull
orundefined
Optional Props
paymentProvider
:'stripe' | 'dodo'
(default:'stripe'
).environment
:'test' | 'live'
(Dodo only, default:'live'
).redirectUrl
: Where to return after checkout.lumenHandlerUrl
: URL of your backend endpoint to fetch a user's current subscription (see below). Defaults to /api/lumen in the same origin.
Showing Current Subscription Status in the Pricing Table (Optional)
If a logged-in user opens the Pricing Table, ideally, we would like to show them their current subscription status. To do this, add the lumenHandlerUrl
prop. This requires setting up a backend endpoint that securely fetches subscription data.
Create Backend API Endpoint
On NextJS, you can create api/lumen/[...all]/route.ts
for App Router (or any other backend framework - just make the endpoint and pass it in lumenHandlerUrl
):
// api/lumen/[...all]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { lumenNextHandler } from "@getlumen/server";
import { auth } from "@clerk/nextjs/server";
const handler = async (request: NextRequest) => {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json(
await lumenNextHandler({
request,
userId,
})
);
};
export {
handler as GET,
handler as POST,
handler as PUT,
handler as DELETE,
handler as PATCH,
handler as OPTIONS,
handler as HEAD,
};
// api/lumen/[...all]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";
import { lumenNextHandler } from "@getlumen/server";
const handler = async (request: NextRequest) => {
const supabase = await createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json(
await lumenNextHandler({
request,
userId: user.id,
})
);
};
export {
handler as GET,
handler as POST,
handler as PUT,
handler as DELETE,
handler as PATCH,
handler as OPTIONS,
handler as HEAD,
};
// api/lumen/[...all]/route.ts
import { auth } from "@/lib/auth";
export async function GET(request: Request) {
const session = await auth.api.getSession({ headers: request.headers });
if (!session?.user) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
const { searchParams } = new URL(request.url);
const externalCustomerId = searchParams.get("externalCustomerId");
const response = await fetch(
`https://api.getlumen.dev/v1/customers/subscription-status?externalCustomerId=${encodeURIComponent(
externalCustomerId
)}`,
{
headers: {
Authorization: `Bearer ${process.env.LUMEN_SECRET_KEY}`,
},
}
);
return Response.json(await response.json());
}
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { lumenNextHandler } from "@getlumen/server";
const handler = async (request: NextRequest) => {
const session = await auth.api.getSession({ headers: request.headers });
if (!session?.user) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json(
await lumenNextHandler({
request,
userId: session.user.id,
})
);
};
export {
handler as GET,
handler as POST,
handler as PUT,
handler as DELETE,
handler as PATCH,
handler as OPTIONS,
handler as HEAD,
};
Make sure your LUMEN_API_KEY
and NEXT_PUBLIC_LUMEN_PUBLISHABLE_KEY
are set in your env vars.
LUMEN_API_KEY=sk_live_...
NEXT_PUBLIC_LUMEN_PUBLISHABLE_KEY=pk_live...
<PricingTable userId={session?.user?.id} loginRedirectUrl="/login" />