Next.js 13集成Stripe支付遇405(Method Not Allowed)错误求助
Next.js集成Stripe支付网关时405 (Method Not Allowed)错误的解决
问题场景
在Next.js应用中集成Stripe支付网关,遵循官方文档操作后遇到以下错误:
405 (Method Not Allowed)
相关代码如下:
CheckoutForm.js
// components/CheckoutForm.js import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; import axios from 'axios'; const CheckoutForm = ({ cartItems, email }) => { const stripe = useStripe(); const elements = useElements(); const handleSubmit = async (event) => { event.preventDefault(); if (!stripe || !elements) { return; } try { const response = await fetch("/api/checkout_sessions", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ items: cartItems }), }); const data = await response.json(); // Destructure clientSecret directly from the response const { clientSecret } = data; // Confirm the card payment const result = await stripe.confirmCardPayment(clientSecret, { payment_method: { card: elements.getElement(CardElement), billing_details: { email: email, }, }, }); if (result.error) { // Show error to your customer console.error(result.error.message); } else if (result.paymentIntent.status === "succeeded") { // Payment succeeded, you can redirect or show a success message console.log("Payment succeeded!"); // Redirect or show success message } } catch (error) { console.error("Error:", error.message); } }; return ( <div className="bg-gray-200 pt-3 pb-2" > <small className="flex w-full justify-left">Credit / Debit Card Details</small> <form onSubmit={handleSubmit}> <div style={{ margin: '20px 0' }}> <CardElement /> </div> <button className="mt-8 flex w-full justify-center rounded-md border border-transparent bg-[#f1592a] py-2 px-4 text-sm font-medium text-white shadow-sm" type="submit" disabled={!stripe}> Pay </button> </form> </div> ); }; export default CheckoutForm;
app/api/check_sessions/route.js
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); export default async function handler(req, res) { switch (req.method) { case "POST": try { const { items } = req.body; // Create an array to store line items const lineItems = items.map((item) => ({ price: item.priceId, // Replace with the actual Price ID from your data quantity: item.quantity, })); // Create Checkout Sessions from body params. const session = await stripe.checkout.sessions.create({ ui_mode: 'embedded', line_items: lineItems, mode: 'payment', return_url: `${req.headers.origin}/return?session_id={CHECKOUT_SESSION_ID}`, }); res.send({clientSecret: session.client_secret}); } catch (err) { res.status(err.statusCode || 500).json(err.message); } case "GET": try { const session = await stripe.checkout.sessions.retrieve(req.query.session_id); res.send({ status: session.status, customer_email: session.customer_details.email }); } catch (err) { res.status(err.statusCode || 500).json(err.message); } default: res.setHeader('Allow', req.method); res.status(405).end('Method Not Allowed'); } }
错误原因及修复步骤
1. API路由路径拼写不匹配
前端请求的路径是/api/checkout_sessions,但后端API文件放在app/api/check_sessions/route.js,对应路径为/api/check_sessions(少了字母o),前后路径不一致导致请求找不到正确的处理路由。
修复:
- 要么将前端请求路径改为
/api/check_sessions,要么把后端文件夹重命名为checkout_sessions,确保前后路径完全一致。
2. Next.js App Router API路由写法错误
你使用的是Next.js App Router(app/目录结构),但API路由采用了Pages Router的export default handler+switch判断写法,这不符合App Router的规范。App Router要求每个HTTP方法单独导出对应的处理函数。
修复:
将app/api/check_sessions/route.js改为以下符合App Router规范的写法:
import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); export async function POST(request) { try { const { items } = await request.json(); const lineItems = items.map((item) => ({ price: item.priceId, quantity: item.quantity, })); const session = await stripe.checkout.sessions.create({ ui_mode: 'embedded', line_items: lineItems, mode: 'payment', return_url: `${new URL(request.url).origin}/return?session_id={CHECKOUT_SESSION_ID}`, }); return new Response(JSON.stringify({ clientSecret: session.client_secret }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } catch (err) { return new Response(JSON.stringify(err.message), { status: err.statusCode || 500, headers: { 'Content-Type': 'application/json' }, }); } } export async function GET(request) { try { const { searchParams } = new URL(request.url); const sessionId = searchParams.get('session_id'); const session = await stripe.checkout.sessions.retrieve(sessionId); return new Response(JSON.stringify({ status: session.status, customer_email: session.customer_details?.email }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } catch (err) { return new Response(JSON.stringify(err.message), { status: err.statusCode || 500, headers: { 'Content-Type': 'application/json' }, }); } }
3. 原switch语句缺少break(次要问题)
即使使用Pages Router的写法,原代码中case "POST"和case "GET"分支都没有添加break,会导致POST请求执行完后继续进入GET分支,最终落入default返回405。改用App Router的写法后该问题自动解决。
额外验证点
- 确保
.env.local文件中正确配置了STRIPE_SECRET_KEY,且重启开发服务器让环境变量生效。 - 确认
cartItems中的priceId是Stripe后台创建的有效价格ID。
内容的提问来源于stack exchange,提问作者fagbemi ayodele
相关产品推荐
相关产品推荐

