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

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做事件转发,执行命令:
    stripe listen --forward-to localhost:3000/webhooks
    
    启动后将CLI输出的whsec_开头密钥配置到本地环境变量/credentials中。
  • 若使用ngrok等内网穿透工具联调,需要先在Stripe控制台将穿透后的公网地址配置为正式Webhook端点,使用该端点生成的签名密钥做校验。

5. 其他配置校验

  • 确认Webhook路由配置为POST请求,Stripe仅通过POST方法投递事件。
  • 检查Nginx、反向代理、CDN层是否存在请求体修改逻辑,比如参数过滤、编码转换,这类操作会破坏原始payload导致签名失败。
  • 确认环境匹配:测试环境的事件只能用测试环境的Webhook密钥校验,生产环境事件必须用生产环境密钥,禁止跨环境混用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:00:53