Next.js集成Stripe支付报错排查及官方文档函数咨询
Next.js + Stripe 集成问题:NaN 参数错误及购物车数据传递疑问
我参考两个旧版Stripe教程实现Next.js电商功能,教程版本早于当前Stripe官方文档,已调试20小时仍未解决问题。
现有两个核心问题:
- 定位当前出现的
Invalid integer: NaN错误原因 - 第三方教程都有将购物车商品传至后端的逻辑,但Stripe官方文档中为何找不到对应示例?
曾尝试完全按照Stripe官方教程操作,虽能跳转页面,但无法传入购物车商品,最终重定向至空白页。
错误日志
error - StripeInvalidRequestError: Invalid integer: NaN at StripeError.generate (/Users/cameron/nextjs-amazon-clone/node_modules/stripe/lib/Error.js:38:16) at res.toJSON.then.StripeAPIError.message (/Users/cameronautism/nextjs-amazon-clone/node_modules/stripe/lib/StripeResource.js:190:35) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { type: 'StripeInvalidRequestError', raw: { code: 'parameter_invalid_integer', doc_url: 'https://stripe.com/docs/error-codes/parameter-invalid-integer', message: 'Invalid integer: NaN', param: 'line_items[0][price_data][unit_amount]', request_log_url: 'https://dashboard.stripe.com/test/logs/req_DZ6nOqke9AHTHi?t=1670342108', type: 'invalid_request_error', headers: { server: 'nginx', date: 'Tue, 06 Dec 2022 15:55:08 GMT', 'content-type': 'application/json', 'content-length': '369', connection: 'keep-alive', 'access-control-allow-credentials': 'true', 'access-control-allow-methods': 'GET, POST, HEAD, OPTIONS, DELETE', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required', 'access-control-max-age': '300', 'cache-control': 'no-cache, no-store', 'idempotency-key': 'e9fdd45b-a707-4421-adfb-e327b7bef077', 'original-request': 'req_DZ6nOqke9AHTHi', 'request-id': 'req_DZ6nOqke9AHTHi', 'stripe-version': '2022-11-15', 'strict-transport-security': 'max-age=63072000; includeSubDomains; preload' }, statusCode: 400, requestId: 'req_DZ6nOqke9AHTHi' }, rawType: 'invalid_request_error', code: 'parameter_invalid_integer', doc_url: 'https://stripe.com/docs/error-codes/parameter-invalid-integer', param: 'line_items[0][price_data][unit_amount]', detail: undefined, headers: { server: 'nginx', date: 'Tue, 06 Dec 2022 15:55:08 GMT', 'content-type': 'application/json', 'content-length': '369', connection: 'keep-alive', 'access-control-allow-credentials': 'true', 'access-control-allow-methods': 'GET, POST, HEAD, OPTIONS, DELETE', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required', 'access-control-max-age': '300', 'cache-control': 'no-cache, no-store', 'idempotency-key': 'e9fdd45b-a707-4421-adfb-e327b7bef077', 'original-request': 'req_DZ6nOqke9AHTHi', 'request-id': 'req_DZ6nOqke9AHTHi', 'stripe-version': '2022-11-15', 'strict-transport-security': 'max-age=63072000; includeSubDomains; preload' }, requestId: 'req_DZ6nOqke9AHTHi', statusCode: 400, charge: undefined, decline_code: undefined, payment_intent: undefined, payment_method: undefined, payment_method_type: undefined, setup_intent: undefined, source: undefined, page: '/api/checkout_sessions' } wait - compiling /... event - compiled client and server successfully in 684 ms (909 modules) error - StripeInvalidRequestError: Invalid integer: NaN at StripeError.generate (/Users/cameronautism/nextjs-amazon-clone/node_modules/stripe/lib/Error.js:38:16) at res.toJSON.then.StripeAPIError.message (/Users/cameronautism/nextjs-amazon-clone/node_modules/stripe/lib/StripeResource.js:190:35) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { type: 'StripeInvalidRequestError', raw: { code: 'parameter_invalid_integer', doc_url: 'https://stripe.com/docs/error-codes/parameter-invalid-integer', message: 'Invalid integer: NaN', param: 'line_items[0][price_data][unit_amount]', request_log_url: 'https://dashboard.stripe.com/test/logs/req_p7bQKyDNDYJcw3?t=1670342119', type: 'invalid_request_error', headers: { server: 'nginx', date: 'Tue, 06 Dec 2022 15:55:19 GMT', 'content-type': 'application/json', 'content-length': '369', connection: 'keep-alive', 'access-control-allow-credentials': 'true', 'access-control-allow-methods': 'GET, POST, HEAD, OPTIONS, DELETE', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required', 'access-control-max-age': '300', 'cache-control': 'no-cache, no-store', 'idempotency-key': 'f982b912-71b7-4583-b1c4-1918d03b8968', 'original-request': 'req_p7bQKyDNDYJcw3', 'request-id': 'req_p7bQKyDNDYJcw3', 'stripe-version': '2022-11-15', 'strict-transport-security': 'max-age=63072000; includeSubDomains; preload' }, statusCode: 400, requestId: 'req_p7bQKyDNDYJcw3' }, rawType: 'invalid_request_error', code: 'parameter_invalid_integer', doc_url: 'https://stripe.com/docs/error-codes/parameter-invalid-integer', param: 'line_items[0][price_data][unit_amount]', detail: undefined, headers: { server: 'nginx', date: 'Tue, 06 Dec 2022 15:55:19 GMT', 'content-type': 'application/json', 'content-length': '369', connection: 'keep-alive', 'access-control-allow-credentials': 'true', 'access-control-allow-methods': 'GET, POST, HEAD, OPTIONS, DELETE', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required', 'access-control-max-age': '300', 'cache-control': 'no-cache, no-store', 'idempotency-key': 'f982b912-71b7-4583-b1c4-1918d03b8968', 'original-request': 'req_p7bQKyDNDYJcw3', 'request-id': 'req_p7bQKyDNDYJcw3', 'stripe-version': '2022-11-15', 'strict-transport-security': 'max-age=63072000; includeSubDomains; preload' }, requestId: 'req_p7bQKyDNDYJcw3', statusCode: 400, charge: undefined, decline_code: undefined, payment_intent: undefined, payment_method: undefined, payment_method_type: undefined, setup_intent: undefined, source: undefined, page: '/api/checkout_sessions' }
后端 API 代码 (/api/checkout_sessions)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); export default async function handler(req, res) { const { items, email } = req.body; const session = await stripe.checkout.sessions.create({ shipping_rates: ["shr_1MBn0HFqoomU2P4qZk4vqOQ3"], shipping_address_collection: { allowed_countries: ["US", "CA", "GB"], }, line_items:[ { quantity: 1, price_data: { currency: 'usd', unit_amount: items.price * 100, product_data: { description: items.description, name: items.title, }}}], mode: 'payment', success_url: `${process.env.HOST}/success`, cancel_url: `${process.env.HOST}/checkout`, metadata: { email, images: JSON.stringify(items.map(items => items.image)) } }); res.status(200).JSON({ id:session.id}); };
前端 Checkout 页面代码
import Header from "../public/components/Header"; import Image from "next/image"; import CheckoutProduct from "../public/components/CheckoutProduct"; import React, { useState, useEffect} from "react"; import { useSession } from "next-auth/react"; import { useAppContext } from "../public/state"; import { loadStripe } from "@stripe/stripe-js"; import { Elements } from "@stripe/react-stripe-js"; import axios from 'axios'; // import CheckoutForm from "../public/components/CheckoutForm"; const stripePromise = loadStripe(process.env.stripe_public_key); function Checkout() { const [basket, addToBasket] = useAppContext(); const { data: session } = useSession(); const itemsInBasketEqualToProducts = basket.map(item => item.price); const sum = itemsInBasketEqualToProducts.reduce( (accumulator, currentValue) => accumulator + currentValue, 0 ); const createCheckoutForm = async () => { const stripe = await stripePromise; //call the backend to create a checkout session... const checkoutSession = await axios.post('/api/checkout_sessions', { items: basket, email: session.user.email }) const result = await stripe.redirectToCheckout({ sessionId: checkoutSession.data.id }); if (result.error) { alert(result.error.message) }; }; return ( <div className="bg-gray-100"> <Header /> <main className="lg:flex max-w-screen-xl mx-auto"> {/* this is to test if the array of objects are being assigned correctly , this is also the proper way to satisfy react if you get the error objects are not valid as a react child*/} {/* <div> {basketItemsObject.map((item) => ( <p>{item.id}</p> ))} </div> */} {/* left*/} <div className="flex-grow m-5 shadow-lg"> <Image src="https://links.papareact.com/ikj" width={2000} height={250} objectfit='contain' /> <div className="flex flex-col p-5 space-y-5 bg-white"> <h1 className="text-3xl border-b pb-4"> {/* {maybe} */} {basket.length === 0 ? 'Your Basket is empty' : 'Shopping Basket'} </h1> <CheckoutProduct/> </div> </div> {/* right */} {/* subtotal */} <div className=" bg-white shadow-lg flex"> {basket.length > 0 && ( <div className=""> <h2 className=" "> Subtotal ({basket.length} items): <span className="font-bold"> {sum} </span> </h2> <button onClick={createCheckoutForm} type="submit" role="link" disabled={!session} className={`button mt-2 ${ !session && ` from-gray-300 to-gray-500 border-gray-200 text-gray-100 cursor-not-allowed` }`} > {!session ? 'Sign in to checkout' : 'Proceed to checkout' } </button> </div> )} </div> </main> </div> ) }
问题1:错误原因定位
从错误日志和代码来看,核心问题是后端尝试访问items.price但items是一个数组而非单个对象:
- 前端传递的
items是整个购物车数组(basket),但后端代码直接用items.price,导致undefined * 100得到NaN,触发Stripe的参数验证错误。 - 同时后端的
line_items只生成了一个条目,没有遍历整个购物车数组,无法处理多商品场景。 - 额外问题:后端返回时用了
res.status(200).JSON(),正确写法是小写的json(),这会导致前端无法正确解析响应,可能引发后续跳转问题。
修复方案:
- 后端遍历购物车数组生成
line_items:
// 替换原line_items部分 line_items: items.map(item => ({ quantity: 1, // 如果购物车支持多数量,这里应该用item.quantity price_data: { currency: 'usd', unit_amount: parseInt(item.price) * 100, // 确保是整数 product_data: { description: item.description, name: item.title, images: [item.image] // Stripe支持商品图片 } } })),
- 修正响应方法:
res.status(200).json({ id: session.id }); // 小写json()
- 前端确保
basket中每个商品的price是数字类型,避免字符串转数字时出现NaN。
问题2:官方文档为何没有类似函数
Stripe官方文档的示例通常聚焦于Stripe自身API的使用规范,而购物车数据传递属于业务逻辑范畴:
- 官方文档不会替你实现购物车管理、前后端数据传递这类业务代码,只会说明如何将符合格式要求的商品数据传入Stripe Checkout Session。
- 第三方教程为了完整演示流程,会补充业务层逻辑(比如购物车数据传递、格式转换),这些是项目特有的实现,不属于Stripe API的一部分。
- 官方推荐的最佳实践是:后端从可信数据源(比如数据库)获取商品信息,而非完全依赖前端传递的数据(防止篡改价格),这也是官方文档不展示前端直接传购物车的原因之一。
内容的提问来源于stack exchange,提问作者Cameron Austin
相关产品推荐
相关产品推荐

