npm package: @hibrit/connect
Description: SDK oficial para integração de produtos ao ecossistema Hibrit Hub
Author: Hibrit
Latest version: 0.4.0

# @hibrit/connect

SDK oficial para integração de produtos ao ecossistema Hibrit Hub.

## Instalação

```bash
npm install @hibrit/connect
# ou
pnpm add @hibrit/connect
# ou
yarn add @hibrit/connect
```

## Configuração

Você precisa das credenciais fornecidas pelo Hibrit Hub:

- `apiKey`: Chave de API do seu produto (formato: `hbt_...`)
- `apiSecret`: Secret para assinatura de webhooks (formato: `whsec_...`)
- `productSlug`: Slug do seu produto (ex: `taktics`, `leadfloww`)

```typescript
import { HibritConnect } from '@hibrit/connect';

const hub = new HibritConnect({
  apiKey: process.env.HUB_API_KEY,
  apiSecret: process.env.HUB_WEBHOOK_SECRET,
  productSlug: 'taktics',
  baseUrl: 'https://connect.hibrit.com.br', // opcional (default)
});
```

## Sincronização de Organizações

Sincronize organizações/clientes do seu produto com o Hub:

```typescript
const result = await hub.syncOrganization({
  externalId: 'org-123',        // ID único no seu sistema
  name: 'Empresa ABC',          // Nome da organização
  slug: 'empresa-abc',          // Slug único (opcional)
  email: 'contato@empresa.com', // Email de contato (opcional)
  status: 'active',             // active | inactive | trial | suspended
  metadata: {                   // Dados extras (opcional)
    plano: 'premium',
    origem: 'indicacao',
  },
});

if (result.success) {
  console.log('Organização sincronizada:', result.data);
  // { mappingId: '...', hubOrgId: '...', action: 'created' | 'updated' }
} else {
  console.error('Erro:', result.error);
}
```

## Sincronização em Lote

```typescript
const results = await hub.syncOrganizations([
  { externalId: 'org-1', name: 'Empresa A', status: 'active' },
  { externalId: 'org-2', name: 'Empresa B', status: 'active' },
]);

console.log(`Sucesso: ${results.success}, Falhas: ${results.failed}`);
```

## Buscar Organizações Mapeadas

Recupere todas as organizações do seu produto que estão mapeadas no Hub:

```typescript
const result = await hub.getOrganizations({
  limit: 100,           // Máx: 500
  offset: 0,            // Para paginação
  includeHubOrg: true,  // Inclui dados da org no Hub
  status: 'synced',     // Filtrar por status de sync (opcional)
});

if (result.success) {
  console.log('Organizações:', result.data);
  console.log('Total:', result.pagination?.total);
  console.log('Mais páginas?', result.pagination?.hasMore);

  // Cada org contém:
  // - external_id: ID no seu sistema
  // - hub_org_id: ID no Hub
  // - sync_status: 'synced' | 'pending' | 'error' | 'conflict'
  // - hubOrg: { id, name, slug, type, status, email } (se includeHubOrg=true)
}
```

## Sincronização de Contratos

**Novo na v0.4.0!** Sincronize contratos vinculados a organizações:

```typescript
const result = await hub.syncContract({
  externalId: 'contrato-123',     // ID único no seu sistema
  organizationId: hubOrgId,       // ID da organização no Hub (hub_org_id)
  name: 'Contrato Premium',       // Nome do contrato
  number: 'CPS-2408',             // Número/código do contrato (opcional)
  status: 'active',               // draft | active | paused | completed | cancelled
  startDate: '2024-01-01',        // Data de início (opcional)
  endDate: '2025-01-01',          // Data de término (opcional)
  value: 50000,                   // Valor do contrato (opcional)
  currency: 'BRL',                // Moeda (opcional, default: BRL)
  billingCycle: 'monthly',        // monthly | quarterly | yearly | one-time (opcional)
  metadata: {                     // Dados extras (opcional)
    sigla: 'AGR',
    tipo: 'consultoria',
  },
});

if (result.success) {
  console.log('Contrato sincronizado:', result.data);
  // { contractId: '...', action: 'created' | 'updated' }
} else {
  console.error('Erro:', result.error);
}
```

### Sincronização em Lote de Contratos

```typescript
const results = await hub.syncContracts([
  {
    externalId: 'contrato-1',
    organizationId: hubOrgId,
    name: 'Contrato A',
    status: 'active',
  },
  {
    externalId: 'contrato-2',
    organizationId: hubOrgId,
    name: 'Contrato B',
    status: 'active',
  },
]);

console.log(`Sucesso: ${results.success}, Falhas: ${results.failed}`);
```

### Exemplo Completo: Organização + Contratos

```typescript
// 1. Sincronizar organização primeiro
const orgResult = await hub.syncOrganization({
  externalId: 'org-123',
  name: 'Empresa XYZ',
  status: 'active',
});

if (orgResult.success) {
  const hubOrgId = orgResult.data.hubOrgId;

  // 2. Sincronizar contratos da organização
  await hub.syncContract({
    externalId: 'contrato-abc',
    organizationId: hubOrgId,  // ← usar o hub_org_id retornado
    name: 'Contrato Principal',
    number: 'CPS-2408',
    status: 'active',
    value: 100000,
  });
}
```

## Recebendo Webhooks

Quando eventos acontecem no Hub (ex: organização atualizada), você recebe webhooks:

```typescript
import { verifyWebhookSignature, isTimestampValid } from '@hibrit/connect';

// Em uma API route (Next.js, Express, etc.)
export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get('x-hub-signature');
  const timestamp = request.headers.get('x-hub-timestamp');

  // Validar timestamp (previne replay attacks)
  if (!isTimestampValid(timestamp, 300)) {
    return Response.json({ error: 'Timestamp expired' }, { status: 401 });
  }

  // Verificar assinatura
  const isValid = verifyWebhookSignature(
    signature,
    timestamp,
    body,
    process.env.HUB_WEBHOOK_SECRET
  );

  if (!isValid) {
    return Response.json({ error: 'Invalid signature' }, { status: 401 });
  }

  // Processar evento
  const event = JSON.parse(body);
  console.log('Evento recebido:', event.type, event.data);

  return Response.json({ received: true });
}
```

## Variáveis de Ambiente

```bash
# .env.local
HUB_API_KEY=hbt_sua_api_key_aqui
HUB_WEBHOOK_SECRET=whsec_seu_secret_aqui
HUB_PRODUCT_SLUG=seu-produto
HUB_BASE_URL=https://connect.hibrit.com.br  # opcional
```

## API Reference

### HibritConnect

```typescript
new HibritConnect(config: HibritConnectConfig)

interface HibritConnectConfig {
  apiKey: string;       // Sua API key
  apiSecret: string;    // Seu webhook secret
  productSlug: string;  // Slug do produto
  baseUrl?: string;     // URL da API (default: https://connect.hibrit.com.br)
  timeout?: number;     // Timeout em ms (default: 30000)
  retries?: number;     // Número de retries (default: 3)
}
```

### Métodos

| Método | Descrição |
|--------|-----------|
| `syncOrganization(payload)` | Sincroniza uma organização (push) |
| `syncOrganizations(payloads)` | Sincroniza múltiplas organizações em lote |
| `getOrganizations(options)` | Busca organizações mapeadas no Hub |
| `removeOrganization(externalId)` | Remove mapeamento de organização |
| `syncContract(payload)` | **[v0.4.0]** Sincroniza um contrato (push) |
| `syncContracts(payloads)` | **[v0.4.0]** Sincroniza múltiplos contratos em lote |
| `verifyWebhook(options)` | Verifica assinatura de webhook |
| `getLoginUrl(options)` | Retorna URL de login SSO |
| `getConfig()` | Retorna configuração (sem secrets) |

### Funções Utilitárias

| Função | Descrição |
|--------|-----------|
| `generateSignature(apiKey, timestamp, body, secret)` | Gera assinatura HMAC-SHA256 |
| `verifyWebhookSignature(signature, timestamp, body, secret)` | Verifica assinatura de webhook |
| `isTimestampValid(timestamp, toleranceSeconds)` | Valida timestamp (anti-replay) |

## Tipos

### Organizações

```typescript
interface SyncOrganizationPayload {
  externalId: string;
  name: string;
  slug?: string;
  email?: string;
  status?: 'active' | 'inactive' | 'trial' | 'suspended';
  plan?: string;
  metadata?: Record<string, unknown>;
}

interface SyncResult {
  success: boolean;
  data?: {
    mappingId: string;
    hubOrgId: string;
    action: 'created' | 'updated';
  };
  error?: {
    code: string;
    message: string;
  };
}

interface BatchSyncResult {
  total: number;
  success: number;
  failed: number;
  errors: Array<{ externalId: string; error: string }>;
}

interface GetOrganizationsOptions {
  limit?: number;    // default: 100, max: 500
  offset?: number;   // default: 0
  status?: 'synced' | 'pending' | 'error' | 'conflict';
  includeHubOrg?: boolean; // default: true
}

interface GetOrganizationsResult {
  success: boolean;
  data?: OrganizationMapping[];
  pagination?: {
    total: number;
    offset: number;
    limit: number;
    hasMore: boolean;
  };
  error?: { code: string; message: string };
}

interface OrganizationMapping {
  id: string;
  hub_org_id: string;
  external_id: string;
  external_name?: string;
  external_email?: string;
  sync_status: 'synced' | 'pending' | 'error' | 'conflict';
  last_sync_at?: string;
  hubOrg?: {
    id: string;
    name: string;
    slug: string;
    type: 'holding' | 'agency' | 'client' | 'partner' | 'internal';
    status: 'active' | 'inactive' | 'suspended' | 'trial' | 'churned';
    email?: string;
  };
}
```

### Contratos (v0.4.0+)

```typescript
interface SyncContractPayload {
  externalId: string;
  organizationId: string; // hub_org_id da organização
  name: string;
  number?: string;
  status?: 'draft' | 'active' | 'paused' | 'completed' | 'cancelled';
  startDate?: string;
  endDate?: string;
  renewalDate?: string;
  value?: number;
  currency?: string;
  billingCycle?: 'monthly' | 'quarterly' | 'yearly' | 'one-time';
  metadata?: Record<string, unknown>;
}

interface SyncContractResult {
  success: boolean;
  data?: {
    contractId: string;
    action: 'created' | 'updated';
  };
  error?: {
    code: string;
    message: string;
  };
}

interface BatchSyncContractsResult {
  total: number;
  success: number;
  failed: number;
  errors: Array<{
    externalId: string;
    error: string;
  }>;
}
```

## Segurança

- Todas as requisições são assinadas com HMAC-SHA256
- Formato da assinatura: `sha256={hmac(timestamp.body, secret)}`
- Timestamps são validados para prevenir replay attacks (tolerância: 5 minutos)
- Nunca exponha `apiSecret` no client-side

## Suporte

- Documentação: https://docs.hibrit.com.br
- Issues: https://github.com/hibritoficial/hibrit-connect/issues

## Licença

MIT