Chaindoc SDK

Ecco la cosa: Chaindoc offre due SDK TypeScript pensati per esigenze diverse. Il Server SDK gestisce il backend (Node.js), mentre l'Embed SDK ti permette di integrare l'interfaccia di firma direttamente nella tua web app. Entrambi includono definizioni di tipo complete, zero dipendenze runtime e funzionano con qualsiasi framework.

SDK Disponibili

Per la configurazione npm e le impostazioni specifiche per framework (variabili d'ambiente, provider, ecc.), consulta la guida all'installazione.

  • Server SDK (@chaindoc_io/server-sdk) - Integrazione backend per Node.js 18+
  • Embed SDK (@chaindoc_io/embed-sdk) - Interfaccia di firma frontend per applicazioni web
  • Python SDK - In arrivo
  • PHP SDK - In arrivo

Server SDK

Il Server SDK avvolge la REST API in un'interfaccia Node.js type-safe. Lo usi per gestire documenti, creare richieste di firma, gestire upload di file e attivare la verifica blockchain.

Installazione

npm install @chaindoc_io/server-sdk

Avvio rapido

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

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

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

// 3. Crea un documento
const doc = await chaindoc.documents.create({
  name: 'Service Agreement',
  description: 'Contract for consulting services',
  media: media[0],
  status: 'published', // Attiva la verifica blockchain
  hashtags: ['#contract', '#2024'],
  meta: [{ key: 'client', value: 'Acme Corp' }],
});

// 4. Crea una richiesta di firma
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. Crea sessione per l'SDK frontend
const session = await chaindoc.embedded.createSession({
  email: 'signer@example.com',
  metadata: {
    documentId: doc.documentId,
    signatureRequestId: sigRequest.signatureRequest.uuid,
  },
});

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

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

// Carica e crea documento
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' });
    }
  }
});

// Crea sessione embedded per firmatario
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);

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

Gestione errori

Onestamente, dovresti sempre racchiudere le chiamate API in blocchi try-catch e gestire ChaindocError in modo specifico:

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 - verifica i parametri
        break;
      case 401:
        // Unauthorized - controlla la chiave API
        break;
      case 404:
        // Not found
        break;
      case 429:
        // Rate limited - l'SDK riproverà automaticamente
        break;
    }
  }
}

Embed SDK

L'Embed SDK ti permette di mostrare l'interfaccia di firma Chaindoc direttamente nella tua app. Gestisce l'iframe, la verifica OTP e la comunicazione tra la tua app e Chaindoc. I tuoi utenti firmano documenti senza mai uscire dal tuo sito.

Installazione

npm install @chaindoc_io/embed-sdk

Utilizzo base

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

// 1. Inizializza l'SDK (una volta per pagina)
const chaindoc = new ChaindocEmbed({
  publicKey: 'pk_live_xxxxxxxxxxxxx',
  environment: 'production',
});

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

// 3. Apri flusso di firma
const instance = chaindoc.openSignatureFlow({
  sessionId,
  
  onReady: () => {
    console.log('Interfaccia di firma caricata');
  },
  
  onSuccess: (data) => {
    console.log('Documento firmato:', data.signatureId);
    instance.close();
  },
  
  onError: (error) => {
    console.error('Firma fallita:', error.code, error.message);
  },
  
  onCancel: () => {
    console.log('Utente annullato');
    instance.close();
  },
});

Integrazione 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('Firmato!', data.signatureId);
        instanceRef.current?.close();
      },
      onCancel: () => {
        instanceRef.current?.close();
      },
    });
  }, [sessionId]);

  return <button onClick={handleSign}>Firma Documento</button>;
}

Integrazione 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('Firmato!', data.signatureId);
      instance?.close();
    },
    onCancel: () => {
      instance?.close();
    },
  });
}
</script>

<template>
  <button @click="openSignature">Firma Documento</button>
</template>

Modalità inline

In pratica, puoi incorporare l'interfaccia di firma direttamente nella pagina invece di usare un modal:

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

Temi

Personalizza l'aspetto con temi chiaro o scuro:

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

// Cambia tema dinamicamente
instance.changeTheme('light');

Esempio flusso completo

L'essenziale è questo: Server SDK sul backend per creare documenti e sessioni, Embed SDK sul frontend per mostrare l'interfaccia di firma.

1Backend: Carica DocumentoUsa Server SDK per caricare file e creare documento

2Backend: Crea Richiesta di FirmaCrea richiesta firma con flusso embedded abilitato

3Backend: Genera SessioneCrea sessione embedded per ogni firmatario

4Frontend: Inizializza Embed SDKInizializza SDK con chiave pubblica

5Frontend: Apri Flusso di FirmaApri interfaccia firma con session ID

6Frontend: Gestisci SuccessoProcessa documento firmato e aggiorna UI

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

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

// Carica e crea documento
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: [],
});

// Crea richiesta firma
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,
});

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

// Restituisci sessionId al frontend
res.json({ sessionId: session.sessionId });

Best practices

  • Inizializza l'SDK una sola volta per ciclo di vita pagina/componente
  • Distruggi sempre l'istanza SDK al smontaggio del componente
  • Gestisci tutti i callback (onSuccess, onError, onCancel)
  • Memorizza le chiavi API in variabili d'ambiente
  • Usa TypeScript per maggiore type safety
  • Implementa gestione errori appropriata con ChaindocError
  • Testa con chiavi sandbox prima del deploy in produzione

Configurazione ambiente

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

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

Cosa fare dopo