你好,哈布尔!
今天,我们将使用 Flask 和 Node.js 在 Bitrix24 中构建一个自动处理交易的服务。该服务将:
-
接受 ONCRMDEALUPDATE Webhook。
-
验证请求的签名和结构。
-
提取。
deal_id
-
接收交易数据。
-
检查交易金额和其他条件。
-
更新交易阶段。
-
为经理创建任务。
-
防止递归和错误。
但首先......Bitrix 如何生成 webhook?当触发 ONCRMDEALUPDATE 事件时,Bitrix 会发送一个有效负载,其中所有交易数据(包括 )都在 FIELDS → ID 结构→数据内。deal_id
data['ID']
为了避免这种情况,我们总是使用 ,如果缺少所需的键,它会安全地返回 None,而不是抛出异常。这样我们就可以处理任何数据,并避免在数据结构由于某种原因发生变化或不完整时逻辑崩溃。.get()
用于处理 Webhook 的 Flask 服务器
让我们建立依赖关系:
pip install flask requests redis
服务器本身的代码:
from flask import Flask, request, jsonify
import hmac, hashlib, logging, requests
import redis
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
WEBHOOK_KEY = 'https://your.bitrix24.ru/rest/1/abc123xyz456789/'
SECRET = b'your_super_secret_key' # если используешь подпись
REDIS = redis.Redis(host='localhost', port=6379, db=0)
TTL_SECONDS = 15 # анти-петля TTL
@app.route('/webhook', methods=['POST'])
def webhook():
raw = request.data
data = request.json
sig = request.headers.get('X-Bitrix-Signature', '')
# Валидация подписи (если настроена)
if sig:
expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, expected):
logging.warning("Invalid signature")
return jsonify({'error': 'bad signature'}), 403
deal_id = data.get('data', {}).get('FIELDS', {}).get('ID')
if not deal_id:
logging.error("No deal ID in payload")
return jsonify({'error': 'no deal ID'}), 400
if REDIS.get(deal_id):
logging.info(f"Skip repeated deal ID: {deal_id}")
return jsonify({'status': 'skipped'}), 200
REDIS.set(deal_id, 1, ex=TTL_SECONDS)
try:
process_deal(deal_id)
except Exception as e:
logging.exception(f"Error during deal processing: {e}")
return jsonify({'error': 'internal'}), 500
return jsonify({'status': 'ok'})
通过 HMAC 验证签名以确保安全,并使用 .Redis 在这里防止同一笔交易在短时间内被重复处理,以避免循环。所有交易处理逻辑都包装在 .deal_id
.get()
try/except
交易处理的业务逻辑
现在我们来处理交易本身:
def process_deal(deal_id):
r = requests.post(WEBHOOK_KEY + 'crm.deal.get', json={'id': deal_id})
if r.status_code != 200:
logging.warning(f"Bitrix GET failed: {r.status_code}")
return
deal = r.json().get('result')
if not deal:
logging.warning(f"Deal {deal_id} not found in response")
return
amount = float(deal.get('OPPORTUNITY', 0))
manager = deal.get('ASSIGNED_BY_ID')
current_stage = deal.get('STAGE_ID')
logging.info(f"Deal #{deal_id}: amount={amount}, stage={current_stage}")
if amount > 100000:
update_stage(deal_id)
create_task(deal_id, manager, amount)
向 Bitrix24 API 发送请求以获取有关交易的数据。检查状态代码是否响应的正确性,如果未找到交易,请记录它。我们还将交易金额的值转换为 ,因为它可以为空或字符串。如果金额超过 100,000,则更新阶段并为经理创建一个任务。crm.deal.get
float
更改阶段并创建任务
更改交易阶段的代码:
def update_stage(deal_id):
res = requests.post(WEBHOOK_KEY + 'crm.deal.update', json={
'id': deal_id,
'fields': {
'STAGE_ID': 'NEW_STAGE_CODE'
}
})
if res.status_code != 200:
logging.warning(f"Failed to update stage for deal {deal_id}")
else:
logging.info(f"Stage updated for deal {deal_id}")
要更改事务的阶段,我们使用 Bitrix24 API。如果请求失败,则会记录下来。
用于创建任务的代码:
def create_task(deal_id, manager_id, amount):
res = requests.post(WEBHOOK_KEY + 'tasks.task.add', json={
'fields': {
'TITLE': f'Сделка на {amount}',
'DESCRIPTION': f'Сделка #{deal_id} превысила 100 000. Проверь.',
'RESPONSIBLE_ID': manager_id
}
})
if res.status_code != 200:
logging.error(f"Ошибка создания задачи по сделке {deal_id}")
else:
logging.info(f"Задача создана по сделке {deal_id}")
如果交易超过一定数量,则为经理创建一个任务。该请求包含任务的描述和负责经理的 ID。
Node.js 版本
让我们建立依赖关系:
npm install express body-parser axios redis
Node.js上的服务器代码:
const express = require('express');
const crypto = require('crypto');
const axios = require('axios');
const Redis = require('ioredis');
const app = express();
app.use(express.json());
const redis = new Redis();
const BITRIX_WEBHOOK = 'https://your.bitrix24.ru/rest/1/abc123xyz456789/';
const SECRET = 'your_super_secret_key';
function isValidSig(body, signature) {
const expected = crypto.createHmac('sha256', SECRET).update(JSON.stringify(body)).digest('hex');
return signature === expected;
}
app.post('/webhook', async (req, res) => {
const sig = req.headers['x-bitrix-signature'] || '';
const dealId = req.body?.data?.FIELDS?.ID;
if (sig && !isValidSig(req.body, sig)) {
return res.status(403).send('Invalid signature');
}
if (!dealId) return res.status(400).send('Missing deal ID');
const isDuplicate = await redis.get(dealId);
if (isDuplicate) return res.status(200).send('Skipped');
await redis.set(dealId, 1, 'EX', 15);
try {
const dealRes = await axios.post(BITRIX_WEBHOOK + 'crm.deal.get', { id: dealId });
const deal = dealRes.data.result;
const amount = parseFloat(deal.OPPORTUNITY);
const manager = deal.ASSIGNED_BY_ID;
if (amount > 100000) {
await axios.post(BITRIX_WEBHOOK + 'crm.deal.update', {
id: dealId,
fields: { STAGE_ID: 'NEW_STAGE_CODE' }
});
await axios.post(BITRIX_WEBHOOK + 'tasks.task.add', {
fields: {
TITLE: `Сделка на ${amount}`,
RESPONSIBLE_ID: manager,
DESCRIPTION: `Проверь сделку #${dealId}`
}
});
}
res.json({ status: 'ok' });
} catch (err) {
console.error('Ошибка обработки:', err.message);
res.status(500).send('Server error');
}
});
app.listen(3000, () => console.log('Слушаем порт 3000'));
该代码执行与 Flask 中相同的逻辑,但使用 Express。我们检查请求的签名,使用 Redis 来防止重新处理交易,并使用 Bitrix24 API 获取有关交易的信息,并在金额超过 100,000 时进行更新。
您对 Bitrix24 中的 webhook 和自动化有什么体验?在评论中分享。
这篇文章是为 Bitrix24 Integrator 在线课程的未来学生准备的。好消息是,作为本课程的一部分,学生将获得 Otus 职业中心的支持。 了解更多信息