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

使用Stripe结账持续报错‘Missing required param: name’求助

Stripe结账报错"Missing required param: name"的解决方案

问题描述

按照学校课程步骤实现Stripe结账功能时,持续触发以下错误:

{"errors":[{"message":"Missing required param: name.","locations":[{"line":2,"column":3}],"path":["checkout"]}

已尝试完全复刻课程操作、查阅Stack Overflow相关问题、安装旧版本Stripe,均未解决问题。相关代码如下:

TypeDefs

const { gql } = require('apollo-server-express');

const typeDefs = gql`
  type Console {
    _id: ID
    name: String
  }

  type Product {
    _id: ID
    name: String
    description: String
    image: String
    quantity: Int
    price: Float
    console: Console
  }

  type Order {
    _id: ID
    purchaseDate: String
    products: [Product]
  }

  type User {
    _id: ID
    firstName: String
    lastName: String
    email: String
    orders: [Order]
  }

  type Checkout {
    session: ID
  }

  type Auth {
    token: ID
    user: User
  }

  type Query {
    consoles: [Console]
    products(console: ID, name: String): [Product]
    product(_id: ID!): Product
    user: User
    order(_id: ID!): Order
    checkout(products: [ID]!): Checkout
  }

  type Mutation {
    addUser(firstName: String!, lastName: String!, email: String!, password: String!): Auth
    addOrder(products: [ID]!): Order
    updateUser(firstName: String, lastName: String, email: String, password: String): User
    updateProduct(_id: ID!, quantity: Int!): Product
    login(email: String!, password: String!): Auth
  }
`;

module.exports = typeDefs;

Queries

import { gql } from "@apollo/client";

export const QUERY_PRODUCTS = gql`
  query getProducts($console: ID) {
    products(console: $console) {
      _id
      name
      description
      price
      quantity
      image
      console {
        _id
      }
    }
  }
`;

export const QUERY_ALL_PRODUCTS = gql`
  {
    products {
      _id
      name
      description
      price
      quantity
      console {
        name
      }
    }
  }
`;

export const QUERY_CONSOLES = gql`
  {
    consoles {
      _id
      name
    }
  }
`;

export const QUERY_USER = gql`
  {
    user {
      firstName
      lastName
      orders {
        _id
        purchaseDate
        products {
          _id
          name
          description
          price
          quantity
          image
        }
      }
    }
  }
`;

export const QUERY_CHECKOUT = gql`
query getCheckout($products: [ID]!) {
  checkout(products: $products) {
    session
  }
}
`;

Resolvers

const { AuthenticationError } = require("apollo-server-express");
const { User, Product, Console, Order } = require("../models");
const { signToken } = require("../utils/auth");
const stripe = require("stripe")("sk_test_*********");

const resolvers = {
  Query: {
    consoles: async () => {
      return await Console.find();
    },
    products: async (parent, { console, name }) => {
      const params = {};

      if (console) {
        params.console = console;
      }

      if (name) {
        params.name = {
          $regex: name
        };
      }

      return await Product.find(params).populate("console");
    },
    product: async (parent, { _id }) => {
      return await Product.findById(_id).populate("console");
    },
    user: async (parent, args, context) => {
      if (context.user) {
        const user = await User.findById(context.user._id).populate({
          path: "orders.products",
          populate: "console"
        });

        user.orders.sort((a, b) => b.purchaseDate - a.purchaseDate);

        return user;
      }

      throw new AuthenticationError("Not logged in");
    },
    order: async (parent, { _id }, context) => {
      if (context.user) {
        const user = await User.findById(context.user._id).populate({
          path: "orders.products",
          populate: "console"
        });

        return user.orders.id(_id);
      }

      throw new AuthenticationError("Not logged in");
    }, 
    checkout: async (parent, args, context) => {
      const url = new URL(context.headers.referer).origin;
      const order = new Order({ products: args.products });
      const { products } = await order.populate("products");

      const line_items = [];

      for (let i = 0; i < products.length; i++) {
        // generate product id
        const product = await stripe.products.create({
          name: products[i].name, 
          description: products[i].description,
          images: [`${url}/images/${products[i].image}`]
        });

        // generate price id using the product id
        const price = await stripe.prices.create({
          product: product.id, 
          unit_amount: products[i].price * 100, 
          currency: "usd", 
        });

        // add price id to the line items array
        line_items.push({
          price: price.id, 
          quantity: 1
        });
      }

      const session = await stripe.checkout.sessions.create({
        payment_method_types: ["card"], 
        line_items, 
        mode: "payment", 
        success_url: `${url}/success?session_id={CHECKOUT_SESSION_ID}`,
        cancel_url: `${url}/`
      });
      return { session: session.id };
    }
  },
  Mutation: {
    addUser: async (parent, args) => {
      const user = await User.create(args);
      const token = signToken(user);

      return { token, user };
    },
    addOrder: async (parent, { products }, context) => {
      console.log(context);
      if (context.user) {
        const order = new Order({ products });

        await User.findByIdAndUpdate(context.user._id, { $push: { orders: order } });

        return order;
      }

      throw new AuthenticationError("Not logged in");
    },
    updateUser: async (parent, args, context) => {
      if (context.user) {
        return await User.findByIdAndUpdate(context.user._id, args, { new: true });
      }

      throw new AuthenticationError("Not logged in");
    },
    updateProduct: async (parent, { _id, quantity }) => {
      const decrement = Math.abs(quantity) * -1;

      return await Product.findByIdAndUpdate(_id, { $inc: { quantity: decrement } }, { new: true });
    },
    login: async (parent, { email, password }) => {
      const user = await User.findOne({ email });

      if (!user) {
        throw new AuthenticationError("Incorrect credentials");
      }

      const correctPw = await user.isCorrectPassword(password);

      if (!correctPw) {
        throw new AuthenticationError("Incorrect credentials");
      }

      const token = signToken(user);

      return { token, user };
    }
  }
};

module.exports = resolvers;

排查分析

错误提示看似是GraphQL参数缺失,但实际问题出在Stripe API调用逻辑:

  1. 原代码每次结账都创建新的Stripe Product和Price,若数据库中某个Product的name字段为空或未正确填充,products[i].name会返回undefined,导致创建Stripe Product时缺失必填的name参数;
  2. 新版Stripe API对Checkout Session的line_items结构有更严格的校验,单独创建Product的流程容易出现参数传递遗漏。

修复方案

1. 检查数据库数据

确认MongoDB中所有Product文档的name字段都已正确赋值,无空值或未定义情况。

2. 修改CheckoutResolver逻辑

无需单独创建Stripe Product和Price,直接在line_items中通过price_data定义产品信息,既简化流程又避免参数缺失问题:

checkout: async (parent, args, context) => {
  const url = new URL(context.headers.referer).origin;
  const order = new Order({ products: args.products });
  const { products } = await order.populate("products");

  const line_items = [];

  for (let i = 0; i < products.length; i++) {
    // 提前校验产品名称,避免空值传入Stripe
    if (!products[i].name) {
      throw new Error(`产品ID ${products[i]._id} 未设置名称`);
    }

    line_items.push({
      price_data: {
        currency: "usd",
        product_data: {
          name: products[i].name,
          description: products[i].description,
          images: [`${url}/images/${products[i].image}`]
        },
        unit_amount: Math.round(products[i].price * 100), // 确保是整数金额
      },
      quantity: 1
    });
  }

  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    line_items,
    mode: "payment",
    success_url: `${url}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${url}/`
  });
  return { session: session.id };
}

3. 固定Stripe版本

在package.json中指定兼容的Stripe版本,避免版本自动升级导致的API变更问题:

"dependencies": {
  "stripe": "^10.17.0"
}

验证说明

修改后重新启动服务,测试结账流程:

  • 若数据库中存在无名称的产品,会提前抛出错误,方便快速定位数据问题;
  • 正常产品会直接生成包含完整信息的Stripe Checkout Session,不会再触发"Missing required param: name"错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 10:37:02