You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js集成Stripe支付报错排查及官方文档函数咨询

Next.js + Stripe 集成问题:NaN 参数错误及购物车数据传递疑问

我参考两个旧版Stripe教程实现Next.js电商功能,教程版本早于当前Stripe官方文档,已调试20小时仍未解决问题。

现有两个核心问题:

  1. 定位当前出现的Invalid integer: NaN错误原因
  2. 第三方教程都有将购物车商品传至后端的逻辑,但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(),这会导致前端无法正确解析响应,可能引发后续跳转问题。

修复方案:

  1. 后端遍历购物车数组生成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支持商品图片
    }
  }
})),
  1. 修正响应方法:
res.status(200).json({ id: session.id }); // 小写json()
  1. 前端确保basket中每个商品的price是数字类型,避免字符串转数字时出现NaN。

问题2:官方文档为何没有类似函数

Stripe官方文档的示例通常聚焦于Stripe自身API的使用规范,而购物车数据传递属于业务逻辑范畴:

  • 官方文档不会替你实现购物车管理、前后端数据传递这类业务代码,只会说明如何将符合格式要求的商品数据传入Stripe Checkout Session。
  • 第三方教程为了完整演示流程,会补充业务层逻辑(比如购物车数据传递、格式转换),这些是项目特有的实现,不属于Stripe API的一部分。
  • 官方推荐的最佳实践是:后端从可信数据源(比如数据库)获取商品信息,而非完全依赖前端传递的数据(防止篡改价格),这也是官方文档不展示前端直接传购物车的原因之一。

内容的提问来源于stack exchange,提问作者Cameron Austin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 02:31:01