@turbofy/sdk
v1.0.1
Published
SDK oficial do Gateway Turbofy para Node.js/TypeScript - Integração Pix simplificada
Maintainers
Readme
@turbofy/sdk
SDK oficial do Gateway Turbofy para Node.js/TypeScript
Integre pagamentos Pix de forma simples, segura e type-safe.
Documentação Completa • Changelog • Suporte
🚀 Instalação
npm install @turbofy/sdk
# ou
yarn add @turbofy/sdk
# ou
pnpm add @turbofy/sdk⚡ Quickstart
Crie sua primeira cobrança Pix em menos de 1 minuto:
import { createTurbofyClient } from "@turbofy/sdk";
// Criar cliente
const client = createTurbofyClient({
credentials: {
clientId: process.env.TURBOFY_CLIENT_ID!,
clientSecret: process.env.TURBOFY_CLIENT_SECRET!,
},
});
// Criar cobrança Pix
const charge = await client.pix.createCharge({
amountCents: 10000, // R$ 100,00 (mínimo: R$ 5,00 = 500 centavos)
description: "Pedido #123",
});
// QR Code em base64 (PNG) - renderize diretamente em <img>
const qrCodeUrl = `data:image/png;base64,${charge.pix.qrCode}`;
// Pix copia e cola
console.log(charge.pix.copyPaste);Pronto! Em apenas essas linhas você já tem uma cobrança Pix funcional com QR Code e copia-e-cola.
📚 Documentação
Recursos Principais
✅ Cobranças Pix
// Criar cobrança
const charge = await client.pix.createCharge({
amountCents: 10000, // Obrigatório: valor em centavos (mínimo: 500 = R$ 5,00)
description: "Pedido #123", // Opcional: descrição
expiresAt: "2024-12-31", // Opcional: expiração (ISO 8601)
externalRef: "order:123", // Opcional: referência do seu sistema
metadata: { orderId: 123 },// Opcional: dados customizados
});
// Consultar cobrança
const status = await client.pix.getCharge({ id: charge.id });
console.log(status.status); // "PENDING" | "PAID" | "EXPIRED" | "CANCELLED" | "FAILED"🔔 Webhooks
import { verifyWebhookSignature, parseWebhookPayload } from "@turbofy/sdk";
import express from "express";
const app = express();
// IMPORTANTE: use express.raw() para receber o body como string
app.post("/webhooks/turbofy", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["turbofy-signature"] as string;
const rawBody = req.body.toString();
// Verificar assinatura HMAC
const result = verifyWebhookSignature({
signature,
rawBody,
secret: process.env.TURBOFY_WEBHOOK_SECRET!,
});
if (!result.valid) {
return res.status(401).json({ error: result.reason });
}
// Processar payload
const payload = parseWebhookPayload(rawBody);
if (payload.type === "charge.paid") {
console.log("Cobrança paga:", payload.data.id);
// Atualizar pedido no seu sistema
}
res.json({ received: true });
});⚠️ Tratamento de Erros
import {
TurbofyApiError,
TurbofyNetworkError,
TurbofyValidationError,
isTurbofyApiError,
} from "@turbofy/sdk";
try {
await client.pix.createCharge({ amountCents: -100 });
} catch (error) {
// Erro de validação local (Zod)
if (error instanceof TurbofyValidationError) {
console.log("Dados inválidos:", error.fieldErrors);
// { amountCents: ["amountCents deve ser positivo"] }
}
// Erro da API (HTTP 4xx/5xx)
if (error instanceof TurbofyApiError) {
console.log("Código:", error.code); // "VALIDATION_ERROR"
console.log("Mensagem:", error.message); // "Dados inválidos"
console.log("HTTP Status:", error.statusCode); // 400
}
// Erro de rede (timeout, DNS, conexão)
if (error instanceof TurbofyNetworkError) {
console.log("Erro de rede:", error.message);
console.log("É timeout?", error.isTimeout);
console.log("Retriável?", error.retryable);
}
}⚙️ Configuração Avançada
import { createTurbofyClient } from "@turbofy/sdk";
import pino from "pino";
const client = createTurbofyClient({
// URL base (default: https://api.turbofy.com.br)
baseUrl: "https://sandbox.turbofy.com.br",
credentials: {
clientId: process.env.TURBOFY_CLIENT_ID!,
clientSecret: process.env.TURBOFY_CLIENT_SECRET!,
},
// Timeout em ms (default: 30000)
timeoutMs: 15000,
// Configuração de retry
retry: {
maxAttempts: 5, // default: 3
initialDelayMs: 2000, // default: 1000
backoffMultiplier: 2, // default: 2
maxJitterMs: 1000, // default: 500
},
// Logger customizado
logger: pino({ level: "debug" }),
// Hooks de observabilidade
hooks: {
onRequest: (ctx) => {
console.log(`[${ctx.method}] ${ctx.url} (tentativa ${ctx.attempt})`);
},
onResponse: (ctx) => {
metrics.recordLatency("turbofy_api", ctx.durationMs);
},
onError: (ctx) => {
sentry.captureException(ctx.error);
},
},
});🧪 Helpers de Teste
import { createMockClient, createWebhookSignature, fixtures } from "@turbofy/sdk/testing";
describe("Meu serviço de pagamento", () => {
const client = createMockClient();
beforeEach(() => {
client.pix.createCharge.mockClear();
});
it("deve criar cobrança", async () => {
// Configurar mock
client.pix.createCharge.mockResolvedValueOnce(fixtures.pixCharge.success);
// Chamar serviço
const result = await meuServico.cobrar(100);
// Verificar
expect(client.pix.createCharge.calls).toHaveLength(1);
expect(client.pix.createCharge.calls[0][0].amountCents).toBe(10000);
});
it("deve validar webhook", () => {
const { signature, rawBody } = createWebhookSignature({
payload: fixtures.webhook.chargePaid,
secret: fixtures.webhookSecret,
});
const result = verifyWebhookSignature({
signature,
rawBody,
secret: fixtures.webhookSecret,
});
expect(result.valid).toBe(true);
});
});🎯 Casos de Uso
E-commerce
// Criar cobrança para um pedido
const charge = await client.pix.createCharge({
amountCents: order.total * 100,
description: `Pedido #${order.id}`,
externalRef: `order:${order.id}`,
metadata: {
orderId: order.id,
customerId: order.customerId,
items: order.items.map(i => ({ id: i.id, quantity: i.quantity })),
},
});
// Enviar QR Code para o cliente
await sendEmail(order.customerEmail, {
subject: "Pagamento do seu pedido",
qrCode: charge.pix.qrCode,
copyPaste: charge.pix.copyPaste,
});Assinaturas
// Criar cobrança recorrente
const charge = await client.pix.createCharge({
amountCents: subscription.monthlyPrice,
description: `Assinatura ${subscription.planName} - ${new Date().toLocaleDateString('pt-BR')}`,
externalRef: `subscription:${subscription.id}:${Date.now()}`,
metadata: {
subscriptionId: subscription.id,
planId: subscription.planId,
billingCycle: "monthly",
},
});Split de Pagamentos
// Criar cobrança com split automático
const charge = await client.pix.createCharge({
amountCents: 10000,
description: "Venda com comissão",
splits: [
{ merchantId: "produtor-id", percentage: 90 },
{ merchantId: "afiliado-id", percentage: 10 },
],
});🔒 Segurança
⚠️ Importante
- Nunca exponha
clientSecretno frontend - O SDK é para uso server-to-server (S2S) apenas - O SDK nunca loga o
clientSecretou outros dados sensíveis - Webhooks são verificados com HMAC-SHA256 para prevenir ataques
- Retries são seguros: POSTs usam idempotency keys automaticamente
- Proteção contra replay attacks com tolerância configurável (default: 5 minutos)
Boas Práticas
// ✅ CORRETO: Usar variáveis de ambiente
const client = createTurbofyClient({
credentials: {
clientId: process.env.TURBOFY_CLIENT_ID!,
clientSecret: process.env.TURBOFY_CLIENT_SECRET!,
},
});
// ❌ ERRADO: Hardcode de credenciais
const client = createTurbofyClient({
credentials: {
clientId: "abc123",
clientSecret: "secret456", // NUNCA faça isso!
},
});📖 API Reference
createTurbofyClient(options)
Cria um cliente Turbofy.
Parâmetros:
options.baseUrl(opcional): URL base da API (default:https://api.turbofy.com.br)options.credentials.clientId(obrigatório): Client ID da sua contaoptions.credentials.clientSecret(obrigatório): Client Secret da sua contaoptions.timeoutMs(opcional): Timeout em milissegundos (default:30000)options.retry(opcional): Configuração de retryoptions.logger(opcional): Logger customizadooptions.hooks(opcional): Hooks de observabilidade
Retorna: TurbofyClient
client.pix.createCharge(input)
Cria uma nova cobrança Pix.
Parâmetros:
input.amountCents(obrigatório): Valor em centavos (mínimo: 500)input.description(opcional): Descrição da cobrançainput.expiresAt(opcional): Data de expiração (ISO 8601)input.externalRef(opcional): Referência externainput.metadata(opcional): Dados customizados (JSON)input.splits(opcional): Array de splits de pagamento
Retorna: Promise<CreatePixChargeResult>
client.pix.getCharge(input)
Consulta uma cobrança Pix existente.
Parâmetros:
input.id(obrigatório): ID da cobrança
Retorna: Promise<GetPixChargeResult>
verifyWebhookSignature(options)
Verifica a assinatura de um webhook.
Parâmetros:
options.signature(obrigatório): Headerturbofy-signatureoptions.rawBody(obrigatório): Body raw da requisição (string)options.secret(obrigatório): Webhook secretoptions.toleranceMs(opcional): Tolerância para replay attacks (default:300000)
Retorna: VerifyWebhookResult
parseWebhookPayload(rawBody)
Faz parse do payload de webhook.
Parâmetros:
rawBody(obrigatório): Body raw da requisição (string)
Retorna: WebhookPayload
🛠️ Requisitos
- Node.js: 18.0.0 ou superior
- TypeScript: 5.0.0 ou superior (opcional, mas recomendado)
📦 Dependências
zod: ^3.23.8 (validação de dados)
O SDK usa apenas fetch nativo do Node.js 18+, sem dependências pesadas.
🔗 Links
🤝 Contribuindo
Contribuições são bem-vindas! Por favor, leia nosso guia de contribuição antes de enviar PRs.
📄 Licença
MIT © Turbofy
Feito com ❤️ pela equipe Turbofy
Documentação • Blog • Twitter
