Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions app/api/stripe-portal/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
try {
// TODO: Replace with dynamic customer ID from your authentication system
const CUSTOMER_ID = 'cus_test_customer_id';
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
const RETURN_URL = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';

if (!STRIPE_SECRET_KEY) {
return NextResponse.json(
{ error: 'Stripe secret key not configured' },
{ status: 500 }
);
}

const response = await fetch('https://api.stripe.com/v1/billing_portal/sessions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${STRIPE_SECRET_KEY}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
customer: CUSTOMER_ID,
return_url: RETURN_URL,
}),
});

if (!response.ok) {
const errorData = await response.text();
console.error('Stripe API error:', errorData);
return NextResponse.json(
{ error: 'Failed to create portal session' },
{ status: response.status }
);
}

const session = await response.json();
return NextResponse.json({ url: session.url });

} catch (error) {
console.error('Error creating portal session:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
35 changes: 26 additions & 9 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
"use client";

import Image from "next/image";

export default function Home() {
const handleStripePortal = async () => {
try {
const response = await fetch('/api/stripe-portal', {
method: 'POST',
});

if (!response.ok) {
throw new Error('Failed to create portal session');
}

const { url } = await response.json();
window.location.href = url;
} catch (error) {
console.error('Error opening Stripe portal:', error);
alert('Failed to open Stripe portal. Please try again.');
}
};
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
Expand All @@ -26,8 +45,14 @@ export default function Home() {
</ol>

<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
<button
onClick={handleStripePortal}
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
>
Open Stripe Portal
</button>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
Expand All @@ -41,14 +66,6 @@ export default function Home() {
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
Expand Down