30
loading...
This website collects cookies to deliver better user experience
If you'd like to learn more about Next.js, check out my FREE course here.
nom install --save stripe @stripe/stripe-js
developers
link in the sidebar and click API keys..env.local
and create the following environment variables:NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
get-stripe.js
file, load the loading wrapper loadStripe
from the stripe-js
library.// get-stripe.js
import { loadStripe } from '@stripe/stripe-js';
getStripe
for doing so.// get-stripe.js
...
let stripePromise = null;
const getStripe = () => {
...
};
loadStripe
and pass in your Stripe publishable key using the environment variable we created earlier. And then, return the Stripe instance from that function.// get-stripe.js
...
const getStripe = () => {
if (!stripePromise) {
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
}
return stripePromise;
};
getStripe
function.export default getStripe;
getStripe
function from within our Next.js application, we will create the API endpoints we need to create a Stripe checkout session and retrieve the data from a checkout session using its session ID.api
under the pages
folder. And then, within this folder, create another folder called checkout_sessions
and create a file named index.js
.stripe
and then instantiate a new Stripe instance using your secret key from the STRIPE_SECRET_KEY
environment variable.// /pages/api/checkout_sessions/index.js
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
handler
function and export it as default.// /pages/api/checkout_sessions/index.js
...
export default async function handler(req, res) {
...
}
POST
request. Otherwise, return of 405
status code to the client that initiated that request.// /pages/api/checkout_sessions/index.js
...
export default async function handler(req, res) {
if (req.method === 'POST') {
...
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
POST
request, we will handle everything inside a try-catch
block. Finally, we return a 500
status code to the client if we catch an error.// /pages/api/checkout_sessions/index.js
...
if (req.method === 'POST') {
try {
...
} catch (err) {
res.status(500).json({ statusCode: 500, message: err.message });
}
}
create
function all the session's options. // /pages/api/checkout_sessions/index.js
...
if (req.method === 'POST') {
try {
const session = await stripe.checkout.sessions.create({
mode: 'payment',
payment_method_types: ['card'],
line_items: req?.body?.items ?? [],
success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${req.headers.origin}/cart`,
});
res.status(200).json(session);
} catch (err) {
res.status(500).json({ statusCode: 500, message: err.message });
}
}
success_url
will be used by Stripe to redirect the user once his payment has been successful. Here we use /success
for this URL and pass the current checkout session ID as a query parameter. /cart
. checkout_sessions
folder and call it [id].js
.// /pages/api/checkout_sessions/[id].js
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// /pages/api/checkout_sessions/[id].js
...
export default async function handler(req, res) {
const id = req.query.id;
}
try-catch
block, and if something goes wrong, return of 500
status code to the client.// /pages/api/checkout_sessions/[id].js
...
export default async function handler(req, res) {
const id = req.query.id;
try {
...
} catch (err) {
res.status(500).json({ statusCode: 500, message: err.message });
}
}
cs_
. Otherwise, throw an error.// /pages/api/checkout_sessions/[id].js
...
export default async function handler(req, res) {
const id = req.query.id;
try {
if (!id.startsWith('cs_')) {
throw Error('Incorrect CheckoutSession ID.');
}
const checkout_session = await stripe.checkout.sessions.retrieve(id);
res.status(200).json(checkout_session);
} catch (err) {
res.status(500).json({ statusCode: 500, message: err.message });
}
}
redirectToCheckout
.// /pages/cart.js
const redirectToCheckout = async () => {
...
};
axios
to perform a POST
request to the /api/checkout_sessions
API endpoint we just created.Note that the key for the ID is actually named price and NOT id.
// /pages/cart.js
const redirectToCheckout = async () => {
// Create Stripe checkout
const {
data: { id },
} = await axios.post('/api/checkout_sessions', {
items: Object.entries(cartDetails).map(([_, { id, quantity }]) => ({
price: id,
quantity,
})),
});
...
};
// /pages/cart.js
const redirectToCheckout = async () => {
...
// Redirect to checkout
const stripe = await getStripe();
await stripe.redirectToCheckout({ sessionId: id });
};
/api/webhook
, which is the Next.js API endpoint we are about to create just after that.checkout.session.completed
, which is the event that Stripe will send to the endpoint URL once a session has been completed successfully. In other words, when the user has successfully paid for its order.STRIPE_WEBHOOK_SECRET
inside the .env.local
file, and pass the value you just copied.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
api/
folder and call it webhook
. index.js
that we will use to implement our webhook API endpoint.stripe
and the buffer
method from the micro
npm package. You can install this package with npm install micro
. We are going to use this package/method to retrieve the raw body from the request.// /pages/api/webhook/index.js
import Stripe from 'stripe';
import { buffer } from 'micro';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
config
object with the following key/value to tell Next.js not to parse the body of the request because we need the raw data of that body to verify the webhook event signature. Why is it important? Because we need to make sure the webhook event was actually sent by Stripe and not by a malicious third party.// /pages/api/webhook/index.js
...
export const config = {
api: {
bodyParser: false,
},
};
POST
request. Otherwise, return of 405
status code.// /pages/api/webhook/index.js
...
export default async function handler(req, res) {
if (req.method === 'POST') {
...
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
try-catch
block to catch any errors that could occur.// /pages/api/webhook/index.js
...
export default async function handler(req, res) {
if (req.method === 'POST') {
let event;
try {
...
} catch (err) {
console.log(`❌ Error message: ${err.message}`);
res.status(400).send(`Webhook Error: ${err.message}`);
return;
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
// /pages/api/webhook/index.js
...
export default async function handler(req, res) {
if (req.method === 'POST') {
let event;
try {
const rawBody = await buffer(req);
const signature = req.headers['stripe-signature'];
event = stripe.webhooks.constructEvent(
rawBody.toString(),
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
...
}
} else {
...
}
}
// /pages/api/webhook/index.js
...
export default async function handler(req, res) {
if (req.method === 'POST') {
let event;
try {
...
} catch (err) {
...
}
// Successfully constructed event
console.log('✅ Success:', event.id);
// Handle event type (add business logic here)
if (event.type === 'checkout.session.completed') {
console.log(`💰 Payment received!`);
} else {
console.warn(`🤷♀️ Unhandled event type: ${event.type}`);
}
// Return a response to acknowledge receipt of the event.
res.json({ received: true });
} else {
...
}
}
success.js
file, create a new React component called Success
and export it as default.// /pages/success.js
const Success = () => {
...
}
export default Success;
useRouter
hook from next/router
to retrieve this id.// /pages/success.js
const Success = () => {
const {
query: { session_id },
} = useRouter();
...
}
GET
request to /api/checkout_sessions/${session_id}
using the useSWR
hook from the swr
package.// /pages/success.js
const Success = () => {
const {
query: { session_id },
} = useRouter();
const { data, error } = useSWR(
() => `/api/checkout_sessions/${session_id}`,
fetcher
);
...
}
useEffect
hook from React to shoot some fireworks onto the screen and clear the shopping cart. And finally, return the UI of this page.// /pages/success.js
const Success = () => {
...
useEffect(() => {
if (data) {
shootFireworks();
clearCart();
}
}, [data]);
return (
<div>{/* Your UI here */}</div>
);
}