Chaindoc Webhooks
Recevez des notifications en temps réel quand il se passe quelque chose sur votre compte Chaindoc. Les webhooks envoient tout de suite les données d'événement à votre serveur, donc pas besoin de faire de sondage.
Aperçu
Les webhooks Chaindoc permettent à ton application de recevoir des notifications en temps réel quand des événements se produisent sur ton compte. Au lieu d'interroger l'API, les webhooks envoient les données d'événement à ton serveur dès qu'une action se produit.
Caractéristiques principales
- Notifications en temps réel - Envoi instantané des événements à ton serveur
- Reprises automatiques - Jusqu'à 3 tentatives de reprise avec recul exponentiel
- Vérification de la signature - HMAC SHA256 pour l'authenticité de la charge utile
- Filtrage des événements - Ne reçois que les événements qui t'intéressent
- Suivi des erreurs - Surveille l'état de livraison et les échecs des webhooks.
Cas d'utilisation
- Envoie des notifications par e-mail quand les documents sont signés.
- Lancez des workflows quand les documents sont vérifiés sur la blockchain.
- Mettez à jour votre base de données lorsque des demandes de signature sont créées.
- Synchronisez le statut des documents avec les systèmes externes.
- Piste d'audit et journalisation de conformité
Configuration
Étape 1 : Créer une clé API
Va dans Paramètres → Accès API dans ton tableau de bord Chaindoc et crée une clé API avec la configuration webhook activée.
Étape 2 : Configurer l'URL du webhook
Utilise l'API pour configurer ton point de terminaison webhook :
curl -X PATCH https://api.chaindoc.io/user/api-access/1/config \
-H "Authorization: Bearer your_auth_token" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://yourapp.com/webhooks/chaindoc",
"webhookEnabled": true,
"webhookSecret": "your_secure_random_string"
}'Étape 3 : Mettre en place le point de terminaison Webhook
Crée un point de terminaison sur ton serveur pour recevoir les événements webhook. Voici des exemples dans différents langages :
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
app.post('/webhooks/chaindoc', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const eventType = req.headers['x-webhook-event'];
// Verify signature
if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process event
console.log('Received event:', eventType, req.body);
// Handle different event types
switch (eventType) {
case 'document.created':
handleDocumentCreated(req.body);
break;
case 'document.verified':
handleDocumentVerified(req.body);
break;
case 'signature.request.completed':
handleSignatureCompleted(req.body);
break;
}
// Always respond with 200 OK
res.status(200).send('Webhook received');
});
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(payload)).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
app.listen(3000);Types d'événements
Chaindoc envoie des webhooks pour les événements suivants :
document.created
Déclenché lorsqu'un nouveau document est créé via l'API.
{
"event": "document.created",
"documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
"name": "Service Agreement",
"timestamp": "2024-12-04T10:30:00.000Z"
}document.verified
Se déclenche quand un document est validé avec succès sur la blockchain.
{
"event": "document.verified",
"documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
"versionId": "f0b7721f-0399-4035-9b69-7b95d3a367f0",
"txHash": "0x789ghi...",
"chainId": 137,
"timestamp": "2024-12-04T10:35:00.000Z"
}document.signé
Se déclenche quand toutes les signatures requises sont recueillies.
{
"event": "document.signed",
"documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
"signatureRequestId": "req_21096b94498f4a2d9795e810edc2c9a9",
"signers": [
{
"email": "signer1@example.com",
"signedAt": "2024-12-04T10:30:00.000Z"
},
{
"email": "signer2@example.com",
"signedAt": "2024-12-04T10:32:00.000Z"
}
],
"timestamp": "2024-12-04T10:32:00.000Z"
}signature.request.created
Déclenché lorsqu'une nouvelle demande de signature est créée.
{
"event": "signature.request.created",
"signatureRequestId": "req_21096b94498f4a2d9795e810edc2c9a9",
"documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
"recipients": [
{"email": "signer1@example.com"},
{"email": "signer2@example.com"}
],
"deadline": "2024-12-31T23:59:59.000Z",
"timestamp": "2024-12-04T10:30:00.000Z"
}signature.request.completed
Se déclenche quand tous les signataires ont fini de signer.
{
"event": "signature.request.completed",
"signatureRequestId": "req_21096b94498f4a2d9795e810edc2c9a9",
"documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
"completedAt": "2024-12-04T10:32:00.000Z",
"timestamp": "2024-12-04T10:32:00.000Z"
}signature.request.rejected
Se déclenche quand un signataire refuse la demande de signature.
{
"event": "signature.request.rejected",
"signatureRequestId": "req_21096b94498f4a2d9795e810edc2c9a9",
"documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
"rejectedBy": "signer1@example.com",
"reason": "Terms not acceptable",
"timestamp": "2024-12-04T10:30:00.000Z"
}Sécurité
Vérification de la signature
Chaindoc signe toutes les charges utiles des webhooks avec HMAC SHA256. Vérifie toujours les signatures pour t'assurer de leur authenticité et éviter les attaques par rejeu.
Comment ça marche, la vérification de signature
1Chaindoc crée une signatureChaindoc crée une signature HMAC en utilisant votre secret webhook.
2Signature envoyée dans l'en-têteLa signature est envoyée dans l'en-tête X-Webhook-Signature.
3Votre serveur vérifieVotre serveur recalcule la signature et la compare à l'aide d'une fonction sécurisée.
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(payload)).digest('hex');
// Use timing-safe comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
// Usage
const isValid = verifyWebhookSignature(
req.body,
req.headers['x-webhook-signature'],
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}Logique de réessai
Chaindoc réessaie automatiquement les livraisons de webhooks qui ont échoué avec un délai exponentiel.
- 1ère tentative : après 1 minute
- Deuxième tentative : après 5 minutes (total : 6 minutes)
- 3e tentative : après 15 minutes (total : 21 minutes)
Tester les webhooks
Développement local
Utilise des outils comme ngrok pour exposer ton serveur local pour tester les webhooks :
# Install ngrok
npm install -g ngrok
# Start your local server
node server.js
# Expose port 3000
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
# Example: https://abc123.ngrok.io/webhooks/chaindocTests manuels
Teste ton point de terminaison webhook avec un exemple de charge utile :
curl -X POST https://yourapp.com/webhooks/chaindoc \
-H "Content-Type: application/json" \
-H "X-Webhook-Event: document.created" \
-H "X-Webhook-Signature: test_signature" \
-d '{
"event": "document.created",
"documentId": "test-123",
"name": "Test Document",
"timestamp": "2024-12-04T10:30:00.000Z"
}'Bonnes pratiques
- Toujours vérifier les signatures des webhooks avant de les traiter.
- Réponds vite (en moins de 30 secondes) pour éviter les délais d'attente.
- Traite les webhooks de manière asynchrone dans une file d'attente.
- Mets en place l'idempotence pour gérer les événements en double.
- Enregistre tous les événements webhook pour le débogage et l'audit.
- Surveillez les échecs de livraison des webhooks dans votre tableau de bord.
- Utilise des points de terminaison HTTPS pour la sécurité.
- Gère tous les types d'événements avec élégance (ignore les événements inconnus).
Exemple complet
Voici un gestionnaire de webhook prêt à l'emploi avec intégration de base de données :
import express from 'express';
import crypto from 'crypto';
import { PrismaClient } from '@prisma/client';
const app = express();
const prisma = new PrismaClient();
app.use(express.json());
app.post('/webhooks/chaindoc', async (req, res) => {
const signature = req.headers['x-webhook-signature'] as string;
const eventType = req.headers['x-webhook-event'] as string;
const payload = req.body;
// 1. Verify signature
if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
console.error('Invalid webhook signature');
return res.status(401).json({ error: 'Invalid signature' });
}
// 2. Check for duplicate events (idempotency)
const eventId = `${eventType}-${payload.timestamp}`;
const existing = await prisma.webhookEvent.findUnique({
where: { eventId },
});
if (existing) {
console.log('Duplicate event, skipping:', eventId);
return res.status(200).json({ status: 'duplicate' });
}
// 3. Store event
await prisma.webhookEvent.create({
data: {
eventId,
eventType,
payload,
processedAt: new Date(),
},
});
// 4. Process event asynchronously
processWebhookAsync(eventType, payload).catch((error) => {
console.error('Error processing webhook:', error);
});
// 5. Respond immediately
res.status(200).json({ status: 'received' });
});
async function processWebhookAsync(eventType: string, payload: any) {
switch (eventType) {
case 'document.verified':
await handleDocumentVerified(payload);
break;
case 'signature.request.completed':
await handleSignatureCompleted(payload);
await sendNotificationEmail(payload);
break;
case 'signature.request.rejected':
await handleSignatureRejected(payload);
break;
}
}
async function handleDocumentVerified(payload: any) {
await prisma.document.update({
where: { id: payload.documentId },
data: {
status: 'verified',
blockchainTxHash: payload.txHash,
verifiedAt: new Date(payload.timestamp),
},
});
}
async function handleSignatureCompleted(payload: any) {
await prisma.signatureRequest.update({
where: { id: payload.signatureRequestId },
data: {
status: 'completed',
completedAt: new Date(payload.completedAt),
},
});
}
function verifySignature(payload: any, signature: string, secret: string): boolean {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(payload)).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
app.listen(3000);