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

如何将Stripe会话与Node.js+Express应用会话关联并传递客户邮箱

解决方案:从Stripe Webhook传递客户邮箱到成功页

问题背景

基于Node.js+Express搭建的Web应用,核心流程是:

  • 首页通过静态Stripe Payment Link引导用户跳转至结账页面
  • 支付完成后重定向至/success页,需显示付款客户的邮箱
  • Webhook可捕获客户邮箱,但因Stripe的Webhook请求与用户会话不共享,无法通过req.session传递数据

可行解决方案

方案一:动态生成Checkout链接关联用户会话(推荐)

通过Stripe API动态生成Checkout Session,将用户的sessionID作为关联ID传入,Webhook拿到事件后找到对应会话存储邮箱,成功页即可读取。

代码修改步骤:

  1. 替换静态Payment Link,后端生成Checkout URL
// 替换原GET /路由
app.get('/', async (req, res) => {
  console.log('Entering default route....session id = ' + req.sessionID);
  
  // 创建Stripe Checkout Session,传入用户sessionID作为关联标识
  const checkoutSession = await stripe.checkout.sessions.create({
    payment_link: 'pl_test_xxxx', // 替换为你的Payment Link ID
    client_reference_id: req.sessionID,
    success_url: 'http://localhost:3000/success',
    cancel_url: 'http://localhost:3000/',
  });

  // 渲染首页模板,传递生成的结账链接
  res.render('index', { checkoutUrl: checkoutSession.url });
});

对应的index.ejs模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stripe Payment</title>
</head>
<body>
    <p>Stripe Payment</p>
    <a href="<%= checkoutUrl %>">buy</a>
</body>
</html>
  1. 修改Webhook事件处理,关联会话存储邮箱
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  console.log('Entering Stripe webhook....')
  const sig = req.headers['stripe-signature'];

  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.log('Webhook Error: ' + err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  switch (event.type) {
    case 'checkout.session.completed':
      const sessionData = event.data.object;
      const customerEmail = sessionData.customer_details.email;
      const userSessionId = sessionData.client_reference_id;

      if (userSessionId && customerEmail) {
        // 从会话存储中获取用户会话并保存邮箱
        app.sessionStore.get(userSessionId, (err, userSession) => {
          if (err) {
            console.error('Failed to fetch session:', err);
            return;
          }
          if (userSession) {
            userSession.customerEmail = customerEmail;
            app.sessionStore.set(userSessionId, userSession, (saveErr) => {
              saveErr ? console.error('Failed to save session:', saveErr) : console.log('Email saved to session');
            });
          }
        });
      }
      break;
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  res.send();
});

此时/success路由无需修改,即可从req.session.customerEmail读取邮箱并渲染。

方案二:临时存储+查询参数(快速适配静态Payment Link)

若不想修改Payment Link生成方式,可通过唯一订单ID关联邮箱,用临时存储(如Redis、内存对象)暂存数据。

代码修改步骤:

  1. 安装依赖并生成订单ID
npm install uuid
const { v4: uuidv4 } = require('uuid');
// 临时存储(生产环境替换为Redis/数据库)
const orderEmailMap = {};

// 修改首页路由
app.get('/', (req, res) => {
  const orderId = uuidv4();
  req.session.orderId = orderId;
  // 构造带订单ID的支付链接(需确保Stripe允许自定义success_url参数)
  const paymentUrl = `https://buy.stripe.com/test_28oaGrdLl3q28jCbII?success_url=http://localhost:3000/success?orderId=${orderId}`;
  res.render('index', { paymentUrl });
});
  1. Webhook中存储邮箱与订单ID的关联
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  // 签名验证代码省略...

  switch (event.type) {
    case 'checkout.session.completed':
      const sessionData = event.data.object;
      const customerEmail = sessionData.customer_details.email;
      // 从success_url中解析订单ID
      const urlParams = new URLSearchParams(new URL(sessionData.success_url).search);
      const orderId = urlParams.get('orderId');

      if (orderId && customerEmail) {
        orderEmailMap[orderId] = customerEmail;
        // 设置1小时过期,避免内存泄漏
        setTimeout(() => delete orderEmailMap[orderId], 3600000);
      }
      break;
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  res.send();
});
  1. 修改成功页路由读取邮箱
app.get('/success', (req, res) => {
  const orderId = req.query.orderId;
  const customerEmail = orderEmailMap[orderId] || '未获取到邮箱';
  res.render('success', { customerEmail });
});

注意事项

  • 内存存储仅适合开发测试,生产环境必须使用Redis、数据库等持久化存储
  • express-session默认内存存储不可用于生产,推荐搭配connect-redis等插件
  • 务必保证Stripe Webhook签名验证正确,防止恶意请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 13:30:56