@hubpay/sdk
v0.13.2
Published
SDK TypeScript oficial do Hubpay — orquestração de PSPs de pagamento.
Maintainers
Readme
@hubpay/sdk
SDK TypeScript oficial do Hubpay — API unificada para cobranças Pix sobre múltiplos PSPs.
Sumário
- Instalação
- Configuração do client
- Quick start
- PSP Providers (ENUMs aceitos)
- Criando cobranças
- Smart Routing (provider
random) - Webhooks
- Tratamento de erros
- TypeScript — tipos exportados
- Compatibilidade de runtime
- Links
Instalação
pnpm add @hubpay/sdk
# ou
npm install @hubpay/sdk
yarn add @hubpay/sdk
bun add @hubpay/sdkRequisitos
- Node.js ≥ 18 (precisa de
fetchnativo). - TypeScript opcional — o pacote já traz
.d.ts. - Funciona em Bun, Deno e edge runtimes (Vercel Edge, Cloudflare Workers). Veja Compatibilidade de runtime.
Configuração do client
import { Hubpay } from '@hubpay/sdk';
const hubpay = new Hubpay({
apiKey: process.env.HUBPAY_API_KEY!,
// baseUrl: 'https://api.hubpay.dev', // opcional
});| Campo | Tipo | Obrigatório | Default | Descrição |
| --------- | -------- | ----------- | ------------------------- | ------------------------------------------------------------------------------------------ |
| apiKey | string | sim | — | Chave de API gerada no dashboard. Prefixos: hpx_test_... (ambiente test) ou hpx_live_... (live). |
| baseUrl | string | não | https://api.hubpay.dev | Útil pra apontar para staging ou para um proxy interno. |
test vs live — chaves
hpx_test_...só processam cobranças no ambiente test (PSPs em sandbox quando suportado). Chaveshpx_live_...processam dinheiro de verdade. Gere ambas em hubpay.dev/dashboard/api-keys.
Quick start
import { Hubpay } from '@hubpay/sdk';
const hubpay = new Hubpay({
apiKey: process.env.HUBPAY_API_KEY!,
});
// Criar cobrança
const charge = await hubpay.charges.create({
amount: 5000, // R$ 50,00 em centavos
provider: 'asaas',
description: 'Pedido #1234',
payer: { name: 'João Silva', document: '12345678901' },
});
console.log(charge.pix.copy_paste); // Pix copia e cola
console.log(charge.pix.qr_code_base64); // QR code em base64
console.log(charge.status); // 'pending'
// Buscar cobrança (atualiza status em tempo real se >30s de criada)
const updated = await hubpay.charges.get(charge.id);
// Cancelar cobrança pendente
const cancelled = await hubpay.charges.cancel(charge.id);PSP Providers (ENUMs aceitos)
O campo provider em charges.create() aceita os valores abaixo. Os valores são case-sensitive e correspondem exatamente ao ENUM psp_provider no backend.
| PSP | Valor a usar no provider |
| ---------------------------- | -------------------------- |
| Asaas | "asaas" |
| Mercado Pago | "mercadopago" |
| Pagar.me | "pagarme" |
| Efí (antigo Gerencianet) | "efi" |
| Stark Bank | "starkbank" |
| Stripe | "stripe" |
| Iugu | "iugu" |
| PagSeguro (UOL) | "pagseguro" |
| Banco Inter | "inter" |
| Sicredi | "sicredi" |
| Sicoob | "sicoob" |
| OpenPix (Woovi) | "openpix" |
| Bradesco | "bradesco" |
| Smart routing (round-robin) | "random" |
Pré-requisitos pra usar um provider:
- O PSP precisa estar conectado e ativo no dashboard.
- O ambiente da credencial do PSP precisa bater com o ambiente da
apiKey(test ↔ test, live ↔ live). Caso contrário, a API respondepsp_not_configured(HTTP 402). - Para
"random", são necessários ≥ 2 PSPs ativos no mesmo ambiente.
Criando cobranças
Parâmetros — CreateChargeParams
| Campo | Tipo | Obrigatório | Descrição |
| -------------------- | ------------------------------- | ----------- | -------------------------------------------------------------------------- |
| amount | number | sim | Valor em centavos. Ex.: 5000 = R$ 50,00. |
| provider | string (ver tabela acima) | sim | Provider específico ou "random". |
| description | string | não | Descrição livre da cobrança (até 500 caracteres). |
| expires_in_seconds | number | não | Tempo de expiração. Default: 86400 (24h). |
| payer.name | string | não | Nome do pagador. |
| payer.document | string | não | CPF (11 dígitos) ou CNPJ (14 dígitos), só números. |
| payer.email | string | não | E-mail do pagador. |
| metadata | Record<string, unknown> | não | Dados arbitrários que retornam intactos no GET / webhook. |
Objeto retornado — Charge
| Campo | Tipo | Descrição |
| ------------------ | ------------------------------------------------- | -------------------------------------------------------------------- |
| id | string | ID único (ch_...). |
| object | "charge" | Discriminador. |
| status | ChargeStatus | Ver lista abaixo. |
| amount | number | Valor em centavos. |
| currency | string | Sempre "BRL" por enquanto. |
| description | string \| null | |
| provider | string | PSP que efetivamente processou (nunca "random"). |
| environment | "test" \| "live" | Reflete a apiKey usada. |
| routing | ChargeRouting \| undefined | Presente apenas se foi usado provider: "random". |
| pix.copy_paste | string \| null | Pix copia-e-cola pronto para o cliente. |
| pix.qr_code_base64 | string \| null | PNG em base64 (sem prefixo data:image/png;base64,). |
| payer | { name, document, email } | Os campos podem ser null. |
| metadata | Record<string, unknown> \| null | Echo do que foi enviado. |
| expires_at | string \| null | ISO 8601. |
| paid_at | string \| null | ISO 8601. |
| created_at | string | ISO 8601. |
| updated_at | string | ISO 8601. |
Valores de ChargeStatus
| Status | Significado |
| ------------- | ------------------------------------------------------------------------ |
| pending | Cobrança criada, aguardando pagamento. |
| paid | Pagamento confirmado pelo PSP. |
| expired | Passou de expires_at sem pagamento. |
| cancelled | Cancelada via charges.cancel() (só permitido enquanto pending). |
| failed | Erro no PSP que inviabilizou a cobrança. |
| refunded | Devolução total processada. |
Idempotência
Reenviar a mesma requisição com a mesma idempotencyKey e mesmo body retorna a cobrança original (não cria duplicata). Bom para retries automáticos.
import { randomUUID } from 'node:crypto';
const charge = await hubpay.charges.create(
{ amount: 5000, provider: 'asaas' },
{ idempotencyKey: randomUUID() },
);Se a mesma key for reusada com um body diferente, a API responde
409 idempotency_conflict.
Smart Routing (provider random)
Distribua cobranças automaticamente entre todos os seus PSPs conectados usando round-robin com fallback automático.
Como funciona
Em vez de escolher um PSP específico, envie provider: "random" e o Hubpay distribui sequencialmente:
- Requisição 1 → Asaas
- Requisição 2 → Mercado Pago
- Requisição 3 → Efí
- Requisição 4 → Asaas (recomeça)
Se o PSP da vez estiver fora do ar, o Hubpay tenta automaticamente o próximo. Seus clientes nunca veem erro de pagamento.
Exemplo
const charge = await hubpay.charges.create({
amount: 5000,
provider: 'random',
description: 'Pedido #1234',
payer: {
name: 'João da Silva',
document: '12345678900',
email: '[email protected]',
},
});
console.log(charge.provider);
// => "mercadopago" (o PSP que efetivamente processou)
console.log(charge.routing);
// => {
// mode: "round_robin",
// selected_provider: "mercadopago",
// attempted_providers: ["mercadopago"]
// }Verificando fallback
Se houve fallback (PSP principal falhou), attempted_providers terá mais de um item:
if (charge.routing && charge.routing.attempted_providers.length > 1) {
console.log('Houve fallback!');
console.log('Tentou:', charge.routing.attempted_providers.join(' → '));
console.log('Funcionou em:', charge.routing.selected_provider);
}Limites de volume por PSP
Quando o PSP selecionado tem routeLimit configurado no dashboard, a resposta inclui routing.volume com o estado atual do ciclo:
console.log(charge.routing?.volume);
// => {
// limit_cents: 100_000_00, // teto do ciclo (R$ 100.000)
// accumulated_cents: 42_500_00, // já processado neste ciclo
// remaining_cents: 57_500_00 // saldo restante até o reset
// }Ao atingir o limite, o roteador pula automaticamente esse PSP no round-robin.
Requisitos
- Pelo menos 2 PSPs ativos conectados no dashboard, no mesmo ambiente (test ou live).
- Com apenas 1 PSP, use o nome do provider diretamente (
"asaas","mercadopago", etc.).
Nota sobre o campo provider na resposta
Quando você usa "random", o campo provider na resposta mostra o PSP que efetivamente processou a cobrança (ex.: "mercadopago"), não "random". O campo routing traz o detalhe do roteamento.
Quando você usa um provider específico, o campo routing não aparece na resposta.
Webhooks
Eventos disponíveis
| Evento | Disparado quando… |
| ------------------- | ---------------------------------------------------------- |
| charge.pending | Cobrança criada. |
| charge.paid | PSP confirmou o pagamento. |
| charge.expired | Cobrança passou de expires_at. |
| charge.cancelled | charges.cancel() foi chamado com sucesso. |
| charge.failed | O PSP retornou erro irrecuperável. |
| charge.refunded | Devolução processada. |
Verificando a assinatura
Cada requisição vem com o header X-Hubpay-Signature: v1=<hmac-sha256-hex>, calculado com o body cru (antes de qualquer parse).
Next.js App Router
export async function POST(req: Request) {
const body = await req.text(); // RAW
const sig = req.headers.get('x-hubpay-signature') ?? '';
const verifier = hubpay.webhooks(process.env.HUBPAY_WEBHOOK_SECRET!);
if (!verifier.verify(body, sig)) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(body);
if (event.type === 'charge.paid') {
// processar pagamento…
}
return new Response('ok');
}Express (raw body obrigatório)
import express from 'express';
const app = express();
app.post(
'/webhooks/hubpay',
express.raw({ type: 'application/json' }), // entrega Buffer cru
(req, res) => {
const sig = req.header('x-hubpay-signature') ?? '';
const verifier = hubpay.webhooks(process.env.HUBPAY_WEBHOOK_SECRET!);
if (!verifier.verify(req.body, sig)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body.toString('utf8'));
// …
res.send('ok');
},
);⚠️ Nunca use o body já parseado (objeto JS) para verificar a assinatura — qualquer reformatação invalida o HMAC.
Tratamento de erros
import { HubpayApiError } from '@hubpay/sdk';
try {
await hubpay.charges.create({ amount: 5000, provider: 'asaas' });
} catch (err) {
if (err instanceof HubpayApiError) {
console.error(err.status); // 400, 401, 402, 409, 502…
console.error(err.error.type); // string — ver tabela abaixo
console.error(err.error.message); // descrição legível
console.error(err.error.param); // campo que originou o erro (quando aplicável)
console.error(err.error.doc_url); // link para a página de docs do erro
}
}Tipos de erro
| err.error.type | HTTP | Significado |
| ------------------------ | :--: | -------------------------------------------------------------------------- |
| invalid_request | 400 | Parâmetro faltando ou inválido. Veja err.error.param. |
| unauthorized | 401 | apiKey ausente, inválida ou revogada. |
| psp_not_configured | 402 | PSP solicitado não tem credencial ativa no ambiente da chave. |
| forbidden | 403 | Chave não autorizada para essa operação. |
| not_found | 404 | ID de cobrança inexistente. |
| idempotency_conflict | 409 | Mesma idempotencyKey reusada com body diferente. |
| invalid_state | 409 | Operação inválida pro status atual (ex.: cancelar uma paid). |
| rate_limit_exceeded | 429 | Excedeu o rate limit. Faça backoff exponencial. |
| internal_error | 500 | Erro no Hubpay. Tente novamente. |
| psp_error | 502 | O PSP de destino retornou erro. Considere fallback (ou use random). |
TypeScript — tipos exportados
import {
Hubpay,
HubpayApiError,
WebhookVerifier,
type HubpayConfig,
type Charge,
type ChargeStatus,
type ChargePix,
type ChargePayer,
type ChargeRouting,
type CreateChargeParams,
type CreateChargeOptions,
type HubpayError,
} from '@hubpay/sdk';| Símbolo | O que é |
| -------------------- | ---------------------------------------------------------------------- |
| Hubpay | Classe principal — instancie uma vez por processo. |
| HubpayApiError | Erro lançado por todas as chamadas HTTP do SDK. |
| WebhookVerifier | Verificador HMAC; também acessível via hubpay.webhooks(secret). |
| HubpayConfig | Tipo de configuração do construtor. |
| Charge | Objeto cobrança completo. |
| ChargeStatus | União com os 6 status possíveis. |
| ChargePix | { copy_paste, qr_code_base64 }. |
| ChargePayer | { name, document, email }. |
| ChargeRouting | Detalhes de smart routing (presente quando provider: "random"). |
| CreateChargeParams | Tipo do body de charges.create(). |
| CreateChargeOptions| Segundo argumento (idempotência). |
| HubpayError | Shape do err.error em HubpayApiError. |
Compatibilidade de runtime
| Runtime | Suportado? | Observação |
| ------------------------------- | :--------------: | --------------------------------------------------------------------- |
| Node.js ≥ 18 | ✅ | fetch nativo, sem polyfill. |
| Bun | ✅ | |
| Deno | ✅ | |
| Vercel Edge / Cloudflare Workers | ⚠️ | Hubpay (HTTP) funciona; WebhookVerifier exige polyfill de node:crypto (ou Web Crypto manual). |
| Browser | ❌ | Nunca exponha apiKey no front-end. Use server-side. |
Links
License
MIT
