Stripe Webhook单元测试中如何生成有效stripe-signature签名?
测试环境生成合法Stripe Webhook签名的实现方案
完全可以在测试环境本地生成通过校验的合法签名,不需要依赖真实Stripe平台的回调,只要严格对齐Stripe官方的签名计算规则即可。核心注意点是签名校验是*基于原始请求体(raw body)*计算的,不是解析后的JSON对象,这也是绝大多数人测试时签名校验失败的原因。
具体实现步骤
- 统一测试环境的Webhook密钥:专门为测试场景定义一个固定的Webhook密钥(格式对齐Stripe真实密钥即可,比如
whsec_test_xxxx),保证你的Webhook路由里读取的stripeWebhookSecret和测试时计算签名用的密钥是同一个值,因为是单元测试,不需要对接真实Stripe服务。 - 严格按照Stripe的签名格式生成头字段:
合法的stripe-signature头格式固定为t=<秒级Unix时间戳>,v1=<HMAC-SHA256哈希值>,其中哈希值的计算输入为{时间戳}.{完整原始请求体字符串},使用测试Webhook密钥作为HMAC计算的密钥。 - 保证原始请求体完全一致:计算签名时用的字符串,必须和测试请求实际发送出去的未解析的请求体内容100%相同,哪怕序列化时多一个空格、字段顺序不一样,都会导致签名校验失败。
可直接复用的测试代码示例
import crypto from 'crypto'; // 与路由中读取的测试环境webhook密钥完全一致 const TEST_WEBHOOK_SECRET = 'whsec_test_custom_mock_key'; // 先手动序列化模拟事件,作为计算签名和实际发送的统一原始内容 const mockEventRawBody = JSON.stringify({ id: 'evt_mock_123', type: 'payment_intent.succeeded', // 此处替换为你需要的模拟Webhook事件数据 data: { object: { amount: 1000, status: 'succeeded' } } }); // 生成合法签名 const timestamp = Math.floor(Date.now() / 1000); // 使用秒级时间戳,匹配Stripe校验规则 const signedPayload = `${timestamp}.${mockEventRawBody}`; const hmac = crypto.createHmac('sha256', TEST_WEBHOOK_SECRET); const generatedSignature = hmac.update(signedPayload, 'utf8').digest('hex'); const validStripeSignature = `t=${timestamp},v1=${generatedSignature}`; // 发起测试请求 const result = await request(app.app) .post("/webhook/stripe") .set('stripe-signature', validStripeSignature) .set('Content-Type', 'application/json') .send(mockEventRawBody); // 直接传递序列化好的原始字符串,避免测试库自动序列化导致内容不一致
常见踩坑提示
- 时间戳偏差:Stripe默认允许签名时间戳与服务器当前时间的最大偏差为5分钟,测试时不要用太久之前的时间戳算签名,否则即使哈希值正确也会校验失败。
- 原始body丢失:如果你的路由使用了
express.json()这类请求解析中间件,需要在中间件执行前将原始请求的buffer缓存下来,供Stripe签名校验使用,不然拿到的解析后对象和计算签名时的原始字符串会不一致。 - 异常分支测试:如果需要覆盖签名校验失败的异常逻辑,只需要在生成签名时修改一个字符、或者用错误的密钥计算签名即可,不需要绕过校验逻辑。
内容的提问来源于stack exchange,提问作者Alko
相关产品推荐
相关产品推荐

