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

Stripe Payment Intent Webhook签名验证异常问题求助

Stripe Webhook签名验证失败的原因及解决方案

核心问题(你的代码直接原因)

你在代码中对payload做了解码再重新编码的操作:json_decode($request->getContent())后又json_encode(),这个过程会破坏Stripe发送的原始请求体结构——比如JSON键值的顺序、空格、转义字符格式都会和Stripe生成签名时使用的原始内容不一致,而Stripe的签名是严格基于原始未修改的payload生成的,所以必然验证失败。

修正后的代码

直接使用原始请求体,不要做解码再编码的操作:

public function handleWebhook(Request $request)
{
    $sPayload = $request->getContent(); // 直接使用原始请求内容
    $sSignature = $request->header('Stripe-Signature');
    $sWebhookSecret = env('STRIPE_WEBHOOK_SECRET');

    $oPaymentIntentResponse = Webhook::constructEvent(
         $sPayload, $sSignature, $sWebhookSecret
    );

    $this->processPaymentIntentFeedback($oPaymentIntentResponse);

    return response()->json(['success' => true]);
}

其他可能的排查方向

  • 中间件篡改请求体:如果应用有全局中间件(比如请求格式化、日志中间件),可能会自动修改JSON格式(如添加空格、调整缩进),导致payload和原始内容不符。可以暂时禁用相关中间件,或者在Webhook路由上排除这些中间件测试。
  • Webhook密钥不匹配:再次确认使用的是正式环境的Webhook密钥(不是测试环境密钥,也不是API密钥,两者完全不同),并且env('STRIPE_WEBHOOK_SECRET')确实加载到了正确的值,可以用dd($sWebhookSecret)输出验证。
  • 请求头获取异常:部分服务器/框架会将请求头转换为下划线或小写格式,比如Stripe-Signature变成HTTP_STRIPE_SIGNATURE,可以尝试用$request->server('HTTP_STRIPE_SIGNATURE')获取签名头。
  • 服务器时间偏差过大:Stripe签名包含时间戳,如果你的服务器时间和Stripe服务器时间偏差超过5分钟,会触发验证失败。可以同步服务器时间,或者在验证时手动设置允许的时间偏差:
    $oPaymentIntentResponse = Webhook::constructEvent(
         $sPayload, $sSignature, $sWebhookSecret,
         300 // 允许的时间偏差,单位秒,默认300
    );
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:05:01