Rails对接Stripe Webhook报SignatureVerificationError签名错误求助
Stripe Webhook 签名验证错误排查方案
核心报错复现
#<Stripe::SignatureVerificationError: No signatures found matching the expected signature for payload> No template found for WebhooksController#create, rendering head :no_content Completed 204 No Content in 1ms (Allocations: 594)
异常触发点为Stripe::SignatureVerificationError签名校验失败分支,捕获逻辑仅打印日志后直接return,未返回明确响应。
排查修复步骤
1. 校验Webhook签名密钥正确性
- 必须使用对应Webhook端点生成的、以
whsec_开头的专属签名密钥,禁止混用Stripe账号的publishable key、secret key。 - 本地用Stripe CLI调试时,要使用CLI启动时输出的临时webhook密钥,不能用线上控制台配置的固定密钥。
- 进入Rails控制台执行
Rails.application.credentials[:stripe][:webhook],确认返回值为完整的whsec_字符串,无多余空格、换行,不存在nil值。
2. 修复请求体读取逻辑(最高发原因)
Rails框架的日志、参数解析等中间件会提前读取请求体,导致后续直接调用request.body.read拿到空值或不完整内容,签名校验必然失败。
将payload读取代码修改为如下内容,读取前先重置请求体指针:
request.body.rewind payload = request.body.read sig_header = request.env['HTTP_STRIPE_SIGNATURE']
注意:必须使用完全原始、未经过任何转义、截断、参数重写的请求体做校验,任意字符修改都会导致签名不匹配。
3. 补全异常分支的响应返回
当前代码捕获异常后直接return,未显式渲染响应,Rails会默认查找对应模板,找不到就返回204状态码,会导致Stripe判定投递失败反复重试。
将两个异常捕获块修改为如下逻辑,返回明确的400状态码:
rescue JSON::ParserError => e puts "Payload parse error: #{e.message}" render json: { error: "Invalid payload" }, status: 400 return rescue Stripe::SignatureVerificationError => e puts "Signature verify error: #{e.message}" render json: { error: "Invalid signature" }, status: 400 return
4. 本地调试配置校验
- 本地开发测试必须使用Stripe CLI做事件转发,执行命令:
启动后将CLI输出的stripe listen --forward-to localhost:3000/webhookswhsec_开头密钥配置到本地环境变量/credentials中。 - 若使用ngrok等内网穿透工具联调,需要先在Stripe控制台将穿透后的公网地址配置为正式Webhook端点,使用该端点生成的签名密钥做校验。
5. 其他配置校验
- 确认Webhook路由配置为POST请求,Stripe仅通过POST方法投递事件。
- 检查Nginx、反向代理、CDN层是否存在请求体修改逻辑,比如参数过滤、编码转换,这类操作会破坏原始payload导致签名失败。
- 确认环境匹配:测试环境的事件只能用测试环境的Webhook密钥校验,生产环境事件必须用生产环境密钥,禁止跨环境混用。
内容的提问来源于stack exchange,提问作者Pauli
相关产品推荐
相关产品推荐

