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
相关产品推荐
相关产品推荐

