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

如何通过Stripe CLI传递客户邮箱?及支付字段访问确认

问题1:如何通过Stripe CLI传递客户邮箱

你可以通过Stripe CLI的--override参数自定义触发事件的payload字段,或者提前创建带邮箱的测试客户关联到事件中,两种方式任选:

方式1:直接覆盖事件中的邮箱字段

如果触发的是payment_intent.succeeded事件,可以直接修改付款记录的账单详情邮箱,无需提前创建客户:

stripe trigger payment_intent.succeeded --override payment_intent.charges.data[0].billing_details.email=test@example.com

方式2:关联带邮箱的测试客户

  1. 先创建一个带指定邮箱的测试客户:
stripe customers create --email test@example.com

执行后会返回一个客户ID(格式如cus_xxx123)。
2. 触发事件时将PaymentIntent关联到这个客户:

stripe trigger payment_intent.succeeded --override payment_intent.customer=cus_xxx123

此时webhook事件中的PaymentIntent会包含该客户ID,你可以通过Stripe SDK调用stripe.customers.retrieve(cus_xxx123)获取邮箱。

注意:你代码中监听的是payment_intent.succeeded事件,但之前提到用stripe trigger invoice.payment_succeeded,这两个事件对应的数据对象完全不同(一个是PaymentIntent,一个是Invoice),要保证事件类型和监听逻辑匹配。


问题2:event.data.object字段访问的正确性及生产环境验证

你的代码存在两处关键问题,生产环境也会报错:

1. 类型断言错误

你监听的是payment_intent.succeeded事件,event.data.object的实际类型是PaymentIntent,但你断言成了Invoice,这会导致TypeScript类型提示错误,且运行时无法找到Invoice专属字段。

2. 字段匹配错误

  • id:PaymentIntent确实有id字段,这个是正确的,但注意这是PaymentIntent的ID,不是Invoice ID。
  • amount_paid:这是Invoice的专属字段,PaymentIntent对应的字段是amount_captured(已成功捕获的金额),你访问amount_paid会返回undefined。
  • customer_email:PaymentIntent中没有这个直接字段,获取邮箱有两种方式:
    • 如果PaymentIntent关联了客户(即存在customer字段),需通过该客户ID调用Stripe API获取客户详情,从中提取email;
    • 如果是客人付款(未创建客户),可从payment_intent.charges.data[0].billing_details.email获取邮箱。

更适合你的优化方案

因为你用的是Stripe Checkout,推荐直接监听checkout.session.completed事件——这个事件是Checkout付款完成后触发的,data.object(CheckoutSession)中直接包含customer_email字段,无需额外查询:

const buy = async (req: Request, res: IResponse): Promise<IResponse> => {
  try {
    const signature = req.headers["stripe-signature"];
    const event = stripe.webhooks.constructEvent(
      req.rawBody,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET
    );
    if (event.type === "checkout.session.completed") {
      const session = event.data.object as Stripe.Checkout.Session;
      const { id: sessionId, customer_email, amount_total } = session;
      const serialNumber = await PaymentControler.generateUniqueSerialNumber();
      await PaymentControler.updateUserMembership(
        customer_email,
        sessionId, // 或取session.payment_intent拿到PaymentIntent ID
        amount_total,
        serialNumber
      );
      return res.sendStatus(200);
    }
    return res.sendStatus(200); // 非目标事件直接返回200,避免Stripe重复重试
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`); // 返回400让Stripe重试,500会终止重试
  }
};

生产环境验证

生产环境中,只要你前端Checkout配置了customerEmail,checkout.session.completed事件的customer_email字段一定会存在;如果用payment_intent.succeeded,只要付款流程正常,amount_captured和customer/charges字段也会正常返回,不存在测试环境和生产环境的字段差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 17:22:15