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

NextJS亚马逊克隆项目出现Stripe权限错误(500状态码)

问题:NextJS亚马逊克隆项目出现500错误,Stripe提示需使用密钥API密钥

问题详情

跟随教程实现亚马逊克隆项目,代码与教程完全一致,但始终触发Unhandled Runtime Error Error: Request failed with status code 500。已尝试重新生成Firebase和Stripe密钥、修改Stripe运费率,问题仍存在。项目代码托管在GitHub仓库。

终端输出关键错误信息:

StripePermissionError: 此API调用无法使用可发布API密钥,请使用密钥API密钥。你可以在Stripe账户API密钥页面找到你的所有API密钥。
...
code: 'secret_key_required',
statusCode: 403

相关代码

后端API路由(/api/create-checkout-session)

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

export default async (req, res) => {
  const { items, email } = req.body;

  const transformedItems = items.map((item) => ({
    price_data: {
      currency: "gbp",
      unit_amount: item.price * 100,
      product_data: {
        name: item.title,
        images: [item.image],
        description: item.description,
      },
    },
    quantity: 1,
  }));

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    shipping_options: [
      {
        shipping_rates: ["shr_1M97piHPvZNNoU6bvw5ut7Oy"],
      },
    ],
    shipping_address_collection: {
      allowed_countries: ["GB", "US", "CA"],
    },
    line_items: transformedItems,
    mode: "payment",
    success_url: `${process.env.HOST}/success`,
    cancel_url: `${process.env.HOST}/checkout`,
    metadata: {
      email,
      images: JSON.stringify(items.map((item) => item.image)),
    },
  });
  res.status(200).json({ id: session.id });
};

Checkout组件

import Image from "next/image";
import React from "react";
import { useSelector } from "react-redux";
import CheckoutProduct from "../components/CheckoutProduct";
import Header from "../components/Header";
import { selectItems, selectTotal } from "../slices/basketSlice";
import Currency from "react-currency-formatter";
import { useSession } from "next-auth/client";
import { loadStripe } from "@stripe/stripe-js";
import axios from "axios";

const stripePromise = loadStripe(process.env.stripe_public_key);

function Checkout() {
  const items = useSelector(selectItems);
  const total = useSelector(selectTotal);
  const [session] = useSession();

  const createCheckoutSession = async () => {
    const stripe = await stripePromise;

    // 调用后端创建结账会话
    const checkoutSession = await axios.post("/api/create-checkout-session", {
      items: items,
      email: session.user.email,
    });

    // 重定向到Stripe结账页面
    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-2xl mx-auto">
        {/* 左侧购物车列表 */}
        <div className="flex-grow m-5 shadow-sm">
          <Image
            src="https://links.papareact.com/ikj"
            width={1020}
            height={250}
            objectFit="contain"
          />

          <div className="flex flex-col p-5 space-y-10 bg-white">
            <h1 className="text-3xl border-b pb-4">
              {items.length === 0
                ? "你的亚马逊购物车是空的。"
                : "购物篮"}
            </h1>

            {items.map((item, i) => (
              <CheckoutProduct
                key={i}
                id={item.id}
                title={item.title}
                rating={item.rating}
                price={item.price}
                description={item.description}
                category={item.category}
                image={item.image}
                hasPrime={item.hasPrime}
              />
            ))}
          </div>
        </div>

        {/* 右侧结账区域 */}
        <div className="flex flex-col bg-white p-10 shadow-md">
          {items.length > 0 && (
            <>
              <h2 className="whitespace-nowrap">
                小计 ({items.length} 件商品):{" "}
                <span className="font-bold">
                  <Currency quantity={total} currency="GBP" />
                </span>
              </h2>

              <button
                role="link"
                onClick={createCheckoutSession}
                disabled={!session}
                className={`button mt-2 ${!
                  session &&
                  "from-gray-300 to-gray-500 border-gray-200 text-gray-300 cursor-not-allowed"
                }`}
              >
                {!session ? "登录以结账" : "继续结账"}
              </button>
            </>
          )}
        </div>
      </main>
    </div>
  );
}

export default Checkout;

解决方案

  1. 核对Stripe密钥类型:

    • Stripe密钥分两种:密钥(Secret Key)以sk_开头,仅用于后端;可发布密钥(Publishable Key)以pk_开头,仅用于前端。
    • 确保环境变量STRIPE_SECRET_KEY的值是sk_开头的密钥,而非pk_开头的可发布密钥。
  2. 检查环境变量配置:

    • 打开项目根目录的.env.local文件,确认STRIPE_SECRET_KEY的取值正确。
    • 前端使用的可发布密钥,在NextJS中需要添加NEXT_PUBLIC_前缀,比如改为NEXT_PUBLIC_STRIPE_PUBLIC_KEY,并在Checkout组件中对应修改为process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY,否则前端无法读取该变量。
  3. 重启开发服务器:
    修改环境变量后,必须重启NextJS开发服务器,确保新的环境变量生效。

  4. 验证Stripe运费率ID:
    确认shipping_rates中的ID是你Stripe账户中已创建的有效运费率ID,避免因无效ID导致额外错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:50:38