Server-side@veroao/node v2.0.0Node.js ≥ 18TypeScript nativo

SDK Node.js

A biblioteca oficial Vero para integrações backend. Emite facturas certificadas pela AGT directamente do teu servidor - sem burocracia, com total segurança de tipos.

Ambiente

Node.js 18+ · Bun · Deno

Compliance

DFe + ATCUD + JWS RS256 automáticos

Resiliência

Retry automático · Idempotência

Instalação

npm
npm install @veroao/node
pnpm
pnpm add @veroao/node

Inicialização

Cria uma instância do cliente uma vez e reutiliza-a em toda a aplicação. O ambiente (test/live) é detectado automaticamente pelo prefixo da chave - não precisas de configurar nada extra.

lib/vero.ts
import { createVeroClient } from '@veroao/node'

// Lê VERO_API_KEY do ambiente automaticamente
export const vero = createVeroClient()

// Ou passa a chave explicitamente
export const vero = createVeroClient({
  // Teste:    vero_test_sk_...  → AGT HML (sem valor fiscal)
  // Produção: vero_live_sk_...  → AGT produção (valor fiscal real)
  secretKey: process.env.VERO_API_KEY!,
  timeout: 10000,   // ms - default: 30 000
  maxRetries: 3,     // tentativas em erro 5xx - default: 3
})

console.log(vero.environment) // 'test' ou 'live' - detectado do prefixo da chave

Emitir uma Factura

Uma factura precisa de três elementos: organização, cliente e produto. O fluxo recomendado usa ensure para garantir que o cliente existe sem criar duplicados, e referencia produtos do teu catálogo para manter consistência.

Garantir que o cliente existe (cria ou actualiza pelo externalId)

Garantir que o produto existe no catálogo

Emitir a factura com cliente + produto

Fluxo completo
import { vero } from '@/lib/vero'

const ORG_ID = process.env.VERO_ORG_ID!

// 1. Garantir que o cliente existe
//    ensure() faz upsert pelo externalId - seguro chamar a cada pedido
const customer = await vero.customers.ensure(ORG_ID, {
  externalId: 'user_123',          // ID do utilizador no teu sistema
  name: 'Maria Fernandes',
  taxId: '003456789LA042',
  phone: '923456789',
  email: 'maria@empresa.ao',
  addressLine1: 'Rua Amílcar Cabral, 45',
  city: 'Luanda',
})

// 2. Garantir que o produto existe no catálogo
//    Podes criar uma vez e guardar o ID, ou referenciar sempre pelo código
const product = await vero.products.create(ORG_ID, {
  code: 'PLAN-PRO',                // código único por organização
  name: 'Plano Pro - Mensal',
  description: 'Subscrição mensal do plano Pro',
  unitPrice: 2500000,            // 25 000,00 AOA em kwanzas × 100
  taxRate: 14,                     // IVA Angola: 0 | 5 | 14
  unitOfMeasure: 'UN',
})

// 3. Emitir a factura
const invoice = await vero.invoices.create(ORG_ID, {
  customerId: customer.id,
  documentType: 'FT',              // FT = Factura | FR = Factura-Recibo
  items: [
    {
      description: product.name,
      quantity: 1,
      unitPrice: product.unitPrice,
      taxRate: product.taxRate,
      productId: product.id,       // liga ao catálogo (opcional mas recomendado)
    },
  ],
  // idempotencyKey gerada automaticamente - passa a tua para evitar duplicados
  idempotencyKey: `pedido_${orderId}`,
})

console.log(invoice.number)        // "FT FT6326S62896N/1" - número AGT certificado
console.log(invoice.atcud)         // "FT7826S214N-1" - código de verificação AGT
console.log(invoice.jwsSignature)  // assinatura RS256 submetida à AGT
console.log(invoice.pdfUrl)        // URL do PDF com QR code e ATCUD

Recursos

Todos os métodos recebem primeiro o orgId. Os detalhes de cada campo estão na página da API.

vero.customerslist · get · create · update · ensure
vero.invoicescreate · list · get · cancel · send · advances
vero.proformascreate · list · get · update · convert · advance · cancel
vero.recurringInvoicescreate · list · get · update · pause · resume · end · pending · issue · skip
vero.creditNotescreate · list · get · send
vero.debitNotescreate · list · get · send · cancel
vero.receiptscreate · createForDebitNote · list · get · send · cancel
vero.productslist · create · update · deactivate
vero.webhooksregister · list · delete

Valores Monetários

Todos os valores usam kwanzas × 100 (inteiros) para evitar erros de vírgula flutuante. Multiplica o valor em AOA por 100.

TypeScript
// 25 000,00 AOA
unitPrice: 2500000

// 1 500,50 AOA
unitPrice:   150050

// Converter de AOA para o formato da API
const toKwanzas = (aoa: number) => Math.round(aoa * 100)
toKwanzas(25000)    // → 2500000

// Converter da API para exibição
const fromKwanzas = (k: number) => (k / 100).toFixed(2)
fromKwanzas(2500000) // → "25000.00"

Tipos TypeScript

Tipos disponíveis
import type {
  Invoice,
  InvoiceStatus,    // 'pending' | 'issued' | 'paid' | 'failed' | 'cancelled'
  DocumentType,     // 'FT' | 'FR' | 'FA'
  Customer,
  Product,
  ProformaInvoice,
  CreditNote,       // kind: 'cancel' | 'advance'
  Receipt,
  OpenAdvance,      // invoices.advances()
  RecurringInvoice, // recurringInvoices.*
  Webhook,
  WebhookEvent,
  CreateInvoiceInput,
  CreateCustomerInput,
  EnsureCustomerInput,
  CreateProductInput,
  InvoiceItem,
  ListResult,
} from '@veroao/node'

Tratamento de Erros

O SDK lança erros tipados - usa instanceof para tratar cada caso de forma granular.

Erros tipados
import {
  VeroAPIError,
  VeroAuthError,
  VeroNotFoundError,
  VeroRateLimitError,
} from '@veroao/node'

try {
  const invoice = await vero.invoices.create(ORG_ID, input)
} catch (err) {
  if (err instanceof VeroAuthError) {
    // Chave inválida ou revogada → verificar VERO_API_KEY
  } else if (err instanceof VeroNotFoundError) {
    // Cliente ou organização não existe
  } else if (err instanceof VeroRateLimitError) {
    // Limite excedido - err.retryAfter tem os segundos a aguardar
    await sleep(err.retryAfter ?? 60)
  } else if (err instanceof VeroAPIError) {
    // Erro da API: err.status (HTTP), err.code (string), err.message
    console.error(err.status, err.code)
  }
}

Idempotência

O SDK gera uma idempotencyKey automaticamente em cada invoices.create(). Para garantir que um pedido repetido (ex: timeout + retry) não cria uma segunda factura, passa a tua própria chave ligada ao ID do pedido.

TypeScript
// Garante que o pedido #789 só gera uma factura, mesmo com retries
const invoice = await vero.invoices.create(ORG_ID, {
  customerId: customer.id,
  items: [...],
  idempotencyKey: `order_${order.id}`,
})

Segurança

A chave vero_live_sk_... tem acesso total à conta. Usa sempre variáveis de ambiente e nunca a exponhas no frontend, em logs ou em repositórios públicos.