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

什么是webhook?React+Express开发者的Stripe支付场景疑问

理解Stripe Webhook的作用与实现逻辑

先直接说设置https://example.com/payment的作用

当你在Stripe后台把这个URL设为webhook地址后,Stripe会在发生支付相关的关键事件时,主动给这个URL发送POST请求,把事件详情推送给你的Express服务器。

比如用户付款成功、付款失败、发起退款、订阅续费成功/失败这些事件,Stripe不会等你去主动查询,而是直接把消息送到你的服务器上,让你能实时处理这些事件对应的业务逻辑。


为什么需要Webhook?

你前端和Stripe的交互只是用户操作层面的(比如用户点击付款、输入卡号),但很多核心流程是在Stripe后台完成的:

  • 用户付款后,Stripe需要和银行确认资金到账,这个过程可能有延迟
  • 订阅类产品到期后,Stripe会自动尝试扣费,这个事件你的前端完全感知不到
  • 有些退款是用户在Stripe后台发起的,你的系统没法主动获知

没有Webhook的话,你只能靠定时去调用Stripe API查状态,不仅效率低,还可能错过实时处理的时机(比如用户付款成功后立刻要给用户发确认邮件)。


具体的工作流程(以你的URL为例)

  1. 配置阶段:你在Stripe后台填写https://example.com/payment,同时获取Stripe提供的webhook签名密钥(用来验证请求的合法性)
  2. 事件触发:当Stripe检测到某个事件(比如charge.succeeded,即支付成功),它会把这个事件的JSON数据打包成POST请求,发送到你的/payment路由
  3. 服务器处理:你的Express服务器在/payment路由上编写处理逻辑:
    • 先验证这个请求确实来自Stripe(防止有人伪造请求搞破坏)
    • 根据事件类型执行对应的业务操作:比如更新数据库里的订单状态、发送邮件通知用户、解锁用户购买的服务
  4. 响应Stripe:处理完后给Stripe返回200状态码,告诉它你已经收到事件了;如果Stripe没收到200,会重试发送请求

新手友好的Express代码示例

// 先确保安装stripe包:npm install stripe
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const express = require('express');
const app = express();

// 注意:要保留原始请求体,因为Stripe的签名是基于原始body生成的
// 配置Express保留raw body
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));

// 处理webhook的路由
app.post('/payment', async (req, res) => {
  // 第一步:验证请求来自Stripe
  const sig = req.headers['stripe-signature'];
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
  let event;

  try {
    event = stripe.webhooks.constructEvent(req.rawBody, sig, webhookSecret);
  } catch (err) {
    // 验证失败,直接返回400
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // 第二步:根据事件类型处理业务逻辑
  switch (event.type) {
    case 'charge.succeeded':
      const successfulCharge = event.data.object;
      // 这里写你的逻辑:比如更新订单状态、发确认邮件
      console.log(`Payment succeeded for order: ${successfulCharge.metadata.orderId}`);
      break;
    case 'charge.failed':
      const failedCharge = event.data.object;
      // 处理付款失败:通知用户、更新订单为失败状态
      console.log(`Payment failed for customer: ${failedCharge.customer}`);
      break;
    // 其他常用事件:invoice.paid(订阅续费成功)、charge.refunded(退款完成)等
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }

  // 告诉Stripe我们收到了事件
  res.json({ received: true });
});

app.listen(3000, () => console.log('Server running on port 3000'));

新手容易踩的坑

  • 必须验证签名:这一步绝对不能省,不然恶意攻击者可以随便发个POST请求到你的/payment,伪造支付成功的事件,导致你的系统给用户解锁服务但实际没收到钱
  • 本地开发测试:Stripe没法访问你的localhost,所以需要用ngrok这类工具生成一个公网临时域名,映射到你的本地端口(比如https://xxxx.ngrok.io/payment),然后填到Stripe后台测试
  • 处理重试逻辑:如果你的服务器没返回200,Stripe会重试发送事件,所以你的处理逻辑要做成幂等的(比如同一个事件处理多次也不会重复执行操作,比如不会重复发邮件)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:50:45