Stripe结账功能提示未提供API密钥报错排查
Stripe结账报API密钥缺失问题排查修复
核心问题成因
- 前端代码无法读取服务端专属环境变量:你在前端
handleCheckOut函数里直接引用process.env.STRIPE_SECRET_KEY是拿不到值的。在Next.js等全栈框架中,不带NEXT_PUBLIC_前缀的环境变量仅会在服务端注入,前端打包后对应变量值为undefined,导致你传入的Authorization请求头实际为空,触发密钥缺失报错。更严重的是,Stripe私钥(SK开头)属于高敏感权限凭证,绝对不能出现在前端代码中,否则任何人都能通过浏览器开发者工具拿到凭证,完全控制你的Stripe账户。 - 多余的鉴权头配置:你不需要在前端请求自己的
/api/stripe接口时添加Stripe的Bearer鉴权头,这个鉴权逻辑是服务端调用Stripe服务时才需要实现的,前后端内部请求加这个头毫无意义,还会带来密钥泄露风险。 - 环境变量命名不符合框架规则:你在
getStripe.js中加载Stripe公钥时,同样没有给公钥环境变量加NEXT_PUBLIC_前缀,前端读取到的公钥值也是undefined,后续跳转结账页会触发二次报错。 - 多处代码语法/参数错误:
- 前端判断响应状态时用了
response.statusCode,但fetch API返回的响应对象状态字段为response.status,原写法导致500错误判断逻辑完全失效 - API路由中打印日志时写了
req.body.cartItems,但前端传参时直接将购物车数组作为请求体发送,req.body本身就是购物车数据,不存在cartItems属性,日志会打印出undefined - 创建结账会话的参数中
payment_method_type为错误写法,Stripe要求的参数名是复数形式payment_method_types - 注释掉了必填的
success_url和cancel_url参数,会导致结账会话创建失败
- 前端判断响应状态时用了
修复步骤
- 调整环境变量配置
保留私钥变量名STRIPE_SECRET_KEY不变(仅服务端使用,不要加公网暴露前缀),将Stripe公钥的变量名修改为NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,修改完成后必须重启开发服务,环境变量改动才会生效。 - 修复前端结账函数
删除fetch请求中多余的Authorization头,修正响应状态判断字段,修改后代码如下:
const handleCheckOut = async () => { const stripe = await getStripe(); const response = await fetch("/api/stripe", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(cartItems), }); if (response.status === 500) return; const data = await response.json(); toast.loading("Redirecting..."); stripe.redirectToCheckout({ sessionId: data.id }); };
- 修复Stripe公钥加载逻辑
调整getStripe.js中的环境变量读取,对应修改后的公钥变量名:
import { loadStripe } from "@stripe/stripe-js"; let stripePromise; const getStripe = () => { if (!stripePromise) { stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY); } return stripePromise; }; export default getStripe;
- 修复API路由代码
修正日志打印、参数名错误,补全必填的跳转地址参数,初始化Stripe客户端时指定固定API版本避免版本兼容问题,修改后核心代码如下:
import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2022-11-15", // 可替换为你Stripe账户后台显示的当前使用版本 }); export default async function handler(req, res) { console.log(req.body); if (req.method === "POST") { try { const params = { submit_type: "pay", mode: "payment", payment_method_types: ["card"], billing_address_collection: "auto", shipping_options: [ { shipping_rate: "shr_1LJo2EHt0s8JSRoPQEDeHfo5" }, { shipping_rate: "shr_1LJo3ZHt0s8JSRoP8uVNJhwS" }, ], line_items: req.body.map((item) => { const img = item.image[0].asset._ref; const newImage = img .replace( "image-", "https://cdn.sanity.io/images/psdgq2wv/production/" ) .replace("-webp", ".webp"); return { price_data: { currency: "usd", product_data: { name: item.name, images: [newImage], }, unit_amount: item.price * 100, }, adjustable_quantity: { enabled: true, minimum: 1, }, quantity: item.quantity, }; }), success_url: `${req.headers.origin}/?success=true`, cancel_url: `${req.headers.origin}/?canceled=true`, }; const session = await stripe.checkout.sessions.create(params); res.status(200).json(session); } catch (err) { res.status(err.statusCode || 500).json(err.message); } } else { res.setHeader("Allow", "POST"); res.status(405).end("Method Not Allowed"); } }
内容的提问来源于stack exchange,提问作者Cláudio Vitor Dantas
相关产品推荐
相关产品推荐

