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

NextJS+Stripe:getStaticProps返回product序列化错误求助

Next.js单个商品页面序列化错误解决办法

Error: Error serializing .product returned from getStaticProps in "/products/[id]". Reason: undefined cannot be serialized as JSON. Please use null or omit this value.

问题代码

import Image from "next/image";
import { stripe } from "src/utils/stripe";
import { CheckIcon, MinusSmallIcon, PlusSmallIcon } from "@heroicons/react/24/solid"
import { formatCurrencyString, useShoppingCart } from "use-shopping-cart";
import { useState } from "react";
import { toast } from "react-hot-toast";

export default function ProductPage({ product }) {
    const [count, setCount] =  useState(1);
    const { addItem } = useShoppingCart()

    function onAddToCart(event) {
        event.preventDefault();
        const id = toast.loading("Adding 1 item...");
        addItem(product);
        toast.success(product.name, { id });
    }


    return (
        <div className="container lg:max-w-screen-lg mx-auto py-12 px-6">
            <div className="flex flex-col md:flex-row justify-between items-center space-y-8 md:space-y-0 md:space-x-12">
                <div className="relatie w-72 h-72 sm:w-96 sm:h-96">
                    <Image src={product.image} alt={product.name} fill style={{objectFit: "contain" }} sizes="100%" priority />
                </div>

                <div className="w-full flex-1 max-w-md border border-opacity-50 rounded-md shadow-lg p-6 bg-whitie">
                    <h2 className="text-3x1 font-semibold">{product.name}</h2>
                    <p className="pt-2 flex items-center space-x-2">
                        <CheckIcon className="text-lime-500 w-5 h-5" />
                        <span className="font-semibold">In stock</span>
                    </p>

                    <div className="mt-4 border-t pt-4">
                        <p className="text-gray-500">Price: </p>
                        <p className="text-xl front-semibold">{formatCurrencyString({ value: product.price, currency: product.currency,})} </p>
                    </div>

                    <div className="mt-4 border-t pt-4">
                        <p className="text-gray-500">Quantity</p>
                        <div className="mt-1 flex items-center space-x-3">
                            <button disabled={count <= 1} onClick={() => setCount(count - 1)} className="p-1 rounded-md hover:bg-rose-100 hover:text-rose-500">
                                <MinusSmallIcon className="w-6 h-6 flex-shrink-0"/>
                            </button>
                            <p className="font-semibold text-xl">{count}</p>
                            <button onAbort={() => setCount(count + 1)} className="p-1 rounded-md hover:bg-green-100 hover:text-green-500">
                                <PlusSmallIcon className="w-6 h-6 flex-shrink-0"/>
                            </button>
                        </div>
                    </div>

                    <button className="w-full mt-4 border border-lime-500 py-2 px-2 bg-lime-500 hover:bg-lime-600 hover:border-lime-600 focus:ring-4 focus:ring-opacity-50 focus:ring-lime-500 text-white disabled:opacity-50 disabled:cursor-not-allowed rounded-md">
                        Add to cart
                    </button>
                </div>
            </div>
        </div>
        );
}

export async function getStaticPaths() {
    const inventory = await stripe.products.list();
    const paths = inventory.data.map((product) => ({
        params: { id: product.id },
    }));

    return {
        paths, 
        fallback: "blocking"
    };
}

export async function getStaticProps({ params }) {
    const inventory = await stripe.products.list({
        expand: ["data.default_price"],
    });
    const products = inventory.data.map((product) => {
        const price = product.default_price;
        return {
          currency: price.currency,
          id: product.id,
          name: product.name,
          price: price.unit_amount,
          image: product.images[0],
        };
    });
    const product = products.find((product) => product.id === params.id);

    return {
        props: {
            product,
        },
        revalidate: 60 * 60,
    };
}

核心原因

  1. product变量可能为undefined:当products.find未匹配到对应ID的商品时,返回undefined,而Next.js不允许undefined作为序列化的props返回。
  2. Stripe API调用冗余:每次拉取所有商品再筛选,效率低且容易出现匹配失败的情况。
  3. 代码存在语法/拼写错误:比如按钮事件绑定错误、样式类名拼写错误,虽不直接触发序列化错误,但会影响页面功能。

解决步骤

1. 重构getStaticProps,确保product有效

直接通过商品ID查询单个商品,同时处理查询失败的情况:

export async function getStaticProps({ params }) {
  try {
    // 单独查询目标商品,展开默认价格
    const product = await stripe.products.retrieve(params.id, {
      expand: ["default_price"],
    });

    if (!product) {
      return { notFound: true }; // 返回404页面
    }

    const price = product.default_price;
    const formattedProduct = {
      currency: price.currency,
      id: product.id,
      name: product.name,
      price: price.unit_amount,
      image: product.images[0] || "", // 兜底空字符串,避免undefined
    };

    return {
      props: { product: formattedProduct },
      revalidate: 60 * 60,
    };
  } catch (error) {
    // 捕获API错误,返回404
    return { notFound: true };
  }
}

2. 增强组件健壮性

在组件开头添加商品存在性判断:

export default function ProductPage({ product }) {
  if (!product) {
    return <div className="container mx-auto py-12 text-center">商品不存在或已下架</div>;
  }

  // 剩余组件代码...
}

3. 修复代码中的其他错误

  • 将加号按钮的onAbort改为onClick
  • 修正样式类名:
    • relatie → relative
    • bg-whitie → bg-white
    • text-3x1 → text-3xl
    • front-semibold → font-semibold

4. 优化getStaticPaths(可选)

添加错误处理,避免API调用失败导致路径生成异常:

export async function getStaticPaths() {
  try {
    const inventory = await stripe.products.list();
    const paths = inventory.data.map((product) => ({
      params: { id: product.id },
    }));

    return { paths, fallback: "blocking" };
  } catch (error) {
    return { paths: [], fallback: "blocking" };
  }
}

内容的提问来源于stack exchange,提问作者2G-O

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 21:14:58