Como receber notificações automáticas da ConnectVets Notes API
Evento | Quando ocorre |
---|---|
note.completed | Quando o processamento é concluído |
note.failed | Quando há erro no processamento |
note.processing | Quando o processamento inicia |
{
"event": "note.completed",
"timestamp": "2024-02-14T18:25:43Z",
"data": {
"note_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"external_id": "CLIENTE_123"
}
}
const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware para webhook (raw body)
app.use('/webhook', express.raw({ type: 'application/json' }));
// Sua chave secreta do webhook (fornecida pela equipe ConnectVets)
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature.replace('sha256=', ''), 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-connectvets-signature'];
const payload = req.body;
// Verificar assinatura
if (!verifySignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Signature verification failed');
}
const event = JSON.parse(payload);
// Processar evento
switch (event.event) {
case 'note.completed':
handleNoteCompleted(event.data);
break;
case 'note.failed':
handleNoteFailed(event.data);
break;
case 'note.processing':
handleNoteProcessing(event.data);
break;
}
res.status(200).send('OK');
});
async function handleNoteCompleted(data) {
console.log(`Nota ${data.note_id} processada com sucesso`);
// Buscar detalhes completos da nota
const noteDetails = await fetchNoteDetails(data.note_id);
// Atualizar seu sistema
await updateLocalDatabase(noteDetails);
// Notificar usuário
await notifyUser(data.external_id, 'completed');
}
async function handleNoteFailed(data) {
console.log(`Erro no processamento da nota ${data.note_id}`);
// Registrar erro
await logError(data);
// Notificar sobre o erro
await notifyUser(data.external_id, 'failed');
}
async function handleNoteProcessing(data) {
console.log(`Processamento iniciado para nota ${data.note_id}`);
// Atualizar status na interface
await updateStatus(data.note_id, 'processing');
}
app.listen(3000, () => {
console.log('Webhook listener rodando na porta 3000');
});
from flask import Flask, request, abort
import hashlib
import hmac
import json
import os
app = Flask(__name__)
WEBHOOK_SECRET = os.getenv('WEBHOOK_SECRET')
def verify_signature(payload, signature, secret):
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
received_signature = signature.replace('sha256=', '')
return hmac.compare_digest(expected_signature, received_signature)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-ConnectVets-Signature')
payload = request.get_data()
# Verificar assinatura
if not verify_signature(payload, signature, WEBHOOK_SECRET):
abort(401)
event = json.loads(payload)
# Processar evento
if event['event'] == 'note.completed':
handle_note_completed(event['data'])
elif event['event'] == 'note.failed':
handle_note_failed(event['data'])
elif event['event'] == 'note.processing':
handle_note_processing(event['data'])
return 'OK', 200
def handle_note_completed(data):
print(f"Nota {data['note_id']} processada com sucesso")
# Buscar detalhes da nota
note_details = fetch_note_details(data['note_id'])
# Atualizar banco de dados local
update_local_database(note_details)
# Enviar notificação
notify_user(data.get('external_id'), 'completed')
def handle_note_failed(data):
print(f"Erro no processamento da nota {data['note_id']}")
# Registrar erro
log_error(data)
# Notificar sobre erro
notify_user(data.get('external_id'), 'failed')
def handle_note_processing(data):
print(f"Processamento iniciado para nota {data['note_id']}")
# Atualizar status
update_status(data['note_id'], 'processing')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3000)
<?php
// webhook.php
$webhookSecret = $_ENV['WEBHOOK_SECRET'];
function verifySignature($payload, $signature, $secret) {
$expectedSignature = hash_hmac('sha256', $payload, $secret);
$receivedSignature = str_replace('sha256=', '', $signature);
return hash_equals($expectedSignature, $receivedSignature);
}
// Obter dados do webhook
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CONNECTVETS_SIGNATURE'] ?? '';
// Verificar assinatura
if (!verifySignature($payload, $signature, $webhookSecret)) {
http_response_code(401);
exit('Signature verification failed');
}
$event = json_decode($payload, true);
// Processar evento
switch ($event['event']) {
case 'note.completed':
handleNoteCompleted($event['data']);
break;
case 'note.failed':
handleNoteFailed($event['data']);
break;
case 'note.processing':
handleNoteProcessing($event['data']);
break;
}
http_response_code(200);
echo 'OK';
function handleNoteCompleted($data) {
error_log("Nota {$data['note_id']} processada com sucesso");
// Buscar detalhes da nota
$noteDetails = fetchNoteDetails($data['note_id']);
// Atualizar banco de dados
updateLocalDatabase($noteDetails);
// Enviar notificação
notifyUser($data['external_id'] ?? null, 'completed');
}
function handleNoteFailed($data) {
error_log("Erro no processamento da nota {$data['note_id']}");
// Registrar erro
logError($data);
// Notificar sobre erro
notifyUser($data['external_id'] ?? null, 'failed');
}
function handleNoteProcessing($data) {
error_log("Processamento iniciado para nota {$data['note_id']}");
// Atualizar status
updateStatus($data['note_id'], 'processing');
}
?>
https://seudominio.com/webhook
["note.completed", "note.failed", "note.processing"]
# Testar se seu endpoint está funcionando
curl -X POST "https://seudominio.com/webhook" \
-H "Content-Type: application/json" \
-H "X-ConnectVets-Signature: sha256=test" \
-d '{"event":"test","data":{}}'
// ❌ NUNCA faça isso
app.post('/webhook', (req, res) => {
const event = req.body;
processEvent(event); // Sem verificação!
});
// ✅ SEMPRE faça isso
app.post('/webhook', (req, res) => {
if (!verifySignature(req.body, req.headers['x-connectvets-signature'], SECRET)) {
return res.status(401).send('Unauthorized');
}
processEvent(req.body);
});
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
const event = JSON.parse(req.body);
// Evitar processar o mesmo evento duas vezes
if (processedEvents.has(event.id)) {
return res.status(200).send('Already processed');
}
processEvent(event);
processedEvents.add(event.id);
res.status(200).send('OK');
});
app.post('/webhook', (req, res) => {
// Responder imediatamente
res.status(200).send('OK');
// Processar assincronamente
setImmediate(() => {
processEvent(JSON.parse(req.body));
});
});
# Instalar ngrok
npm install -g ngrok
# Expor porta local
ngrok http 3000
# Usar URL do ngrok no webhook
# https://abc123.ngrok.io/webhook
app.post('/webhook', (req, res) => {
console.log('Headers:', req.headers);
console.log('Body:', req.body.toString());
// ... resto do código
});