🎊 Embedded Checkout
Create an embedded stripe checkout page
Commands
Refer to the Stripe React Documentation
npm i @stripe/react-stripe-js @stripe/stripe-js
Code
Create an API rotue for the checkout session.
app/api/embedded-checkout/route.ts
import { NextResponse } from 'next/server';
import { stripe } from '@/utils/stripe';
export async function POST(request: Request) {
try {
const { priceId } = await request.json();
const session = await stripe.checkout.sessions.create({
ui_mode: 'embedded',
payment_method_types: ['card'],
line_items: [
{
price: priceId,
},
],
mode: 'subscription',
return_url: `${request.headers.get('origin')}/return?session_id={CHECKOUT_SESSION_ID}`,
});
return NextResponse.json({ id: session.id, client_secret: session.client_secret });
} catch (error: any) {
console.error(error);
return NextResponse.json({ message: error.message }, { status: 500 });
}
}
Create a client component to display the checkout form.
"use client";
import { loadStripe } from "@stripe/stripe-js";
import {
EmbeddedCheckoutProvider,
EmbeddedCheckout,
} from "@stripe/react-stripe-js";
import { useCallback, useRef, useState } from "react";
export default function EmbeddedCheckoutButton() {
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);
const [showCheckout, setShowCheckout] = useState(false);
const modalRef = useRef<HTMLDialogElement>(null);
const fetchClientSecret = useCallback(() => {
// Create a Checkout Session
return fetch("/api/embedded-checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ priceId: "price_1OtHkdBF7AptWZlcIjbBpS8r" }),
})
.then((res) => res.json())
.then((data) => data.client_secret);
}, []);
const options = { fetchClientSecret };
const handleCheckoutClick = () => {
setShowCheckout(true);
modalRef.current?.showModal();
};
const handleCloseModal = () => {
setShowCheckout(false);
modalRef.current?.close();
};
return (
<div id="checkout" className="my-4">
<button className="btn" onClick={handleCheckoutClick}>
Open Modal with Embedded Checkout
</button>
<dialog ref={modalRef} className="modal">
<div className="modal-box w-100 max-w-screen-2xl">
<h3 className="font-bold text-lg">Embedded Checkout</h3>
<div className="py-4">
{showCheckout && (
<EmbeddedCheckoutProvider stripe={stripePromise} options={options}>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
)}
</div>
<div className="modal-action">
<form method="dialog">
<button className="btn" onClick={handleCloseModal}>
Close
</button>
</form>
</div>
</div>
</dialog>
</div>
);
}
Create a server component page to handle the return URL.
import { stripe } from "@/utils/stripe";
async function getSession(sessionId: string) {
const session = await stripe.checkout.sessions.retrieve(sessionId!);
return session;
}
export default async function CheckoutReturn({ searchParams }) {
const sessionId = searchParams.session_id;
const session = await getSession(sessionId);
console.log(session);
if (session?.status === "open") {
return <p>Payment did not work.</p>;
}
if (session?.status === "complete") {
return (
<h3>
We appreciate your business! Your Stripe customer ID is:
{(session.customer as string)}.
</h3>
);
}
return null;
}