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

Firebase中Stripe Webhook返回500空错误体,求排查建议

Firebase中Stripe Webhook返回500空错误的排查思路

我在Firebase中开发用于接收结账完成更新的Stripe Webhook,但无法正常工作。Stripe持续返回500响应且错误体为空,相关代码如下:

相关代码

index.js

const webHooks = express();

PaymentController.prototype.bindWebHookEndpoints(webHooks);
exports.webhook = functions.region(REGION).https.onRequest(webHooks);

PaymentController.js

bindWebHookEndpoints(app) {
    /**
     * Endpoint to receive payments status updates.
     */
    app.post("/payments", express.raw({type: "application/json"}), (request, response) => {
      const signature = request.headers["stripe-signature"];
      console.log({rawBody: request.rawBody});
      return PaymentSystemRemoteService.prototype.postWebHookValidationAsync(request.rawBody.toString(), signature).then((event)=>{
        functions.logger.log({event: event});
        switch (event.type) {
          case "checkout.session.async_payment_failed":
            // 处理checkout.session.async_payment_failed事件
            break;
          case "checkout.session.async_payment_succeeded":
            // 处理checkout.session.async_payment_succeeded事件
            break;
          case "checkout.session.completed":
            // 处理checkout.session.completed事件
            break;
          default:
            console.log(`Unhandled event type ${event.type}`);
        }
        response.status(200).send({});
      }).catch((err)=>{
        functions.logger.log({err: err});
        response.status(500).send(err);
      });
    });
  }

PaymentSystemRemoteService.js

const endpointSecret = "whsec_..."; // 从Stripe开发者平台获取

postWebHookValidationAsync(body, signature) {
    return new Promise((res, rej)=>{
      return stripe.webhooks.constructEvent(body, signature, endpointSecret).then((event)=>{
        return res(event);
      }).catch((err)=>{
        return rej(err);
      });
    });
  }

排查思路

  • 优先验证签名环节
    Stripe的constructEvent抛出的错误是500响应的核心诱因:

    • 确认endpointSecret与Stripe控制台Webhook端点的签名密钥完全匹配,注意区分测试/生产环境密钥,避免串用。
    • 确保传递给constructEvent的原始请求体未被修改:对比日志中request.rawBody与Stripe测试工具的请求体,检查是否存在空格、换行符等差异。
    • 核对stripe-signature头的获取逻辑,部分环境下请求头会被转为小写,当前代码写法没问题,但需确认日志中该头是否存在。
  • 检查Firebase Functions的请求处理特性

    • 避免在express实例上全局使用express.json()或express.urlencoded()等解析中间件,这类中间件会提前消费原始请求体,导致request.rawBody失效。
    • 确认express.raw({type: "application/json"})中间件仅作用于/payments路由,未被其他中间件覆盖。
  • 修正错误处理逻辑
    当前catch块直接返回错误对象,Stripe可能无法序列化该对象导致错误体为空。修改为返回错误文本信息:

    response.status(500).send(err.message || 'Webhook validation failed');
    

    这样能在Stripe控制台看到具体错误详情。

  • 优化日志与代码结构

    • 在catch块中打印err.stack获取完整错误栈,精确定位出错位置。
    • 简化postWebHookValidationAsync的Promise包裹逻辑,stripe.webhooks.constructEvent本身已返回Promise,无需额外封装:
      postWebHookValidationAsync(body, signature) {
        return stripe.webhooks.constructEvent(body, signature, endpointSecret);
      }
      
      多余的Promise包裹可能导致异常捕获失效。
  • 利用Stripe测试工具排查
    使用Stripe控制台的Webhook测试工具发送测试事件,查看工具内的详细错误日志;同时确认Webhook端点URL已正确配置且能正常访问。

  • 排查异步处理超时问题
    处理checkout.session.completed等事件时,若存在数据库写入等耗时操作,需确保返回Promise等待操作完成,避免函数提前结束导致响应异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 21:01:11