> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wyzecash.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK para Node.js

> O pacote oficial wyzecash: cobranças, saques, subcontas, webhooks e tempo real com tipos e mensagens em português, sem dependências.

O `wyzecash` é o SDK oficial da WyzeCash para **Node.js 20+, Bun e Deno**. Ele fala com a mesma API da documentação, só que com tipos, listas que passam de página sozinhas, novas tentativas seguras, conferência dos webhooks e o WebSocket prontos.

> [card] Sem dependências
>
> Usa só o `fetch` e o `crypto` que já vêm no Node, no Bun e no Deno.

> [card] Tipos em português
>
> Todo campo e toda resposta têm tipo e descrição. O editor mostra ao passar o mouse.

> [card] Seguro com dinheiro
>
> O que tira dinheiro nunca é repetido sozinho.

> [!WARNING]
>
> Use o SDK **só no servidor**. A chave de API vai em todo pedido: nunca a coloque no navegador, no app ou num repositório.

## Instalar

```bash npm theme={null}
npm install wyzecash
```

```bash pnpm theme={null}
pnpm add wyzecash
```

```bash yarn theme={null}
yarn add wyzecash
```

```bash bun theme={null}
bun add wyzecash
```

## Começar

**1. Crie a chave de API**

No painel, em **Integrações e API › Chaves de API**, crie uma chave `bk_live_…` só com as permissões que o seu sistema usa (ex.: `/payments/create` e `/payments/get`).

**2. Guarde a chave no ambiente**

```bash .env theme={null}
WYZECASH_API_KEY=bk_live_...
```

Sem passar a chave no código, o SDK lê a variável `WYZECASH_API_KEY`.

**3. Crie a primeira cobrança**

```javascript theme={null}
import WyzeCash from "wyzecash";

const wyzecash = new WyzeCash(process.env.WYZECASH_API_KEY);

const cobranca = await wyzecash.payments.create(
  { amount: 15000, description: "Pedido 1052" }, // R$ 150,00, sempre em centavos
  { idempotencyKey: "pedido-1052" },
);

console.log(cobranca.id);        // BKY-...
console.log(cobranca.copyPaste); // Pix Copia e Cola (vira QR Code)
```

**4. Receba o aviso de pago**

Pelo [webhook](#webhooks) (seu servidor tem endereço HTTPS) ou pelo [tempo real](#tempo-real-websocket) (não tem). Libere o pedido só no `payment.paid`.

> [!NOTE]
>
> Não existe ambiente de testes: todo pedido é de verdade. Para testar, faça uma cobrança pequena e pague com o seu banco.

## Configurar o cliente

```javascript theme={null}
import WyzeCash from "wyzecash";

const wyzecash = new WyzeCash(process.env.WYZECASH_API_KEY, {
  timeout: 60_000, // tempo máximo de cada pedido, em ms (padrão 60 000)
  maxRetries: 2,   // novas tentativas quando é seguro (padrão 2)
});
```

| Opção | Padrão | O que faz |
| - | - | - |
| `apiKey` (ou o 1º parâmetro) | a variável `WYZECASH_API_KEY` | A chave `bk_live_…` |
| `timeout` | `60000` | Tempo máximo de cada pedido, em milissegundos |
| `maxRetries` | `2` | Quantas vezes tentar de novo quando é seguro (veja [Erros e novas tentativas](#erros-e-novas-tentativas)) |
| `baseUrl` | o endereço da API | Só para casos especiais (proxy) |
| `fetch` | o `fetch` do ambiente | Outro `fetch` (testes ou proxy) |

Cada chamada também aceita opções próprias no último parâmetro: `idempotencyKey`, `timeout`, `maxRetries` e `signal` (um `AbortSignal` para cancelar).

## Recursos

Os métodos seguem as rotas da API. Cada um precisa da permissão daquela rota na chave.

| Recurso | Métodos | Rotas |
| - | - | - |
| `payments` | `create`, `methods`, `list`, `retrieve` | [Cobranças](https://docs.wyzecash.com.br/endpoints/cobrancas.md) |
| `paymentLinks` | `create`, `list`, `retrieve`, `update`, `delete`, `listPayments` | [Links de pagamento](https://docs.wyzecash.com.br/endpoints/links-de-pagamento.md) |
| `customers` | `create`, `list`, `retrieve`, `update`, `delete` | [Clientes](https://docs.wyzecash.com.br/endpoints/clientes.md) |
| `balance` | `retrieve` | [Saldo e tarifas](https://docs.wyzecash.com.br/endpoints/saldo.md) |
| `fees` | `list`, `preview` | [Saldo e tarifas](https://docs.wyzecash.com.br/endpoints/saldo.md) |
| `statement` | `list` | [Extrato](https://docs.wyzecash.com.br/endpoints/transacoes.md) |
| `transactions` | `list`, `retrieve` | [Extrato](https://docs.wyzecash.com.br/endpoints/transacoes.md) |
| `search` | `query` | [Extrato](https://docs.wyzecash.com.br/endpoints/transacoes.md) |
| `receipts` | `retrieve` | [Saques](https://docs.wyzecash.com.br/endpoints/saques.md) |
| `withdrawals` | `create`, `list`, `retrieve` | [Saques](https://docs.wyzecash.com.br/endpoints/saques.md) |
| `refunds` | `create`, `list`, `retrieve` | [Cobranças](https://docs.wyzecash.com.br/endpoints/cobrancas.md) |
| `meds` | `list`, `retrieve`, `respond` | [MED](https://docs.wyzecash.com.br/endpoints/med.md) |
| `subaccounts` | `create`, `list`, `retrieve`, `update`, `delete`, `retrieveSettings`, `updateSettings`, `previewSettings`, `listEntries`, `fund`, `transfer`, `returnToAccount`, `withdraw` | [Subcontas](https://docs.wyzecash.com.br/endpoints/subcontas.md) |

## Exemplos

> [tab] Pix
>
> ```javascript theme={null}
> const cobranca = await wyzecash.payments.create(
>   { amount: 4990, description: "Assinatura de novembro", customerId: "0d6c1a7e-..." },
>   { idempotencyKey: "assinatura-2026-11-cliente-81" },
> );
>
> // Mostre o QR Code (gerado do copyPaste) ou o texto para copiar.
> enviarParaCliente(cobranca.copyPaste);
> ```
>
> O Pix vale 30 minutos (`expiresAt`).

> [tab] Cartão
>
> ```javascript theme={null}
> const cobranca = await wyzecash.payments.create(
>   { amount: 15000, method: "card", description: "Pedido 1053" },
>   { idempotencyKey: "pedido-1053" },
> );
>
> // Mande o cliente para a página segura de pagamento.
> redirecionar(cobranca.paymentUrl);
> ```
>
> O número do cartão nunca passa pelo seu servidor. A venda aprovada fica "A liberar" até `availableAt`.

> [tab] Cripto
>
> ```javascript theme={null}
> const cobranca = await wyzecash.payments.create(
>   { amount: 20000, method: "crypto", cryptoCurrency: "sol" },
>   { idempotencyKey: "pedido-1054" },
> );
>
> console.log(cobranca.crypto); // moeda, valor na moeda e endereço
> ```
>
> A cotação vale 10 minutos.

> [tab] Cliente paga a tarifa
>
> ```javascript theme={null}
> const cobranca = await wyzecash.payments.create(
>   { amount: 10000, feePaidBy: "payer" },
>   { idempotencyKey: "pedido-1055" },
> );
>
> console.log(cobranca.total);     // o que o cliente paga (valor + tarifa)
> console.log(cobranca.netAmount); // o que entra para você (o valor inteiro)
> ```

> [tab] Saque
>
> ```javascript theme={null}
> const saque = await wyzecash.withdrawals.create(
>   { amount: 10000, destination: { keyType: "email", key: "financeiro@exemplo.com" } },
>   { idempotencyKey: "saque-2026-11-05" },
> );
>
> console.log(saque.status); // "processing": confirme pelo webhook withdrawal.completed
> ```
>
> A chave precisa da permissão `/withdrawals/create`, que pede o 2FA ligado na conta ao criar a chave.

> [tab] Estorno
>
> ```javascript theme={null}
> const estorno = await wyzecash.refunds.create(
>   { transactionId: "BKY-7KQ2M9XP4RZT", amount: 5000, reason: "Produto devolvido" },
>   { idempotencyKey: "estorno-pedido-1052" },
> );
> ```
>
> Estorno só no Pix. Sem `amount`, devolve tudo o que ainda não foi devolvido.

## Listas

`await` traz **a primeira página**. `for await` passa por **todas**, buscando a próxima só quando precisa.

```javascript theme={null}
// Só a primeira página
const pagina = await wyzecash.payments.list({ status: "paid", limit: 50 });
console.log(pagina.items.length, pagina.nextCursor);

// Todas, uma de cada vez
for await (const cobranca of wyzecash.payments.list({ status: "paid" })) {
  console.log(cobranca.id, cobranca.amount);
}

// Numa lista só (cuidado com contas grandes)
const ultimas = await wyzecash.statement.list().toArray(200);
```

## Idempotency-Key

Criar cobrança, saque, estorno e os envios das subcontas pedem a `Idempotency-Key`. Passe **o código do pedido no seu sistema**: repetir a mesma chave devolve o mesmo resultado, sem fazer de novo.

```javascript theme={null}
await wyzecash.withdrawals.create(
  { amount: 10000, destination: { keyType: "cpf", key: "12345678900" } },
  { idempotencyKey: "saque-1052" },
);
```

> [!TIP]
>
> Sem a chave, o SDK cria uma por chamada: protege a nova tentativa automática, mas não protege você de chamar duas vezes. Em dinheiro saindo, passe sempre a sua.

## Erros e novas tentativas

Todo erro da API vira um `WyzeCashError`:

```javascript theme={null}
import WyzeCash, { WyzeCashError, WyzeCashConnectionError } from "wyzecash";

try {
  await wyzecash.payments.create({ amount: 15000 }, { idempotencyKey: "pedido-1056" });
} catch (erro) {
  if (erro instanceof WyzeCashConnectionError) {
    // A resposta não voltou (rede ou tempo esgotado): confira antes de tentar de novo.
  } else if (erro instanceof WyzeCashError) {
    console.log(erro.status, erro.code, erro.message, erro.requestId);
  }
}
```

| Campo | O que é |
| - | - |
| `status` | A situação HTTP (`0` quando o pedido nem chegou à API) |
| `code` | Código estável para o seu sistema: `VALIDATION`, `INSUFFICIENT_FUNDS`, `RATE_LIMITED`... (veja [Erros](https://docs.wyzecash.com.br/erros.md)) |
| `message` | O motivo em português |
| `requestId` | O código do pedido: mande para o suporte quando precisar de ajuda |
| `details` | Detalhes, quando a API manda (ex.: qual campo está errado) |

O SDK tenta de novo sozinho só quando é seguro:

| Situação | Tenta de novo? |
| - | - |
| `429` (passou de 100 pedidos por minuto) | Sim, esperando o `Retry-After` |
| Erro de rede ou `5xx` numa consulta ou ao criar cobrança | Sim |
| Saque, estorno e envios das subcontas | **Nunca.** Se a rede cair, confira pela consulta ou pelo webhook antes de pedir de novo |
| `4xx` (dado errado, sem saldo, sem permissão) | Não: corrija o pedido |

## Webhooks

Cada aviso chega com o cabeçalho `X-Bukio-Signature`. Confira **com o corpo cru**, do jeito que chegou:

> [tab] Express
>
> ```javascript theme={null}
> import express from "express";
> import { verifyWebhook, WyzeCashWebhookError } from "wyzecash";
>
> const app = express();
>
> app.post("/webhooks/wyzecash", express.raw({ type: "application/json" }), async (req, res) => {
>   try {
>     const evento = await verifyWebhook(req.body, req.headers["x-bukio-signature"], process.env.WYZECASH_WEBHOOK_SECRET);
>     res.sendStatus(200); // responda logo; processe depois
>     if (evento.type === "payment.paid") await liberarPedido(evento.data); // use evento.id para não processar duas vezes
>   } catch (erro) {
>     if (erro instanceof WyzeCashWebhookError) return res.sendStatus(400);
>     throw erro;
>   }
> });
> ```

> [tab] Fastify
>
> ```javascript theme={null}
> import Fastify from "fastify";
> import { wyzecashWebhooks } from "wyzecash/fastify";
>
> const app = Fastify({ logger: true });
>
> await app.register(wyzecashWebhooks, {
>   path: "/webhooks/wyzecash",                 // padrão
>   secret: process.env.WYZECASH_WEBHOOK_SECRET,   // padrão: a variável WYZECASH_WEBHOOK_SECRET
>   async onEvent(evento) {
>     if (evento.type === "payment.paid") await liberarPedido(evento.data);
>   },
> });
>
> await app.listen({ port: 3000 });
> ```
>
> O plugin lê o corpo cru só na rota dele e responde `200` quando deu certo, `400` quando o aviso não é da WyzeCash e `500` quando o `onEvent` falha (a WyzeCash manda de novo mais tarde).

> [tab] Next.js, Bun ou Deno
>
> ```javascript theme={null}
> import { verifyWebhook } from "wyzecash";
>
> export async function POST(request) {
>   const corpo = await request.text();
>   try {
>     const evento = await verifyWebhook(corpo, request.headers.get("x-bukio-signature"), process.env.WYZECASH_WEBHOOK_SECRET);
>     if (evento.type === "payment.paid") await liberarPedido(evento.data);
>     return new Response("ok");
>   } catch {
>     return new Response("assinatura inválida", { status: 400 });
>   }
> }
> ```

| Erro (`WyzeCashWebhookError`) | Quando |
| - | - |
| `MISSING_SIGNATURE` | O aviso veio sem o cabeçalho `X-Bukio-Signature` |
| `INVALID_SIGNATURE` | Assinatura errada ou em formato errado |
| `TIMESTAMP_OUT_OF_RANGE` | Assinado há mais de 5 minutos (mude com `{ tolerance: segundos }`) |
| `INVALID_PAYLOAD` | O corpo não é um evento válido |

O segredo `whsec_…` fica em **Integrações e API › Webhooks**. Veja [Validando a assinatura](https://docs.wyzecash.com.br/webhooks/assinatura.md) e a [lista de eventos](https://docs.wyzecash.com.br/webhooks/configurar.md).

## Tempo real (WebSocket)

Os mesmos eventos dos webhooks, na hora, **sem precisar de um endereço HTTPS seu**. Bom para bots e workers.

```javascript theme={null}
const ws = wyzecash.realtime({
  events: ["payment.*", "withdrawal.*"], // padrão: ["*"]
  // payments: ["BKY-6H99YBDMMGBY"],     // só estas cobranças, por 30 minutos
  // since: "evt_...",                   // último evento que você já processou
});

ws.on("open", () => console.log("conectado"));
ws.on("payment.paid", (evento) => liberarPedido(evento.data));
ws.on("withdrawal.*", (evento) => console.log(evento.event, evento.data.id));
ws.on("error", (erro) => console.error(erro.code, erro.message));
ws.on("close", ({ code, reconnecting }) => console.log("caiu", code, reconnecting));
```

| O que | Como |
| - | - |
| Ouvir mais uma cobrança | `ws.subscribePayment("BKY-…")` |
| Parar de ouvir | `ws.unsubscribe("payment.*")` ou `ws.unsubscribe("BKY-…")` |
| Último evento recebido | `ws.lastEventId`: guarde e passe em `since` quando o seu sistema reiniciar |
| Fechar de vez | `ws.close()` |
| Todos os eventos num lugar só | `ws.on("event", fn)` |

* Caiu? Conecta de novo sozinho, esperando cada vez mais (até 30 s), e pede o que perdeu desde o último evento (até 24 horas).
* Chave apagada, inválida ou sem permissão (fechamento `1008`) **não** reconecta.
* No Node 20, que não tem WebSocket, passe o do pacote `ws`: `wyzecash.realtime({ WebSocket })`, com `import WebSocket from "ws"`.

Detalhes do protocolo, erros e limites em [WebSocket](https://docs.wyzecash.com.br/webhooks/websocket.md).

## TypeScript

Os tipos já vêm no pacote. Parâmetros e respostas têm nomes como `PaymentsCreateParams` e `PaymentsCreateResponse`:

```typescript theme={null}
import WyzeCash, { type PaymentsCreateParams, type WebhookEvent } from "wyzecash";

const pedido: PaymentsCreateParams = { amount: 15000, method: "pix" };
const cobranca = await new WyzeCash().payments.create(pedido, { idempotencyKey: "pedido-1057" });
```

## Onde roda

| Ambiente | Funciona? |
| - | - |
| Node.js 22+ | Sim (WebSocket incluso) |
| Node.js 20 | Sim (para o tempo real, passe o pacote `ws`) |
| Bun | Sim |
| Deno | Sim |
| Navegador ou app | **Não use**: a chave de API ficaria exposta |

## Outras linguagens

Também há SDK oficial para [Python](https://docs.wyzecash.com.br/sdk/python.md) (`pip install wyzecash`) e [PHP](https://docs.wyzecash.com.br/sdk/php.md) (`composer require wyzecash/wyzecash`), com as mesmas regras de novas tentativas e de idempotência.

O SDK é opcional. Qualquer linguagem que faça requisição HTTP integra: veja os exemplos em cURL, Python e PHP em [Sua primeira cobrança](https://docs.wyzecash.com.br/inicio-rapido.md) e a [Referência da API](https://docs.wyzecash.com.br/referencia/visao-geral.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.