Chaindoc webhookid

Webhookid saadavad sündmuste andmed sinu serverile hetkega, kui midagi Chaindocis juhtub. Pole vaja küsitlusi ega viivitusi. See leht hõlmab seadistust, sündmuste tüüpe, HMAC verifitseerimist, uuesti proovimise loogikat ja testimist.

Ülevaade

Selle asemel, et API pidevalt küsitleda, annavad webhookid sinu serverile teada, mis juhtus kohe, kui see juhtub. Sa kasutad neid dokumentide oleku sünkroonimiseks, töövoogude käivitamiseks pärast allkirju, teavituste saatmiseks ja andmebaasi sünkroonis hoidmiseks.

  • Kohene kohaletoimetamine kuni 3 automaatse uuesti proovimisega (eksponentsiaalne taandareng)
  • HMAC SHA256 allkirja verifitseerimine igal koormusel
  • Filtreeri ainult need sündmuste tüübid, mis sind huvitavad
  • Kohaletoimetamise oleku jälgimine sinu armatuurlaual

Seadistus

Samm 1: Loo API võti

Navigeeri oma Chaindoc armatuurlaual Seaded → API Ligipääs ja loo API võti, millel on webhookide seadistamine lubatud.

Samm 2: Seadista webhooki URL

Kasuta API-t oma webhooki lõpp-punkti seadistamiseks:

terminal
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"
  }'

Samm 3: Rakenda oma lõpp-punkt

Loo lõpp-punkt oma serveris webhooki sündmuste vastuvõtmiseks. Siin on näited erinevates keeltes:

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);

Sündmuste tüübid

Chaindoc saadab webhook-e järgmiste sündmuste puhul:

document.created

Käivitub, kui uus dokument luuakse API kaudu.

terminal
{
  "event": "document.created",
  "documentId": "86840ee4-8bf2-4a91-a289-e99d8307ec25",
  "name": "Service Agreement",
  "timestamp": "2024-12-04T10:30:00.000Z"
}

document.verified

Käivitub, kui dokument on edukalt plokiahelas verifitseeritud.

terminal
{
  "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.signed

Käivitub, kui kõik nõutud allkirjad on kogutud.

terminal
{
  "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

Käivitub, kui uus allkirjapäring luuakse.

terminal
{
  "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

Käivitub, kui kõik allkirjastajad lõpetavad oma allkirjad.

terminal
{
  "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

Käivitub, kui allkirjastaja lükkab allkirjapäringu tagasi.

terminal
{
  "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"
}

Turvalisus

Allkirja verifitseerimine

Chaindoc allkirjastab kõik webhooki koormused HMAC SHA256 abil. Verifitseeri alati allkirjad autentsuse tagamiseks ja kordusrünnakute vältimiseks.

Kuidas verifitseerimine töötab

1Chaindoc loob allkirjaChaindoc loob HMAC allkirja kasutades sinu webhooki saladust

2Allkiri saadetakse päisesAllkiri saadetakse X-Webhook-Signature päises

3Sinu server verifitseeribSinu server arvutab allkirja uuesti ja võrdleb ajakindel funktsiooni abil

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');
}

Uuesti proovimise loogika

Chaindoc proovib automaatselt ebaõnnestunud webhooki kohaletoimetamisi uuesti eksponentsiaalse taandarenguga.

  • 1. kord: Pärast 1 minutit
  • 2. kord: Pärast 5 minutit (kokku: 6 minutit)
  • 3. kord: Pärast 15 minutit (kokku: 21 minutit)

Webhookide testimine

Lokaalne arendus

Kasuta tööriistu nagu ngrok, et avalikustada oma lokaalne server webhookide testimiseks:

terminal
# 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/chaindoc

Manuaalne testimine

Testi oma webhooki lõpp-punkti näidiskoormusega:

terminal
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"
  }'

Parimad tavad

  • Verifitseeri alati webhooki allkirjad enne töötlemist
  • Vasta kiiresti (alla 30 sekundi), et vältida aegumisi
  • Töötle webhook-e asünkroonselt järjekorras
  • Rakenda idempotentsus duplikaat-sündmuste käsitlemiseks
  • Logi kõik webhooki sündmused silumiseks ja auditeerimiseks
  • Jälgi webhooki kohaletoimetamise ebaõnnestumisi oma armatuurlaual
  • Kasuta turvalisuse jaoks HTTPS lõpp-punkte
  • Käitle kõiki sündmuste tüüpe viisakalt (ignoreeri tundmatuid sündmusi)

Tootmise näide

Siin on tootmisvalmis webhooki käitleja andmebaasi integratsiooniga:

webhooks/chaindoc.ts
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', txHash: payload.txHash },
  });
}

function verifySignature(payload: any, signature: string, secret: string) {
  const hmac = crypto.createHmac('sha256', secret);
  const digest = hmac.update(JSON.stringify(payload)).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}

Mida järgmiseks teha