Chaindoc SDK

Офіційні SDK для безперебійної інтеграції Chaindoc. Повна підтримка TypeScript, відсутність залежностей та незалежний від фреймворку дизайн як для бекенд-, так і для фронтенд-додатків.

Доступні SDK

  • Server SDK (@chaindoc_io/server-sdk) — інтеграція бекенду для Node.js 18+
  • Embed SDK (@chaindoc_io/embed-sdk) — інтерфейс для підписання веб-додатків
  • Python SDK — незабаром
  • PHP SDK - Скоро буде доступний

Серверний SDK

Server SDK надає безпечний для типів інтерфейс Node.js для Chaindoc API. Використовуйте його для управління документами, створення запитів на підпис, обробки завантажених файлів та інтеграції перевірки блокчейну у ваш бекенд.

Встановлення

npm install @chaindoc_io/server-sdk

Швидкий старт

server.ts
import { Chaindoc } from '@chaindoc_io/server-sdk';
import { readFile } from 'fs/promises';

// 1. Initialize the SDK
const chaindoc = new Chaindoc({
  secretKey: process.env.CHAINDOC_SECRET_KEY!,
});

// 2. Upload a document file
const buffer = await readFile('./contract.pdf');
const file = new Blob([buffer], { type: 'application/pdf' });
const { media } = await chaindoc.media.upload([file]);

// 3. Create a document
const doc = await chaindoc.documents.create({
  name: 'Service Agreement',
  description: 'Contract for consulting services',
  media: media[0],
  status: 'published', // Triggers blockchain verification
  hashtags: ['#contract', '#2024'],
  meta: [{ key: 'client', value: 'Acme Corp' }],
});

// 4. Create a signature request
const sigRequest = await chaindoc.signatures.createRequest({
  versionId: doc.document.versions[0].uuid,
  recipients: [{ email: 'signer@example.com' }],
  deadline: new Date('2024-12-31'),
  embeddedFlow: true,
});

// 5. Create session for frontend SDK
const session = await chaindoc.embedded.createSession({
  email: 'signer@example.com',
  metadata: {
    documentId: doc.documentId,
    signatureRequestId: sigRequest.signatureRequest.uuid,
  },
});

console.log('Session ID:', session.sessionId);

Інтеграція Express.js

server.ts
import express from 'express';
import { Chaindoc, ChaindocError } from '@chaindoc_io/server-sdk';
import multer from 'multer';

const app = express();
const upload = multer({ storage: multer.memoryStorage() });

const chaindoc = new Chaindoc({
  secretKey: process.env.CHAINDOC_SECRET_KEY!,
});

// Upload and create document
app.post('/api/documents', upload.single('file'), async (req, res) => {
  try {
    const file = new Blob([req.file!.buffer], { type: req.file!.mimetype });
    const { media } = await chaindoc.media.upload([file]);

    const doc = await chaindoc.documents.create({
      name: req.body.name,
      description: req.body.description || '',
      media: media[0],
      status: 'published',
      hashtags: req.body.hashtags || [],
      meta: req.body.meta || [],
    });

    res.json({ documentId: doc.documentId });
  } catch (error) {
    if (error instanceof ChaindocError) {
      res.status(error.statusCode || 500).json({ error: error.message });
    } else {
      res.status(500).json({ error: 'Internal server error' });
    }
  }
});

// Create embedded session for signer
app.post('/api/signing/session', async (req, res) => {
  try {
    const { email, documentId, signatureRequestId } = req.body;

    const session = await chaindoc.embedded.createSession({
      email,
      metadata: { documentId, signatureRequestId },
    });

    res.json({ sessionId: session.sessionId });
  } catch (error) {
    if (error instanceof ChaindocError) {
      res.status(error.statusCode || 500).json({ error: error.message });
    }
  }
});

app.listen(3000);

Маршрути API Next.js

app/api/signing/create-session/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Chaindoc, ChaindocError } from '@chaindoc_io/server-sdk';

const chaindoc = new Chaindoc({
  secretKey: process.env.CHAINDOC_SECRET_KEY!,
});

export async function POST(request: NextRequest) {
  try {
    const { email, documentId, signatureRequestId } = await request.json();

    const session = await chaindoc.embedded.createSession({
      email,
      metadata: { documentId, signatureRequestId },
    });

    return NextResponse.json({
      sessionId: session.sessionId,
      expiresAt: session.expiresAt,
    });
  } catch (error) {
    if (error instanceof ChaindocError) {
      return NextResponse.json(
        { error: error.message },
        { status: error.statusCode || 500 }
      );
    }
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}

Обробка помилок

Завжди обертайте виклики API в блоки try-catch і обробляйте ChaindocError окремо:

terminal
import { ChaindocError } from '@chaindoc_io/server-sdk';

try {
  const doc = await chaindoc.documents.create({ /* ... */ });
} catch (error) {
  if (error instanceof ChaindocError) {
    console.error('API Error:', error.message);
    console.error('Status Code:', error.statusCode);
    
    switch (error.statusCode) {
      case 400:
        // Bad request - check parameters
        break;
      case 401:
        // Unauthorized - check API key
        break;
      case 404:
        // Not found
        break;
      case 429:
        // Rate limited - SDK will auto-retry
        break;
    }
  }
}

Вбудувати SDK

Embed SDK дозволяє безперешкодно інтегрувати функцію підписання документів у веб-додатки. Він управляє процесом підписання на основі iframe із захищеним зв'язком між вашим додатком і Chaindoc.

Встановлення

npm install @chaindoc_io/embed-sdk

Основне використання

terminal
import { ChaindocEmbed } from '@chaindoc_io/embed-sdk';

// 1. Initialize SDK (once per page)
const chaindoc = new ChaindocEmbed({
  publicKey: 'pk_live_xxxxxxxxxxxxx',
  environment: 'production',
});

// 2. Get session from your backend
const response = await fetch('/api/signing/create-session', {
  method: 'POST',
  body: JSON.stringify({ documentId, signerEmail }),
});
const { sessionId } = await response.json();

// 3. Open signing flow
const instance = chaindoc.openSignatureFlow({
  sessionId,
  
  onReady: () => {
    console.log('Signing interface loaded');
  },
  
  onSuccess: (data) => {
    console.log('Document signed:', data.signatureId);
    instance.close();
  },
  
  onError: (error) => {
    console.error('Signing failed:', error.code, error.message);
  },
  
  onCancel: () => {
    console.log('User cancelled');
    instance.close();
  },
});

Інтеграція React

components/SignButton.tsx
import { useCallback, useRef, useEffect } from 'react';
import { ChaindocEmbed, EmbedInstance } from '@chaindoc_io/embed-sdk';

function SignButton({ sessionId }: { sessionId: string }) {
  const sdkRef = useRef<ChaindocEmbed | null>(null);
  const instanceRef = useRef<EmbedInstance | null>(null);

  useEffect(() => {
    sdkRef.current = new ChaindocEmbed({
      publicKey: process.env.REACT_APP_CHAINDOC_PUBLIC_KEY!,
    });

    return () => {
      sdkRef.current?.destroy();
    };
  }, []);

  const handleSign = useCallback(() => {
    if (!sdkRef.current) return;

    instanceRef.current = sdkRef.current.openSignatureFlow({
      sessionId,
      onSuccess: (data) => {
        console.log('Signed!', data.signatureId);
        instanceRef.current?.close();
      },
      onCancel: () => {
        instanceRef.current?.close();
      },
    });
  }, [sessionId]);

  return <button onClick={handleSign}>Sign Document</button>;
}

Інтеграція Vue 3

SignButton.vue
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { ChaindocEmbed, type EmbedInstance } from '@chaindoc_io/embed-sdk';

const props = defineProps<{ sessionId: string }>();

let sdk: ChaindocEmbed | null = null;
let instance: EmbedInstance | null = null;

onMounted(() => {
  sdk = new ChaindocEmbed({
    publicKey: import.meta.env.VITE_CHAINDOC_PUBLIC_KEY,
  });
});

onUnmounted(() => {
  sdk?.destroy();
});

function openSignature() {
  if (!sdk) return;
  
  instance = sdk.openSignatureFlow({
    sessionId: props.sessionId,
    onSuccess: (data) => {
      console.log('Signed!', data.signatureId);
      instance?.close();
    },
    onCancel: () => {
      instance?.close();
    },
  });
}
</script>

<template>
  <button @click="openSignature">Sign Document</button>
</template>

Вбудований режим

Замість модального вікна вбудуйте інтерфейс підписання безпосередньо у вашу сторінку:

terminal
const instance = chaindoc.openSignatureFlow({
  sessionId,
  mode: 'inline',
  container: document.getElementById('signature-container'),
  
  onSuccess: (data) => {
    console.log('Signed!');
  },
});

Тематика

Налаштуйте зовнішній вигляд за допомогою світлих або темних тем:

terminal
const instance = chaindoc.openSignatureFlow({
  sessionId,
  theme: 'dark',
  // ... other options
});

// Change theme dynamically
instance.changeTheme('light');

Повний приклад робочого процесу

Ось повний приклад, що поєднує Server SDK та Embed SDK:

1Бекенд: завантажити документВикористовуйте Server SDK для завантаження файлу та створення документа

2Бекенд: Створити запит на підписСтворіть запит на підпис із вбудованим потоком

3Бекенд: Створити сесіюСтворіть вбудовану сесію для кожного підписанта

4Фронтенд: ініціалізація Embed SDKІніціалізуйте SDK за допомогою відкритого ключа

5Фронтенд: відкритий процес підписанняВідкрийте інтерфейс підписання з ідентифікатором сеансу

6Фронтенд: обробка успішних операційОбробіть підписаний документ та оновлюйте інтерфейс користувача

// server.ts
import { Chaindoc } from '@chaindoc_io/server-sdk';

const chaindoc = new Chaindoc({
  secretKey: process.env.CHAINDOC_SECRET_KEY!,
});

// Upload & create document
const { media } = await chaindoc.media.upload([pdfFile]);
const doc = await chaindoc.documents.create({
  name: 'Contract',
  description: 'Service agreement',
  media: media[0],
  status: 'published',
  hashtags: ['#contract'],
  meta: [],
});

// Create signature request
const sigRequest = await chaindoc.signatures.createRequest({
  versionId: doc.document.versions[0].uuid,
  recipients: [{ email: 'signer@example.com' }],
  deadline: new Date('2024-12-31'),
  embeddedFlow: true,
});

// Create session
const session = await chaindoc.embedded.createSession({
  email: 'signer@example.com',
  metadata: {
    documentId: doc.documentId,
    signatureRequestId: sigRequest.signatureRequest.uuid,
  },
});

// Return sessionId to frontend
res.json({ sessionId: session.sessionId });

Кращі практики

  • Ініціалізуйте SDK один раз за цикл життя сторінки/компонента
  • Завжди знищуйте екземпляр SDK при від'єднанні компонента
  • Обробляйте всі події зворотного виклику (onSuccess, onError, onCancel)
  • Зберігайте API-ключі в змінних середовища
  • Використовуйте TypeScript для кращої безпеки типів
  • Впровадьте належне оброблення помилок за допомогою ChaindocError
  • Перед впровадженням у виробництво протестуйте за допомогою ключів пісочниці

Налаштування середовища

// Backend
const chaindoc = new Chaindoc({
  secretKey: 'sk_live_xxxxx',
});

// Frontend
const embed = new ChaindocEmbed({
  publicKey: 'pk_live_xxxxx',
  environment: 'production',
});