Chaindoc SDKs

SDK chính thức cho tích hợp Chaindoc mượt mà. Hỗ trợ đầy đủ TypeScript, không phụ thuộc vào thư viện nào và thiết kế không phụ thuộc vào khung công tác cho cả ứng dụng backend và frontend.

Các SDK có sẵn

  • Server SDK (@chaindoc_io/server-sdk) - Tích hợp backend cho Node.js 18+
  • Embed SDK (@chaindoc_io/embed-sdk) - Giao diện ký kết phía client cho ứng dụng web
  • Python SDK - Sắp ra mắt
  • PHP SDK - Sắp ra mắt

SDK máy chủ

Server SDK cung cấp một giao diện Node.js an toàn về kiểu dữ liệu cho API Chaindoc. Sử dụng nó để quản lý tài liệu, tạo yêu cầu ký tên, xử lý tải lên tệp và tích hợp xác minh blockchain vào hệ thống backend của cậu.

Cài đặt

npm install @chaindoc_io/server-sdk

Bắt đầu nhanh

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

Tích hợp 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);

Các đường dẫn API của 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 }
    );
  }
}

Xử lý lỗi

Luôn bao quanh các cuộc gọi API trong khối try-catch và xử lý lỗi ChaindocError cụ thể:

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

Nhúng SDK

SDK nhúng cho phép tích hợp mượt mà chức năng ký tài liệu vào ứng dụng web. Nó quản lý quy trình ký dựa trên iframe với giao tiếp an toàn giữa ứng dụng của cậu và Chaindoc.

Cài đặt

npm install @chaindoc_io/embed-sdk

Cách sử dụng cơ bản

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();
  },
});

Tích hợp 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>;
}

Tích hợp 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>

Chế độ Inline

Thay vì sử dụng cửa sổ pop-up, hãy nhúng giao diện ký tên trực tiếp vào trang web của cậu:

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

Thiết kế giao diện

Tùy chỉnh giao diện với chủ đề sáng hoặc tối:

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

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

Ví dụ về quy trình làm việc hoàn chỉnh

Dưới đây là một ví dụ hoàn chỉnh từ đầu đến cuối kết hợp Server SDK và Embed SDK:

1Backend: Tải lên tài liệuSử dụng Server SDK để tải lên tệp và tạo tài liệu

2Backend: Tạo yêu cầu chữ kýTạo yêu cầu chữ ký với tính năng nhúng được kích hoạt

3Backend: Tạo phiên làm việcTạo phiên làm việc nhúng cho từng người ký

4Frontend: Khởi tạo SDK nhúngKhởi tạo SDK bằng khóa công khai

5Frontend: Mở quy trình ký kếtMở giao diện ký tên với ID phiên làm việc

6Frontend: Xử lý thành côngXử lý tài liệu đã ký và cập nhật giao diện người dùng

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

Thực hành tốt nhất

  • Khởi tạo SDK một lần cho mỗi vòng đời trang/thành phần
  • Luôn xóa đối tượng SDK khi thành phần được gỡ bỏ.
  • Xử lý tất cả các sự kiện callback (onSuccess, onError, onCancel)
  • Lưu trữ các khóa API trong biến môi trường
  • Sử dụng TypeScript để đảm bảo an toàn kiểu dữ liệu tốt hơn
  • Thực hiện xử lý lỗi đúng cách với ChaindocError
  • Thử nghiệm với các khóa sandbox trước khi triển khai vào môi trường sản xuất

Cấu hình môi trường

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

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