跳到主要内容
Didit 融资 750 万美元,打造身份与欺诈基础设施
Didit
返回博客
博客 · 2026年3月6日

使用Fastify和PostgreSQL构建Didit Webhook监听器 (ZH)

学习如何使用Node.js、Fastify和PostgreSQL为Didit的实时身份验证通知构建一个健壮且安全的Webhook监听器。.

作者:Didit更新于
building-a-didit-webhook-listener-with-fastify-postgresql.png

安全Webhook集成实施HMAC-SHA256签名验证对于确保Didit Webhook有效载荷的真实性和完整性至关重要,保护您的应用程序免受伪造请求的侵害并维护数据安全。

实时数据处理利用Webhooks接收身份验证会话状态变化的即时通知,使您的应用程序能够立即作出反应,并自动化用户入职或风险管理工作流程。

可扩展数据存储利用PostgreSQL高效存储和管理Webhook数据,实现强大的查询、审计以及与现有用户管理系统的集成,确保数据持久性和可靠性。

Didit的模块化优势Didit的模块化身份平台和全面的Webhook系统简化了集成,为开发人员提供了清晰的API和实时数据流,以构建灵活的、AI原生的KYC解决方案,提供免费核心KYC且无设置费用。

理解Didit Webhooks及其重要性

在身份验证领域,实时反馈至关重要。无论是新用户入职、防范欺诈还是确保合规性,实时了解验证会话的状态都至关重要。这就是Webhooks的作用。Didit的Webhooks提供了一种自动化的方式,可以接收关于身份验证工作流程中发生的事件的通知,例如会话完成、失败或需要进一步操作。

Didit的Webhooks不会不断轮询Didit API以获取更新(这可能效率低下并导致延迟),而是将数据直接推送到您配置的端点。这种事件驱动的架构确保您的应用程序始终拥有最新信息,从而实现即时处理和更流畅的用户体验。例如,一旦用户成功完成Didit的身份验证被动与主动活体检测,Webhook可以立即触发您的入职流程的下一步。

Didit提供可配置的Webhook版本,v3是推荐的标准,因为它具有全面的有效载荷结构。您可以直接通过Didit业务控制台或通过API设置您的Webhook URL、版本,甚至定义数据保留策略,从而完全控制您的数据流和合规性义务。

设置您的Fastify Webhook监听器

Fastify是一个用于Node.js的快速且低开销的Web框架,是构建高性能Webhook监听器的绝佳选择。以下是入门方法:

1. 项目设置和依赖项

首先,初始化您的Node.js项目并安装必要的软件包:

mkdir didit-webhook-listener
cd didit-webhook-listener
npm init -y
npm install fastify dotenv pg crypto

创建一个.env文件来存储您的Didit Webhook密钥和PostgreSQL连接详细信息:

DIDIT_WEBHOOK_SECRET=your_didit_webhook_secret_key
DATABASE_URL=postgresql://user:password@host:port/database

您可以从Didit业务控制台的“应用设置”中或通过Didit APIGET /v3/webhook/端点检索您的DIDIT_WEBHOOK_SECRET

2. Fastify服务器配置

创建一个app.js文件。Fastify需要原始正文进行签名验证,因此我们将配置它,使其不对我们的Webhook路由自动解析JSON。

require('dotenv').config();
const fastify = require('fastify')({ logger: true });
const crypto = require('crypto');
const { Client } = require('pg');

const DIDIT_WEBHOOK_SECRET = process.env.DIDIT_WEBHOOK_SECRET;
const DATABASE_URL = process.env.DATABASE_URL;

const pgClient = new Client({ connectionString: DATABASE_URL });

// 为'application/json'注册一个内容解析器,它为特定路由保留原始正文
fastify.addContentTypeParser('application/json', { parseAs: 'buffer' }, function (req, body, done) {
  if (req.raw.url === '/webhooks/didit') {
    done(null, body); // 为webhook端点保留原始数据
  } else {
    try {
      const json = JSON.parse(body.toString());
      done(null, json);
    } catch (error) {
      error.statusCode = 400;
      done(error, undefined);
    }
  }
});

fastify.post('/webhooks/didit', async (request, reply) => {
  const signature = request.headers['x-signature'];
  const timestamp = request.headers['x-timestamp'];
  const rawBody = request.body;

  if (!signature || !timestamp || !rawBody) {
    reply.code(400).send({ message: '缺少签名、时间戳或正文' });
    return;
  }

  // 1. 验证HMAC-SHA256签名
  const expectedSignature = crypto
    .createHmac('sha256', DIDIT_WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody.toString()}`)
    .digest('hex');

  if (expectedSignature !== signature) {
    reply.code(403).send({ message: '无效签名' });
    return;
  }

  // 2. 验证时间戳(例如,在5分钟内以防止重放攻击)
  const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
  if (parseInt(timestamp) < fiveMinutesAgo) {
    reply.code(403).send({ message: '时间戳过旧' });
    return;
  }

  // 3. 验证后解析JSON正文
  let payload;
  try {
    payload = JSON.parse(rawBody.toString());
  } catch (error) {
    reply.code(400).send({ message: '无效的JSON有效载荷' });
    return;
  }

  fastify.log.info('收到Didit webhook:', payload);

  // 4. 处理webhook有效载荷(例如,存储到数据库,更新用户状态)
  try {
    await pgClient.query(
      'INSERT INTO webhooks (event_id, event_type, payload, received_at) VALUES ($1, $2, $3, NOW())',
      [payload.id, payload.event, JSON.stringify(payload)]
    );
    // 示例:根据payload.status更新用户状态
    // await pgClient.query(
    //   'UPDATE users SET verification_status = $1 WHERE didit_session_id = $2',
    //   [payload.status, payload.session_id]
    // );
    reply.code(200).send({ message: 'Webhook已接收并处理' });
  } catch (dbError) {
    fastify.log.error('数据库错误:', dbError);
    reply.code(500).send({ message: '内部服务器错误' });
  }
});

const start = async () => {
  try {
    await pgClient.connect();
    fastify.log.info('已连接到PostgreSQL');
    await fastify.listen({ port: 3000, host: '0.0.0.0' });
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

将Webhook数据存储在PostgreSQL中

持久化Webhook数据对于审计、调试和确保数据完整性至关重要。PostgreSQL是一个健壮的关系型数据库,非常适合此任务。

1. 数据库模式

首先,创建一个表来存储您的Webhook事件。此模式提供了一个很好的起点:

CREATE TABLE webhooks (
    id SERIAL PRIMARY KEY,
    event_id VARCHAR(255) UNIQUE NOT NULL, -- Didit的唯一事件ID
    event_type VARCHAR(255) NOT NULL,
    payload JSONB NOT NULL, -- 存储完整的JSON有效载荷
    received_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

2. 与Fastify集成

app.js示例所示,我们集成了pg客户端以连接到您的PostgreSQL数据库并插入传入的Webhook有效载荷。payload JSONB NOT NULL列非常适合存储整个JSON对象,允许以后进行灵活查询,而无需为Didit可能引入的每个新字段更改预定义模式。

请记住优雅地处理潜在的数据库错误。上面的示例包含一个用于数据库操作的基本try-catch块。

保护您的Webhook端点

在处理敏感身份验证数据时,安全性至关重要。Didit使用HMAC-SHA256签名来确保传入的Webhooks是合法的且未被篡改。

1. HMAC签名验证

每个Didit Webhook请求都包含两个关键的标头:X-SignatureX-TimestampX-Signature标头包含使用您的唯一Webhook密钥和与时间戳连接的原始请求正文生成的HMAC-SHA256签名。您的监听器必须:

  1. 检索您的DIDIT_WEBHOOK_SECRET
  2. 提取X-SignatureX-Timestamp标头。
  3. 重建签名有效载荷:`${timestamp}.${rawBody}`
  4. 使用您的密钥和重建的有效载荷生成您自己的HMAC-SHA256签名。
  5. 将您生成的签名与请求中的X-Signature进行比较。如果它们不匹配,则拒绝请求。

提供的Fastify示例演示了这一确切过程,确保只处理真实的Didit Webhooks。

2. 时间戳验证

X-Timestamp标头对于防止重放攻击至关重要。当攻击者拦截合法的Webhook并在稍后重新发送时,就会发生重放攻击。通过检查时间戳是否是最近的(例如,在当前时间的5分钟内),您可以减轻这种风险。Fastify代码片段包含一个检查以确保时间戳不过旧。

Didit如何提供帮助

Didit平台旨在使身份验证的集成无缝且安全。我们的模块化架构允许您即插即用各种身份检查,从用于强大文档分析的身份验证到用于高安全性电子护照/电子身份证检查的NFC验证,以及用于合规性的反洗钱筛选与监控。Webhook系统是这种模块化的核心组件,提供实时更新,使您能够协调复杂的KYC工作流程,而无需持续轮询。

Didit的AI原生方法意味着我们的系统不断学习和适应,提供卓越的准确性和欺诈检测。清晰的API文档和开发者优先的理念,结合免费核心KYC和无设置费用等功能,使得入门变得容易。我们的Webhooks提供结构化的身份数据,允许您的应用程序智能地响应验证结果,并在全球范围内大规模自动化信任。无论您是需要使用Didit的年龄估算验证年龄,还是通过被动与主动活体检测确保用户是活生生的人,Didit都提供了强大身份解决方案所需的工具和实时反馈。

准备好开始了吗?

准备好亲身体验Didit了吗?立即获取免费演示

使用Didit的免费套餐免费开始验证身份。

身份与欺诈基础设施。

一个 API 即可实现 KYC、KYB、交易监控和钱包筛选。5 分钟即可集成。

让 AI 总结此页面
使用Fastify & PostgreSQL构建Didit Webhook监听器.