Chaindoc logoChaindoc

Chaindoc вебхуктары

Вебхуктар Chaindoc-та бір нәрсе болған сәтте деректерді серверіңізге жібереді. Сұраныс жоқ, кідіріс жоқ. Бұл бетте баптау, оқиға түрлері, HMAC растауы, қайталау логикасы және тестілеу көрсетілген.

Шолу

API-ны сұраудың орнына, вебхуктар не болғанын серверіңізге дереу айтады. Құжат күйін синхрондау, қолтаңбалар кейін жұмыс процестерін іске қосу, хабарландырулар жіберу және дерекқорыңызды синхрондау үшін қолданасыз.

  • 3 автоматты қайталаумен (экспоненциалды кідіріс) лезде жеткізу
  • Әр payload-та HMAC SHA256 қолтаңбасын растау
  • Тек қызықтыратын оқиға түрлеріне сүзгі
  • Басқару тақтасындағы жеткізу күйін бақылау

Орнату

1-қадам: API кілтін жасау

Chaindoc басқару тақтасында Параметрлер → API Access бөліміне өтіп, вебхук баптауы қосылған API кілтін жасаңыз.

2-қадам: Вебхук URL-ын баптау

Вебхук endpoint-іңізді баптау үшін API қолданыңыз:

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

3-қадам: Endpoint іске асыру

Вебхук оқиғаларын қабылдау үшін серверіңізде endpoint жасаңыз. Міне түрлі тілдердегі мысалдар:

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

Оқиға түрлері

Chaindoc келесі оқиғалар үшін вебхуктар жібереді:

document.created

Жаңа құжат API арқылы жасалғанда іске қосылады.

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

document.verified

Құжат blockchain-де сәтті расталғанда іске қосылады.

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

Барлық қажетті қолтаңбалар жиналғанда іске қосылады.

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

Жаңа қолтаңба сұранысы жасалғанда іске қосылады.

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

Барлық қол қоюшылар қолтаңбаларын аяқтағанда іске қосылады.

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

Қол қоюшы қолтаңба сұранысын бас тартқанда іске қосылады.

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

Қауіпсіздік

Қолтаңбаны растау

Chaindoc барлық вебхук payload-тарын HMAC SHA256 арқылы қол қояды. Аутентификацияны қамтамасыз ету және қайталау шабуылдарын болдырмау үшін әрдайым қолтаңбаларды растаңыз.

Растау қалай жұмыс істейді

1Chaindoc қолтаңба жасайдыChaindoc вебхук құпиясыңызды қолданып HMAC қолтаңбасын жасайды

2Қолтаңба header-де жіберіледіҚолтаңба X-Webhook-Signature header-інде жіберіледі

3Серверіңіз растайдыСерверіңіз қолтаңбаны қайта есептейді және уақыт-безопас функциямен салыстырады

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

Қайталау логикасы

Chaindoc сәтсіз вебхук жеткізілімдерін экспоненциалды кідіріспен автоматты түрде қайталайды.

  • 1-ші қайталау: 1 минуттан кейін
  • 2-ші қайталау: 5 минуттан кейін (барлығы: 6 минут)
  • 3-ші қайталау: 15 минуттан кейін (барлығы: 21 минут)

Вебхуктарды тестілеу

Жергілікті әзірлеу

ngrok сияқты құралдарды жергілікті серверіңізді вебхук тестілеуі үшін ашуға қолданыңыз:

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

Қолмен тестілеу

Вебхук endpoint-іңізді үлгі payload-пен тестілеңіз:

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

Үздік тәжірибелер

  • Өңдеуден бұрын әрдайым вебхук қолтаңбаларын растаңыз
  • Ұзаққа созылмау үшін тез жауап беріңіз (30 секундтан аз)
  • Вебхуктарды кезекте асинхронды түрде өңдеңіз
  • Қайталанған оқиғаларды өңдеу үшін идемпотенттілікті іске асырыңыз
  • Жөндеу және аудит үшін барлық вебхук оқиғаларын журналдаңыз
  • Басқару тақтасында вебхук жеткізілімінің сәтсіздігін бақылаңыз
  • Қауіпсіздік үшін HTTPS endpoint-терін қолданыңыз
  • Барлық оқиға түрлерін дұрыс өңдеңіз (белгісіз оқиғаларды елемеу)

Өндірістік мысал

Міне, дерекқор интеграциясы бар өндіріске дайын вебхук өңдеушісі:

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,
      verifiedAt: new Date(),
    },
  });
}

async function handleSignatureCompleted(payload: any) {
  await prisma.signatureRequest.update({
    where: { id: payload.signatureRequestId },
    data: {
      status: 'completed',
      completedAt: new Date(),
    },
  });
}

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

Келесі қадамдар