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

如何通过Stripe API在服务端验证用户是否购买特定产品?

验证Stripe购买状态的正确方案

不要依赖前端传入的paystate参数,这种方式完全不可靠。正确的做法是利用Stripe Checkout Session的内置机制,通过服务端验证真实的支付会话。

步骤1:修改Checkout Session创建逻辑

创建会话时,在success_url中加入Stripe提供的{CHECKOUT_SESSION_ID}占位符,这样跳转时会自动携带真实的会话ID:

const session = await stripe.checkout.sessions.create({
  line_items: list,
  mode: 'payment',
  success_url: `${process.env.NEXT_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
  cancel_url: `${process.env.NEXT_URL}/cancel`,
});

步骤2:在getServerSideProps中验证会话

服务端通过会话ID直接向Stripe API查询真实的支付状态,同时验证用户和产品信息:

export async function getServerSideProps(context) {
  const { session_id } = context.query;

  // 没有会话ID直接跳转到取消页
  if (!session_id) {
    return { redirect: { destination: '/cancel', permanent: false } };
  }

  try {
    // 检索会话并展开支付意向、商品明细
    const session = await stripe.checkout.sessions.retrieve(session_id, {
      expand: ['payment_intent', 'line_items'],
    });

    // 1. 验证支付状态是否为已完成
    if (session.payment_status !== 'paid') {
      return { redirect: { destination: '/cancel', permanent: false } };
    }

    // 2. 验证当前用户邮箱与会话中的客户邮箱匹配(需从登录态获取当前用户邮箱)
    const currentUserEmail = "当前登录用户的邮箱"; // 替换为你的用户邮箱获取逻辑
    if (session.customer_email !== currentUserEmail) {
      return { props: { hasPurchased: false, error: "用户身份不匹配" } };
    }

    // 3. 验证是否购买了目标产品(替换为你的产品ID)
    const targetProductId = "你的特定产品ID";
    const hasPurchasedTarget = session.line_items.data.some(item => 
      item.price.product === targetProductId
    );

    return {
      props: {
        hasPurchased: hasPurchasedTarget,
        orderDetails: {
          amount: session.amount_total,
          productName: session.line_items.data[0].description
        }
      }
    };
  } catch (err) {
    // 会话不存在或API调用失败,跳转到取消页
    console.error("验证错误:", err);
    return { redirect: { destination: '/cancel', permanent: false } };
  }
}

为什么你的原有代码无法获取购买记录?

  1. 查询对象错误:在payment模式下,一次性支付的记录优先通过Checkout Session或Payment Intent查询,直接调用charges.list容易遗漏或过滤不到目标记录。如果一定要用charges.list,需要添加过滤条件:
    const charges = await stripe.charges.list({
      customer: customerId,
      status: 'succeeded', // 只查成功的支付
      limit: 100 // 扩大查询范围
    });
    
  2. 会话关联更可靠:通过Checkout Session验证能直接关联到本次结账行为,避免查询用户所有历史订单的冗余,同时保证验证的唯一性和准确性。

关键注意事项

  • 永远不要信任前端传入的任何支付状态参数,必须通过Stripe API做服务端验证
  • 确保你的Stripe Secret Key存储在环境变量中,不要暴露在前端
  • 测试模式下,可以使用Stripe提供的测试卡号(如4242 4242 4242 4242)模拟支付成功场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 06:55:18