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

Next.js集成Stripe支付时API请求返回404错误排查求助

Next.js集成Stripe支付API 404错误排查方案

问题描述

用Next.js搭建电商网站并集成Stripe支付功能时,调用API返回404错误。已将Stripe密钥存储在环境变量中(知晓并非最佳实践,暂未找到替代方案),检查过路由路径、变量拼写,前后端端口与主机也匹配(控制台可显示相关信息),仍无法定位问题根源,寻求排查思路。

相关代码文件

Shop.js

import Link from "next/link";
import { useState } from "react";
import Image from "next/image";
import bag from "pages/public/next.svg";
import { CreateCheckOutSession } from "../../components/Api/create-checkout-session";

function Shop() {
  // const [item, setItem] = useState({
  //   name: "Apple AirPods",
  //   description: "Latest Apple AirPods.",
  //   image: bag,
  //   quantity: 0,
  //   price: 999,
  // });
  const [item, setItem] = useState({
    name: "Apple AirPods",
    description: "Latest Apple AirPods.",
    image: "/path/to/bag.png",
    quantity: 0,
    image: bag,
    price: 999,
    // string instead of component
    // ...
  });
  const simpleItem = {
    name: item.name,
    description: item.description,
    price: item.price,
  };
  const changeQuantity = (value, number) => {
    setItem({ ...item, quantity: Math.max(0, value) });
  };

  const onQuantityPlus = () => {
    changeQuantity(item.quantity + 1);
  };

  const onQuantityMinus = () => {
    changeQuantity(item.quantity - 1);
  };
  const onInputChange = (e) => {
    changeQuantity(parseInt(e.target.value));
  };
  return (
    <div className="body-shop">
      <nav>
        <div className="arrow">
          <Link href="/">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="16"
              height="16"
              fill="currentColor"
              className="bi bi-arrow-left-square"
              viewBox="0 0 16 16"
            >
              <path
                fillRule="evenodd"
                d="M15 2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2zM0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm11.5 5.5a.5.5 0 0 1 0 1H5.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L5.707 7.5H11.5z"
              />
            </svg>
          </Link>
        </div>
        <div className="group">
          <Link href="/">Home</Link>
          <Link href="./About">About</Link>

          <Link href="./blog">Blog</Link>

          <Link href="./shop">Shop</Link>

          <Link href="./services">Personal styled Services</Link>
        </div>
      </nav>
      <h1>Shop</h1>
      <main>
        <div className="shadow-lg border rounded p-2 ">
          <Image src={item.image} width={300} height={150} />

          <h2 className="text-2xl">$ {item.price}</h2>
          <h3 className="text-xl">{item.name}</h3>
          <p className="text-gray-500">{item.description}</p>
          <p className="text-sm text-gray-600 mt-1">Quantity:</p>
          <div className="border rounded">
            <button
              onClick={onQuantityMinus}
              className="bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600"
            >
              -
            </button>
            <input
              type="number"
              className="p-2"
              onChange={onInputChange}
              value={item.quantity}
            />
            <button
              onClick={onQuantityPlus}
              className="bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600"
            >
              +
            </button>
          </div>
          <p>Total: ${item.quantity * item.price}</p>
          <button
            disabled={item.quantity === 0}
            onClick={() => CreateCheckOutSession(item)}
            className="bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100"
          >
            Buy
          </button>
        </div>
      </main>
    </div>
  );
}
export default Shop;

create-checkout-session.js

import { loadStripe } from "@stripe/stripe-js";
import axios from "axios";
const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
const stripePromise = loadStripe(publishableKey);

export const CreateCheckOutSession = async (item) => {
  const stripe = await stripePromise;
  // 修正API请求路径,指向正确的Next.js API路由
  const checkoutSession = await axios.post("/api/create-stripe-session", {
    item: item,
  });

  const result = await stripe.redirectToCheckout({
    sessionId: checkoutSession.data.id,
  });
  if (result.error) {
    alert(result.error.message);
  }
};

create-stripe-session.js

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);

async function CreateStripeSession(req, res) {
  const { item } = req.body;

  const redirectURL =
    process.env.NODE_ENV === "development"
      ? "http://localhost:3000"
      : "https://stripe-checkout-next-js-demo.vercel.app";

  const transformedItem = {
    price_data: {
      currency: "usd",
      product_data: {
        // 注意:Stripe要求图片URL必须是公网可访问的,本地路径无法被识别
        images: [item.image],
        name: item.name,
      },
      unit_amount: item.price * 100,
    },
    description: item.description,
    quantity: item.quantity,
  };

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    line_items: [transformedItem],
    mode: "payment",
    success_url: redirectURL + "?status=success",
    cancel_url: redirectURL + "?status=cancel",
    metadata: {
      images: item.image,
    },
  });

  res.json({ id: session.id });
}

export default CreateStripeSession;

核心排查点与修复建议

  • API路由位置错误:Next.js要求API路由文件必须放在pages/api目录下(App Router对应app/api)。你的create-stripe-session.js当前在components/Api目录,无法被识别为API端点,直接导致404。修复:将该文件移动到pages/api目录,请求路径对应/api/create-stripe-session。
  • 请求URL传错:原代码中直接将CreateStripeSession函数作为axios请求的URL,这是完全错误的。必须传入字符串格式的API路径,如/api/create-stripe-session。
  • 请求参数不匹配:后端期望从req.body.item获取商品数据,但前端原代码传的是组件本身{ Shop: Shop },完全不符合要求。修复:在Shop组件的Buy按钮点击时,将item状态作为参数传入CreateCheckOutSession,并在请求时正确传递{ item: item }。
  • 环境变量验证:确认STRIPE_SECRET_KEY和NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY在.env.local文件中拼写正确,且Next.js服务已重启加载最新变量;生产环境需确保平台(如Vercel)已配置对应环境变量。
  • 图片URL问题:Stripe要求商品图片必须是公网可访问的URL,本地路径(如/path/to/bag.png)或Next.js的Image组件资源无法被Stripe识别,后续测试需替换为公开图片链接。

内容的提问来源于stack exchange,提问作者Andrea Lopez Bravo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:00:55