Chaindoc SDKs
Chaindoc cung cấp hai SDK TypeScript: Server SDK cho backend (Node.js) và Embed SDK để tích hợp giao diện ký vào web app. Cả hai đều có định nghĩa type đầy đủ, zero runtime dependencies, hoạt động với mọi framework.
Các SDK có sẵn
Xem hướng dẫn cài đặt để thiết lập npm và cấu hình framework cụ thể (biến môi trường, providers, v.v.).
- 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ý frontend cho ứng dụng web
- Python SDK - Sắp ra mắt
- PHP SDK - Sắp ra mắt
Server SDK
Server SDK wrap REST API trong interface Node.js type-safe. Bạn dùng nó để quản lý tài liệu, tạo yêu cầu ký, xử lý upload file, và kích hoạt xác minh blockchain.
Cài đặt
npm install @chaindoc_io/server-sdkBắt đầu nhanh
import { Chaindoc } from '@chaindoc_io/server-sdk';
import { readFile } from 'fs/promises';
// 1. Khởi tạo SDK
const chaindoc = new Chaindoc({
secretKey: process.env.CHAINDOC_SECRET_KEY!,
});
// 2. Upload file tài liệu
const buffer = await readFile('./contract.pdf');
const file = new Blob([buffer], { type: 'application/pdf' });
const { media } = await chaindoc.media.upload([file]);
// 3. Tạo tài liệu
const doc = await chaindoc.documents.create({
name: 'Hợp đồng dịch vụ',
description: 'Hợp đồng tư vấn',
media: media[0],
status: 'published', // Kích hoạt xác minh blockchain
hashtags: ['#contract', '#2024'],
meta: [{ key: 'client', value: 'Acme Corp' }],
});
// 4. Tạo yêu cầu ký
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. Tạo session cho 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
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 và tạo tài liệu
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' });
}
}
});
// Tạo session embedded cho người ký
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);Next.js API Routes
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 wrap các API call trong try-catch và xử lý ChaindocError cụ thể:
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 - kiểm tra tham số
break;
case 401:
// Unauthorized - kiểm tra API key
break;
case 404:
// Not found
break;
case 429:
// Rate limited - SDK tự động retry
break;
}
}
}Embed SDK
Embed SDK cho phép hiển thị giao diện ký Chaindoc trong ứng dụng web. Nó xử lý iframe, xác minh OTP, và giao tiếp giữa app và Chaindoc. Người dùng ký tài liệu mà không rời khỏi site.
Cài đặt
npm install @chaindoc_io/embed-sdkCách sử dụng cơ bản
import { ChaindocEmbed } from '@chaindoc_io/embed-sdk';
// 1. Khởi tạo SDK (một lần mỗi trang)
const chaindoc = new ChaindocEmbed({
publicKey: 'pk_live_xxxxxxxxxxxxx',
environment: 'production',
});
// 2. Lấy session từ backend
const response = await fetch('/api/signing/create-session', {
method: 'POST',
body: JSON.stringify({ documentId, signerEmail }),
});
const { sessionId } = await response.json();
// 3. Mở flow ký
const instance = chaindoc.openSignatureFlow({
sessionId,
onReady: () => {
console.log('Giao diện ký đã load');
},
onSuccess: (data) => {
console.log('Tài liệu đã ký:', data.signatureId);
instance.close();
},
onError: (error) => {
console.error('Ký thất bại:', error.code, error.message);
},
onCancel: () => {
console.log('Người dùng hủy');
instance.close();
},
});Tích hợp React
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('Đã ký!', data.signatureId);
instanceRef.current?.close();
},
onCancel: () => {
instanceRef.current?.close();
},
});
}, [sessionId]);
return <button onClick={handleSign}>Ký Tài Liệu</button>;
}Tích hợp Vue 3
<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('Đã ký!', data.signatureId);
instance?.close();
},
onCancel: () => {
instance?.close();
},
});
}
</script>
<template>
<button @click="openSignature">Ký Tài Liệu</button>
</template>Chế độ inline
Thay vì modal, nhúng giao diện ký trực tiếp vào trang:
const instance = chaindoc.openSignatureFlow({
sessionId,
mode: 'inline',
container: document.getElementById('signature-container'),
onSuccess: (data) => {
console.log('Đã ký!');
},
});Tùy chỉnh giao diện
Tùy chỉnh appearance với theme sáng hoặc tối:
const instance = chaindoc.openSignatureFlow({
sessionId,
theme: 'dark',
// ... các tùy chọn khác
});
// Thay đổi theme động
instance.changeTheme('light');Ví dụ workflow đầy đủ
Đây là cách kết hợp cả hai SDK: Server SDK trên backend để tạo tài liệu và sessions, Embed SDK trên frontend để hiển thị UI ký.
1Backend: Upload Tài LiệuDùng Server SDK để upload file và tạo tài liệu
2Backend: Tạo Yêu Cầu KýTạo yêu cầu ký với embedded flow được bật
3Backend: Tạo SessionTạo embedded session cho mỗi người ký
4Frontend: Khởi Tạo Embed SDKKhởi tạo SDK với public key
5Frontend: Mở Flow KýMở giao diện ký với session ID
6Frontend: Xử Lý Thành CôngXử lý tài liệu đã ký và cập nhật UI
// server.ts
import { Chaindoc } from '@chaindoc_io/server-sdk';
const chaindoc = new Chaindoc({
secretKey: process.env.CHAINDOC_SECRET_KEY!,
});
// Upload & tạo tài liệu
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: [],
});
// Tạo yêu cầu ký
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,
});
// Tạo session
const session = await chaindoc.embedded.createSession({
email: 'signer@example.com',
metadata: {
documentId: doc.documentId,
signatureRequestId: sigRequest.signatureRequest.uuid,
},
});
// Trả về sessionId cho frontend
res.json({ sessionId: session.sessionId });Best practices
- Khởi tạo SDK một lần mỗi page/component lifecycle
- Luôn destroy SDK instance khi component unmount
- Xử lý tất cả callback events (onSuccess, onError, onCancel)
- Lưu API keys trong environment variables
- Dùng TypeScript cho type safety tốt hơn
- Triển khai error handling đúng cách với ChaindocError
- Test với sandbox keys trước khi deploy production
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',
});Tiếp theo
- Cài đặt — Thiết lập npm, cấu hình env, và framework-specific providers
- Tài liệu API — Tài liệu tham khảo endpoint REST đầy đủ
- Webhooks — Thông báo sự kiện real-time cho backend
- Bắt đầu nhanh — Gửi chữ ký đầu tiên trong 10 phút
- Bảo mật — Quản lý API key và production hardening