I'm trying to add a simple Stripe checkout to a Next.js App router app. The code works if I'm using the pages router in next.js but not in App router for some reason I get a 404 - This page could not be found error when I click the checkout button. Anyone know why that is happening?
Got the code here:
app/page.js:
'use client'
import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
// Make sure to call `loadStripe` outside of a component’s render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
);
export default function PreviewPage() {
React.useEffect(() => {
// Check to see if this is a redirect back from Checkout
const query = new URLSearchParams(window.location.search);
if (query.get('success')) {
console.log('Order placed! You will receive an email confirmation.');
}
if (query.get('canceled')) {
console.log('Order canceled -- continue to shop around and checkout when you’re ready.');
}
}, []);
return (
<form action="/api/checkout_sessions" method="POST">
<section>
<button type="submit" role="link">
Checkout
</button>
</section>
<style jsx>
{`
section {
background: #ffffff;
display: flex;
flex-direction: column;
width: 400px;
height: 112px;
border-radius: 6px;
justify-content: space-between;
}
button {
height: 36px;
background: #556cd6;
border-radius: 4px;
color: white;
border: 0;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
}
button:hover {
opacity: 0.8;
}
`}
</style>
</form>
);
}
app/api/checkout_sessions/route.js:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
// Create Checkout Sessions from body params.
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: 'price_id',
quantity: 1,
},
],
mode: 'subscription',
success_url: `${req.headers.origin}/?success=true`,
cancel_url: `${req.headers.origin}/?canceled=true`,
});
res.redirect(303, session.url);
} catch (err) {
res.status(err.statusCode || 500).json(err.message);
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
Sounds like you're facing a common hiccup with Next.js and its routing, especially when integrating something like Stripe. Given the details, it seems like the issue might be related to how you've structured your API route in your Next.js app.
In Next.js, API routes are meant to be placed under the
pages/apidirectory. This setup is crucial for Next.js to correctly recognize and handle API requests. From your code snippet, it looks like you've placed yourcheckout_sessionsroute within anapp/api/checkout_sessions/route.jspath.Here's a quick suggestion to potentially fix the 404 error:
Move Your API Route: Ensure your API route (
checkout_sessions) is directly under thepages/apidirectory. The correct path would bepages/api/checkout_sessions.js. This adjustment should help Next.js recognize your API route properly.Adjust the Form Action: After moving your API route file, make sure the form's action in your
PreviewPagecomponent points to the correct endpoint. Since your API route will now be atpages/api/checkout_sessions.js, your form action should be set to/api/checkout_sessionswhich it already seems to be, so you're good here.By moving your API route to the correct directory, Next.js should be able to properly handle requests to
/api/checkout_sessionswithout returning a 404 error. Give this a try and it should help with the checkout button issue. If there are any more bumps along the way or anything else you wanna chat about, just let me know!