npm package: @setup-automatizado/sicredi-sdk
Description: Production-grade TypeScript SDK for Sicredi PIX and Boleto Hibrido APIs
Author: Guilherme Jansen
Latest version: 1.0.0

# @setup-automatizado/sicredi-sdk

[![npm version](https://img.shields.io/npm/v/@setup-automatizado/sicredi-sdk.svg)](https://www.npmjs.com/package/@setup-automatizado/sicredi-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.6+-3178c6.svg)](https://www.typescriptlang.org/)
[![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933.svg)](https://nodejs.org/)

Production-grade TypeScript SDK for the Sicredi PIX and Boleto Hibrido APIs. Built for reliability and developer experience.

## Features

- **Zero dependencies** -- no external runtime dependencies, only dev tooling
- **mTLS authentication** -- native mutual TLS support for Sicredi's certificate-based API
- **Auto-refresh tokens** -- OAuth2 tokens are cached and refreshed automatically before expiry
- **Automatic retries** -- configurable retry logic with exponential backoff for transient failures
- **Bun + Node.js** -- first-class support for both runtimes (auto-detected)
- **Full TypeScript** -- strict types for every request, response, and error
- **QR Code generation** -- built-in SVG QR code generator for PIX "copia e cola" strings
- **Boleto Hibrido** -- complete support for PIX charges with due dates, penalties, interest, and discounts
- **Webhook handling** -- parse and validate Sicredi webhook callback payloads
- **Lazy initialization** -- certificates and connections are set up on first use, not at construction

## Installation

```bash
# Bun (recommended)
bun add @setup-automatizado/sicredi-sdk

# npm
npm install @setup-automatizado/sicredi-sdk

# pnpm
pnpm add @setup-automatizado/sicredi-sdk

# yarn
yarn add @setup-automatizado/sicredi-sdk
```

## Quick Start

```typescript
import { Sicredi, generateTxId } from '@setup-automatizado/sicredi-sdk';

const sicredi = new Sicredi({
  clientId: process.env.SICREDI_CLIENT_ID!,
  clientSecret: process.env.SICREDI_CLIENT_SECRET!,
  certificate: {
    cert: './certs/certificado.pem',
    key: './certs/aplicacao.key',
    ca: './certs/CadeiaCompletaSicredi.pem',
  },
  environment: 'sandbox',
});

// Create a PIX charge
const txid = generateTxId();
const charge = await sicredi.cob.create(txid, {
  calendario: { expiracao: 3600 },
  devedor: { cpf: '12345678909', nome: 'Fulano de Tal' },
  valor: { original: '100.00' },
  chave: 'your-pix-key',
  solicitacaoPagador: 'Payment for order #123',
});

console.log(charge.pixCopiaECola); // PIX copy-and-paste string
console.log(charge.location);      // Payment URL
```

## API Reference

### Configuration

```typescript
import { Sicredi } from '@setup-automatizado/sicredi-sdk';

const sicredi = new Sicredi({
  // Required
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  certificate: {
    cert: './path/to/cert.pem',   // or PEM string
    key: './path/to/key.pem',     // or PEM string
    ca: './path/to/ca.pem',       // optional, CA chain
    passphrase: 'optional',       // private key passphrase
  },

  // Optional
  environment: 'production',      // 'production' | 'sandbox' (default: 'production')
  baseUrl: 'https://custom.url',  // overrides environment URL
  timeout: 30000,                 // request timeout in ms (default: 30000)
  maxRetries: 3,                  // retry attempts (default: 3)
  debug: false,                   // enable debug logging (default: false)
});
```

The `certificate` fields accept either a file path or a PEM-encoded string directly. File paths are resolved relative to the current working directory.

### PIX Imediato (`sicredi.cob`)

Immediate PIX charges without a due date.

#### Create a charge

```typescript
const charge = await sicredi.cob.create(txid, {
  calendario: { expiracao: 3600 }, // expiration in seconds
  devedor: {
    cpf: '12345678909',            // or cnpj: '12345678000195'
    nome: 'Fulano de Tal',
  },
  valor: { original: '100.00' },
  chave: 'your-pix-key',
  solicitacaoPagador: 'Optional description',
  infoAdicionais: [
    { nome: 'Order', valor: '#12345' },
  ],
});
```

#### Create with auto-generated txid

```typescript
const charge = await sicredi.cob.createAuto({
  calendario: { expiracao: 3600 },
  valor: { original: '50.00' },
  chave: 'your-pix-key',
});
// charge.txid is generated by Sicredi
```

#### Get a charge

```typescript
const charge = await sicredi.cob.get(txid);
// Optional: pass a revision number
const charge = await sicredi.cob.get(txid, 2);
```

#### Update a charge

```typescript
const updated = await sicredi.cob.update(txid, {
  valor: { original: '150.00' },
  solicitacaoPagador: 'Updated description',
});
```

#### Cancel a charge

```typescript
const cancelled = await sicredi.cob.cancel(txid);
// Sets status to REMOVIDA_PELO_USUARIO_RECEBEDOR
```

#### List charges

```typescript
const list = await sicredi.cob.list({
  inicio: '2026-01-01T00:00:00Z',
  fim: '2026-01-31T23:59:59Z',
  cpf: '12345678909',           // optional filter
  status: 'ATIVA',              // optional: ATIVA | CONCLUIDA | REMOVIDA_PELO_USUARIO_RECEBEDOR | REMOVIDA_PELO_PSP
  paginacao: {
    paginaAtual: 0,
    itensPorPagina: 10,
  },
});

console.log(list.parametros.paginacao.quantidadeTotalDeItens);
for (const cob of list.cobs) {
  console.log(cob.txid, cob.status, cob.valor.original);
}
```

### Boleto Hibrido (`sicredi.cobv`)

PIX charges with a due date, supporting penalties, interest, and discounts.

#### Create a Boleto Hibrido

```typescript
const cobv = await sicredi.cobv.create(txid, {
  calendario: {
    dataDeVencimento: '2026-03-15',      // due date (YYYY-MM-DD)
    validadeAposVencimento: 30,           // days valid after due date
  },
  devedor: {
    cnpj: '12345678000195',
    nome: 'Empresa Exemplo LTDA',
  },
  valor: {
    original: '1500.00',
    multa: {
      modalidade: 2,                      // 1 = fixed value, 2 = percentage
      valorPerc: '2.00',                  // 2% penalty
    },
    juros: {
      modalidade: 2,                      // 2 = percentage per month
      valorPerc: '1.00',                  // 1% per month
    },
    desconto: {
      modalidade: 1,                      // 1 = fixed value by date
      descontoDataFixa: [
        { data: '2026-03-10', valorPerc: '50.00' },
      ],
    },
  },
  chave: 'your-pix-key',
  solicitacaoPagador: 'Monthly invoice',
});
```

#### Get, update, cancel, and list

```typescript
// Get
const cobv = await sicredi.cobv.get(txid);

// Update
const updated = await sicredi.cobv.update(txid, {
  valor: { original: '1600.00' },
});

// Cancel
const cancelled = await sicredi.cobv.cancel(txid);

// List
const list = await sicredi.cobv.list({
  inicio: '2026-01-01T00:00:00Z',
  fim: '2026-01-31T23:59:59Z',
});
```

### Webhooks (`sicredi.webhook`)

Configure webhook URLs to receive PIX payment notifications.

#### Configure a webhook

```typescript
await sicredi.webhook.configure('your-pix-key', 'https://your-domain.com/webhook/pix');
```

#### Get webhook configuration

```typescript
const webhook = await sicredi.webhook.get('your-pix-key');
console.log(webhook.webhookUrl);
console.log(webhook.chave);
console.log(webhook.criacao);
```

#### List webhooks

```typescript
const list = await sicredi.webhook.list({
  inicio: '2026-01-01T00:00:00Z',
  fim: '2026-01-31T23:59:59Z',
});

for (const wh of list.webhooks) {
  console.log(wh.chave, wh.webhookUrl);
}
```

#### Delete a webhook

```typescript
await sicredi.webhook.delete('your-pix-key');
```

### PIX Recebidos (`sicredi.pix`)

Query received PIX payments and request refunds/returns.

#### Get a received PIX payment

```typescript
const pix = await sicredi.pix.get('E01181521202301011234abcdef12345');
console.log(pix.endToEndId);
console.log(pix.valor);
console.log(pix.chave);
console.log(pix.horario.solicitacao);
```

#### List received PIX payments

```typescript
const list = await sicredi.pix.list({
  inicio: '2026-01-01T00:00:00Z',
  fim: '2026-01-31T23:59:59Z',
  txIdPresente: true,           // optional: filter by txid presence
  devolucaoPresente: false,     // optional: filter by refund presence
  paginacao: { paginaAtual: 0, itensPorPagina: 50 },
});

for (const pix of list.pix) {
  console.log(pix.endToEndId, pix.valor, pix.horario.solicitacao);
}
```

#### Request a refund (devolucao)

```typescript
const refund = await sicredi.pix.requestReturn(
  'E01181521202301011234abcdef12345', // endToEndId
  'refund-001',                        // devolucao ID
  {
    valor: '50.00',
    natureza: 'ORIGINAL',              // optional: 'ORIGINAL' | 'RETIRADA'
    descricao: 'Refund for order #123',
  },
);

console.log(refund.rtrId);    // Return Transaction Reference ID
console.log(refund.status);   // 'EM_PROCESSAMENTO' | 'DEVOLVIDO' | 'NAO_REALIZADO'
```

#### Get refund status

```typescript
const refund = await sicredi.pix.getReturn(
  'E01181521202301011234abcdef12345',
  'refund-001',
);
console.log(refund.status);
```

### Lote CobV (`sicredi.lotecobv`)

Create and manage batches of Boleto Hibrido charges.

#### Create a batch

```typescript
await sicredi.lotecobv.create(1, {
  descricao: 'January invoices',
  cobsv: [
    {
      txid: generateTxId(),
      calendario: { dataDeVencimento: '2026-02-15' },
      devedor: { cpf: '12345678909', nome: 'Cliente A' },
      valor: { original: '200.00' },
      chave: 'your-pix-key',
    },
    {
      txid: generateTxId(),
      calendario: { dataDeVencimento: '2026-02-15' },
      devedor: { cnpj: '12345678000195', nome: 'Empresa B' },
      valor: { original: '500.00' },
      chave: 'your-pix-key',
    },
  ],
});
```

#### Update specific charges in a batch

```typescript
await sicredi.lotecobv.update(1, {
  cobsv: [
    { txid: 'existing-txid-here-12345678', valor: { original: '250.00' } },
  ],
});
```

#### Get batch status

```typescript
const batch = await sicredi.lotecobv.get(1);
for (const item of batch.cobsv) {
  console.log(item.txid, item.status); // 'EM_PROCESSAMENTO' | 'CRIADA'
  if (item.problema) {
    console.error(item.problema.title, item.problema.detail);
  }
}
```

#### List batches

```typescript
const list = await sicredi.lotecobv.list({
  inicio: '2026-01-01T00:00:00Z',
  fim: '2026-01-31T23:59:59Z',
});
```

### Payload Locations (`sicredi.loc`)

Manage payload locations for QR codes.

#### Create a location

```typescript
const loc = await sicredi.loc.create({ tipoCob: 'cob' }); // or 'cobv'
console.log(loc.id);
console.log(loc.location);
```

#### Get a location

```typescript
const loc = await sicredi.loc.get(42);
```

#### List locations

```typescript
const list = await sicredi.loc.list({
  inicio: '2026-01-01T00:00:00Z',
  fim: '2026-01-31T23:59:59Z',
  tipoCob: 'cobv', // optional filter
});
```

#### Unlink a txid from a location

```typescript
await sicredi.loc.unlink(42);
```

#### Handle webhook callbacks

```typescript
import { parseWebhookPayload } from '@setup-automatizado/sicredi-sdk';

// Works with Express, Koa, Bun, or any framework
const result = parseWebhookPayload(requestBody);

if (result.valid) {
  for (const pix of result.payload!.pix) {
    console.log(pix.endToEndId);
    console.log(pix.txid);
    console.log(pix.valor);
    console.log(pix.chave);
    console.log(pix.horario);
  }
}
```

### Utilities

#### Transaction ID

```typescript
import { generateTxId, isValidTxId } from '@setup-automatizado/sicredi-sdk';

const txid = generateTxId();        // 35-char alphanumeric string
const txid = generateTxId(26);      // custom length (26-35)
isValidTxId('abc123...');           // true/false
```

#### Validators

```typescript
import {
  isValidCpf,
  isValidCnpj,
  isValidPixKey,
  detectPixKeyType,
  isValidMonetaryValue,
} from '@setup-automatizado/sicredi-sdk';

isValidCpf('12345678909');                                   // true/false (with check digits)
isValidCnpj('12345678000195');                               // true/false (with check digits)
isValidPixKey('+5511999999999');                              // true/false
detectPixKeyType('user@example.com');                         // 'email'
detectPixKeyType('12345678909');                              // 'cpf'
detectPixKeyType('a1b2c3d4-e5f6-7890-abcd-ef1234567890');    // 'evp'
isValidMonetaryValue('100.00');                               // true
isValidMonetaryValue('100.0');                                // false (must be 2 decimal places)
```

#### QR Code

```typescript
import { generateQrCodeSvg, generateQrCodeDataUrl } from '@setup-automatizado/sicredi-sdk';

// Generate SVG string from PIX "copia e cola"
const svg = generateQrCodeSvg(charge.pixCopiaECola!, {
  size: 256,
  margin: 4,
  darkColor: '#000000',
  lightColor: '#FFFFFF',
});

// Generate data URL for embedding in HTML <img> tags
const dataUrl = generateQrCodeDataUrl(charge.pixCopiaECola!);
// <img src={dataUrl} alt="PIX QR Code" />
```

#### Date helpers

```typescript
import {
  toISOString,
  createDateRange,
  parseDate,
  formatDateOnly,
} from '@setup-automatizado/sicredi-sdk';

const { inicio, fim } = createDateRange(30);   // last 30 days as ISO strings
const dateStr = formatDateOnly(new Date());     // '2026-02-08'
const date = parseDate('2026-02-08T00:00:00Z'); // Date object
```

#### Constants

```typescript
import { SICREDI_URLS, SICREDI_ISPB } from '@setup-automatizado/sicredi-sdk';

SICREDI_URLS.production; // 'https://api-pix.sicredi.com.br'
SICREDI_URLS.sandbox;    // 'https://api-pix-h.sicredi.com.br'
SICREDI_ISPB;            // '01181521'
```

## Error Handling

The SDK provides typed error classes for different failure scenarios:

```typescript
import {
  SicrediApiError,
  SicrediAuthError,
  SicrediValidationError,
  SicrediConnectionError,
  SicrediCertificateError,
} from '@setup-automatizado/sicredi-sdk';

try {
  const charge = await sicredi.cob.create(txid, data);
} catch (error) {
  if (error instanceof SicrediApiError) {
    // API returned an error response (4xx/5xx)
    console.error('API Error:', error.statusCode);
    console.error('Title:', error.title);
    console.error('Detail:', error.detail);
    console.error('Violations:', error.violacoes);
  } else if (error instanceof SicrediAuthError) {
    // Authentication failed (invalid credentials, expired token)
    console.error('Auth Error:', error.message);
  } else if (error instanceof SicrediValidationError) {
    // Local validation failed (invalid txid, etc.)
    console.error('Validation Error:', error.message);
  } else if (error instanceof SicrediConnectionError) {
    // Network error (timeout, DNS, etc.)
    console.error('Connection Error:', error.message);
  } else if (error instanceof SicrediCertificateError) {
    // Certificate loading/validation failed
    console.error('Certificate Error:', error.message);
  }
}
```

All errors extend the base `SicrediError` class, which includes:
- `message` -- human-readable error description
- `code` -- machine-readable error code (e.g., `'API_ERROR'`, `'AUTH_ERROR'`)
- `hint` -- optional suggestion for fixing the issue

## Environments

| Environment | Base URL | Description |
|---|---|---|
| `production` | `https://api-pix.sicredi.com.br` | Live Sicredi PIX API |
| `sandbox` | `https://api-pix-h.sicredi.com.br` | Homologation/testing environment |

```typescript
// Production (default)
const sicredi = new Sicredi({ environment: 'production', ... });

// Sandbox
const sicredi = new Sicredi({ environment: 'sandbox', ... });

// Custom URL (overrides environment)
const sicredi = new Sicredi({ baseUrl: 'https://custom-proxy.example.com', ... });
```

## Certificate Setup

Sicredi requires mTLS (mutual TLS) for API authentication. You will need:

1. **Client certificate** (`cert`) -- your application's certificate in PEM format
2. **Private key** (`key`) -- the private key corresponding to the certificate in PEM format
3. **CA chain** (`ca`) -- Sicredi's complete certificate chain in PEM format (optional but recommended)

These are typically provided by Sicredi during the API onboarding process. If your certificates are in PKCS#12 (`.p12` / `.pfx`) format, convert them to PEM:

```bash
# Extract certificate
openssl pkcs12 -in certificado.p12 -clcerts -nokeys -out certificado.pem

# Extract private key
openssl pkcs12 -in certificado.p12 -nocerts -nodes -out aplicacao.key
```

You can provide the certificate as file paths or directly as PEM strings:

```typescript
// File paths (resolved from cwd)
certificate: {
  cert: './certs/certificado.pem',
  key: './certs/aplicacao.key',
  ca: './certs/CadeiaCompletaSicredi.pem',
}

// PEM strings (e.g., from environment variables)
certificate: {
  cert: process.env.SICREDI_CERT!,
  key: process.env.SICREDI_KEY!,
  ca: process.env.SICREDI_CA!,
}
```

## Examples

See the [`examples/`](./examples) directory for complete working examples:

- **[basic-pix-charge.ts](./examples/basic-pix-charge.ts)** -- Create, query, and list PIX immediate charges
- **[boleto-hibrido.ts](./examples/boleto-hibrido.ts)** -- Create Boleto Hibrido with penalties, interest, and discounts
- **[webhook-setup.ts](./examples/webhook-setup.ts)** -- Configure, list, and delete webhooks
- **[express-webhook-handler.ts](./examples/express-webhook-handler.ts)** -- Handle PIX webhook callbacks with Express

## Contributing

Contributions are welcome! Please follow these steps:

1. Fork the repository
2. Create a feature branch (`git checkout -b feat/my-feature`)
3. Make your changes
4. Run checks:
   ```bash
   bun run typecheck
   bun run lint
   bun run test
   ```
5. Commit using [Conventional Commits](https://www.conventionalcommits.org/) format
6. Open a Pull Request

### Development

```bash
# Install dependencies
bun install

# Type checking
bun run typecheck

# Linting
bun run lint

# Run tests
bun run test

# Run tests with coverage
bun run test:coverage

# Build
bun run build
```

## License

[MIT](./LICENSE) -- Copyright (c) 2026 Guilherme Jansen - Setup Automatizado LTDA