开发者中心-事件订阅 接入指南

概述

开发者中心为企业提供 Webhook 订阅能力,当 CRM 内发生业务事件时,系统会主动推送事件通知到企业配置的回调地址。

快速开始

  1. 开通功能:联系管理员在后台「设置 - 开发者中心」开通该功能
  2. 配置回调地址:填写接收事件的 HTTPS 回调地址
  3. 获取签名密钥:系统生成唯一的 subscribe_secret,用于验签
  4. 选择订阅事件:勾选需要接收的事件类型
  5. 验证推送:点击「测试推送」,确认能收到 test.ping 事件并验签通过
  6. 处理事件:在回调接口实现验签、业务处理逻辑

每个事件的 event_type 格式为 {entity}.{action},例如 customer.updateopportunity.won

推送格式

请求结构

所有事件推送均为 POST 请求,Content-Type 为 application/json

POST {callback_url}
Content-Type: application/json
X-LXY-Signature: {HMAC-SHA256 签名}
X-LXY-Timestamp: {Unix 时间戳,秒}
X-LXY-Nonce: {随机字符串}

{JSON payload}

Payload 结构

{
  "event_id": "evt_a1b2c3d4e5f6",
  "event_type": "customer.update",
  "occurred_at": "2026-09-01T14:30:00+08:00",
  "organization_id": 123,
  "operator": {
    "id": 456,
    "name": "张三"
  },
  "entity": {
    "type": "Customer",
    "id": 789
  },
  "data": {
    "changes": {
      "name": ["旧公司名", "新公司名"]
    }
  }
}

字段说明

  • event_id:全局唯一事件标识,格式 evt_{16位hex}
  • event_type:事件类型,格式 {entity}.{action}
  • occurred_at:事件发生时间(ISO 8601 格式)
  • organization_id:企业 ID
  • operator:操作人信息(后台任务触发时为 null
  • entity:实体类型和 ID,不含完整数据(需通过 API 查询详情)
  • data:事件专属业务数据(见下方示例)

常见 Payload 示例

客户创建

{
  "event_type": "customer.create",
  "entity": { "type": "Customer", "id": 300 },
  "data": {}
}

客户更新(含自定义字段)

{
  "event_type": "customer.update",
  "entity": { "type": "Customer", "id": 300 },
  "data": {
    "changes": {
      "name": ["旧公司名", "新公司名"],
      "note": ["11", "22"],
      "text_asset_dec3c0": ["a", "b"]
    }
  }
}

说明

  • changes 包含所有变更字段(本表字段 + 自定义字段)
  • 自定义字段推送原始值(ID、原始数据),不含显示值
  • 用户/部门字段格式为 ID 数组:[[旧ID数组], [新ID数组]]
  • 日期字段格式为 ISO 8601 字符串

客户转移

{
  "event_type": "customer.transfer",
  "entity": { "type": "Customer", "id": 300 },
  "data": {
    "from_user": { "id": 201, "name": "王五" },
    "to_user": { "id": 202, "name": "赵六" }
  }
}

客户标签更新

{
  "event_type": "customer.label_update",
  "entity": { "type": "Customer", "id": 300 },
  "data": {
    "changes": {
      "labels": [
        [{"id": 1, "content": "重点客户"}, {"id": 2, "content": "VIP"}], //旧值
        [{"id": 2, "content": "VIP"}, {"id": 3, "content": "潜在客户"}]  //新值
      ]
    }
  }
}

商机赢单

{
  "event_type": "opportunity.won",
  "entity": { "type": "Opportunity", "id": 400 },
  "data": {
    "from_stage": { "id": "12", "name": "商务谈判" },
    "to_stage": { "id": "18", "name": "赢单" }
  }
}

签到创建

{
  "event_type": "checkin.create",
  "entity": { "type": "Checkin", "id": 500 },
  "data": {}
}

签名验证

签名算法

系统使用 HMAC-SHA256 对推送内容签名:

signature = HMAC-SHA256(subscribe_secret, "{timestamp}.{nonce}.{body}")
  • subscribe_secret:签名密钥
  • timestamp:请求头 X-LXY-Timestamp 的值(Unix 时间戳秒)
  • nonce:请求头 X-LXY-Nonce 的值(随机字符串)
  • body:原始请求 body(JSON 字符串)

验证步骤

  1. 从请求头取 X-LXY-SignatureX-LXY-TimestampX-LXY-Nonce
  2. 读取原始 body 字符串(不要先解析 JSON 再序列化
  3. subscribe_secret"{timestamp}.{nonce}.{body}" 计算 HMAC-SHA256
  4. 比对计算结果与请求头的签名值)
  5. 可选:校验 timestamp 在 5 分钟内,防止重放攻击

示例代码

Python

import hmac
import hashlib
import time

def verify_webhook_signature(subscribe_secret, timestamp, nonce, body, signature):
    expected = hmac.new(
        subscribe_secret.encode('utf-8'),
        f"{timestamp}.{nonce}.{body}".encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# Flask
from flask import Flask, request, jsonify
import os

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook_callback():
    body = request.get_data(as_text=True)
    signature = request.headers.get('X-LXY-Signature')
    timestamp = request.headers.get('X-LXY-Timestamp')
    nonce = request.headers.get('X-LXY-Nonce')

    if not verify_webhook_signature(os.environ['SUBSCRIBE_SECRET'], timestamp, nonce, body, signature):
        return jsonify({'error': 'Invalid signature'}), 401

    # 可选:防重放
    if abs(time.time() - int(timestamp)) > 300:
        return jsonify({'error': 'Request too old'}), 400

    payload = request.get_json()
    # 处理事件...
    return jsonify({'success': True}), 200

Node.js

const crypto = require('crypto');

function verifyWebhookSignature(subscribeSecret, timestamp, nonce, body, signature) {
  const expected = crypto
    .createHmac('sha256', subscribeSecret)
    .update(`${timestamp}.${nonce}.${body}`)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Express
const express = require('express');
const app = express();

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString('utf8');
  const signature = req.headers['x-lxy-signature'];
  const timestamp = req.headers['x-lxy-timestamp'];
  const nonce = req.headers['x-lxy-nonce'];

  if (!verifyWebhookSignature(process.env.SUBSCRIBE_SECRET, timestamp, nonce, body, signature)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // 可选:防重放
  if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
    return res.status(400).json({ error: 'Request too old' });
  }

  const payload = JSON.parse(body);
  // 处理事件...
  res.json({ success: true });
});

响应要求

成功响应

返回 HTTP 状态码 2xx(200/201/204 均可),响应 body 不限:

HTTP/1.1 200 OK
Content-Type: application/json

{"success": true}

失败处理

  • 返回 非 2xx 状态码连接超时(2 秒)视为失败
  • 推送不会自动重试,失败后事件日志状态标记为 failed
  • 若需重新推送,需自行实现补偿机制

最佳实践

  1. 快速响应:尽快返回 200,将耗时业务逻辑放入队列异步处理
  2. 记录原始 payload:即使解析失败也能事后排查
  3. 幂等处理:同一 event_id 可能推送多次(网络重试),需保证幂等
  4. 监控告警:统计失败率和处理延迟
  5. 定期对账:关键业务场景建议定时全量对账

测试推送

后台「开发者中心」提供「测试推送」按钮,发送 test.ping 事件:

{
  "event_id": "evt_test1234567890ab",
  "event_type": "test.ping",
  "occurred_at": "2026-09-01T10:00:00+08:00",
  "organization_id": 123,
  "operator": null,
  "entity": { "type": "Test", "id": 0 },
  "data": { "message": "This is a test event" }
}

常见问题

1. 如何获取实体完整数据?

Webhook 推送只包含实体的 typeid,不包含完整数据。需要通过 CRM API 根据 ID 查询详情。

2. 自定义字段的值是什么格式?

  • 选择字段:选项 ID(字符串)
  • 多选字段:逗号分隔的选项 ID 字符串,如 "mul_xx1,mul_xx2,mul_xx3"
  • 用户/部门字段:ID 数组,如 [1, 2, 3]
  • 日期字段:ISO 8601 格式字符串,如 "2026-09-01"
  • 日期时间字段:ISO 8601 格式字符串,如 "2026-09-01T14:30:00+08:00"
  • 文本/数值字段:原始值

3. 为什么收不到某些事件?

检查以下几点:

  1. 确认企业已开通开发者中心功能
  2. 确认已勾选该事件类型
  3. 确认未超出配额限制
  4. 检查回调地址是否可访问(HTTPS,2秒内响应)
文档更新时间: 2026-09-04 16:09   作者:曹勇