使用Next.js App Router时Stripe Checkout出现404错误,求解决
Next.js App Router集成Stripe Checkout出现404错误的原因及解决
在Next.js Pages Router中可正常运行的Stripe Checkout代码,迁移到App Router环境后点击结账按钮出现「404 - 页面未找到」错误,核心原因是App Router的API路由语法与Pages Router完全不同,你的API路由代码仍沿用了Pages Router的写法,导致Next.js无法正确识别该API端点。
现有代码问题分析
app/page.js(客户端组件代码)
这段代码本身无问题,但表单提交的目标/api/checkout_sessions对应的API路由写法不符合App Router规范:
'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(问题核心)
App Router要求API路由通过导出对应HTTP方法的函数(如POST、GET)处理请求,而非Pages Router的export default handler模式。你的代码仍使用handler函数,导致Next.js无法映射到正确的API端点,触发404:
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'); } }
修正后的API路由代码
将app/api/checkout_sessions/route.js修改为App Router支持的写法:
import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); export async function POST(req) { try { const session = await stripe.checkout.sessions.create({ line_items: [ { price: 'price_id', quantity: 1, }, ], mode: 'subscription', success_url: `${req.headers.get('origin')}/?success=true`, cancel_url: `${req.headers.get('origin')}/?canceled=true`, }); return Response.redirect(session.url, 303); } catch (err) { return new Response(err.message, { status: err.statusCode || 500 }); } }
关键修改点
- 用
export async function POST(req)替代原handler函数,明确处理POST请求 - 使用App Router的
ResponseAPI替代Express风格的res.redirect、res.status等响应方法 - 通过
req.headers.get('origin')获取请求来源,替换原req.headers.origin - 改用ES模块导入
stripe,保持与App Router模块系统一致
内容的提问来源于stack exchange,提问作者StackUnderFlow
相关产品推荐
相关产品推荐

