Setup
Instale @zhex/node ou monte um helper fetch reutilizável. Variáveis de ambiente, base URL e headers padrão.
Esta página é o boilerplate canônico que você cola no projeto. Depois daqui, todos os exemplos da doc rodam sem mudança.
Dois caminhos paralelos: o SDK oficial (recomendado em Node) e o zhexFetch helper (se você prefere HTTP puro ou está fora de Node).
Variáveis de ambiente
# .env (NUNCA commitar)
ZHEX_SECRET_KEY=zsk_test_abcd1234efgh5678
ZHEX_WEBHOOK_SECRET=whsec_test_xyzNunca commite chaves
zsk_* e whsec_* em .env (gitignored). Para CI/produção, use o secret manager do seu cloud (AWS Secrets Manager, GCP Secret Manager, Vercel/Fly env). A Zhex revoga automaticamente chaves detectadas em commits públicos via GitHub Secret Scanning.
Caminho 1 — SDK oficial
npm install @zhex/node// lib/zhex.ts
import Zhex from '@zhex/node';
if (!process.env.ZHEX_SECRET_KEY) {
throw new Error('ZHEX_SECRET_KEY ausente');
}
export const zhex = new Zhex(process.env.ZHEX_SECRET_KEY);// uso
import { zhex } from '@/lib/zhex';
const intent = await zhex.paymentIntents.create({
amount: 19900,
currency: 'brl',
customer: 'cus_…',
});Opções do construtor
const zhex = new Zhex(process.env.ZHEX_SECRET_KEY!, {
apiBase: 'http://localhost:3333', // dev local; default https://prometheus.zhex.io
zhexVersion: '2026-04-25', // pin de versão; default = ZHEX_API_VERSION_DATE
maxRetries: 3, // 0 desliga retry; default 3
timeoutMs: 80_000, // default 80s; pense em charges com 3DS
});Opções por chamada
await zhex.refunds.create(
{ payment_intent: 'pi_…' },
{
idempotencyKey: 'refund:order-42', // SDK gera UUID se você omitir
timeoutMs: 30_000,
maxRetries: 1,
},
);Caminho 2 — Helper fetch
Quando você não quer dependência ou está fora de Node:
// lib/zhex-fetch.ts
import { randomUUID } from 'node:crypto';
const BASE_URL = process.env.ZHEX_BASE_URL ?? 'https://prometheus.zhex.io';
const API_VERSION = '2026-04-25';
const SECRET = process.env.ZHEX_SECRET_KEY!;
if (!SECRET) throw new Error('ZHEX_SECRET_KEY ausente');
type ZhexInit = RequestInit & {
idempotencyKey?: string;
query?: Record<string, string | number | boolean | undefined>;
};
export class ZhexError extends Error {
constructor(
public status: number,
public details: { type: string; code: string; message: string; param?: string },
public requestId: string | null,
) {
super(`[${details.code}] ${details.message}`);
this.name = 'ZhexError';
}
}
export async function zhexFetch<T = unknown>(
path: string,
init: ZhexInit = {},
): Promise<T> {
const { idempotencyKey, query, headers, ...rest } = init;
const qs = query
? '?' +
Object.entries(query)
.filter(([, v]) => v !== undefined)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
.join('&')
: '';
const method = (rest.method ?? 'GET').toUpperCase();
const isMutating = method === 'POST' || method === 'DELETE';
const res = await fetch(`${BASE_URL}${path}${qs}`, {
...rest,
headers: {
Authorization: `Bearer ${SECRET}`,
'Zhex-Version': API_VERSION,
...(isMutating && { 'Idempotency-Key': idempotencyKey ?? randomUUID() }),
...(rest.body && !(rest.body instanceof FormData) && {
'Content-Type': 'application/json',
}),
...headers,
},
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
throw new ZhexError(res.status, err.error, res.headers.get('request-id'));
}
return res.json() as Promise<T>;
}Uso
import { zhexFetch } from '@/lib/zhex-fetch';
// Criar PaymentIntent
const intent = await zhexFetch<{ id: string; status: string }>(
'/v1/payment_intents',
{
method: 'POST',
body: JSON.stringify({
amount: 19900,
currency: 'brl',
customer: 'cus_…',
}),
},
);
// Listar com paginação
const list = await zhexFetch<{ data: any[]; has_more: boolean }>(
'/v1/payment_intents',
{ query: { limit: 100 } },
);Versão da API
A Zhex versiona em dois eixos:
- Major na URL (
/v1/...) — só muda em quebras radicais. Hoje semprev1. - Minor por header (
Zhex-Version: 2026-04-25) — datas. Mudanças não-breaking saem em datas novas, sua chave fica pinada na data que você escolher.
Sempre pin explícito em produção:
// SDK: passe no construtor
new Zhex(SECRET, { zhexVersion: '2026-04-25' });
// fetch: passe no header
headers['Zhex-Version'] = '2026-04-25';Configurações que você controla
| Configuração | Onde (SDK) | Onde (HTTP) | Quando ajustar |
|---|---|---|---|
Zhex-Version | zhexVersion no construtor | Zhex-Version header | Pin explícito em prod, atualiza em janela controlada |
| Timeout | timeoutMs no construtor ou call | AbortSignal.timeout(ms) no fetch | Reduzir em endpoints user-facing (5s); aumentar em workers de billing (30-80s) |
| Retry | maxRetries (default 3) | Helper próprio (ver Retry) | SDK já cobre; em HTTP, sempre passar Idempotency-Key reutilizada |
| HTTPS proxy / cert | fetch custom no construtor | undici.Agent ou dispatcher no fetch | Ambientes corporativos |
| Telemetria | Sentry/Datadog em try/catch | OTel auto-instrumenta fetch em Node 20+ | Sempre |
Boas práticas
- Helper único. Não espalhe
fetch(ounew Zhex(...)) pelo código — exporte um singleton e reuse. - Erro tipado. Cape
code(não só status).card_declined,insufficient_funds,idempotency_key_in_usesão tratáveis diferente. request-idem log. Cada response temRequest-Idno header (eerror.requestIdno SDK). Log junto do seu próprio request id pra suporte.- Timeout sempre. SDK tem default 80s; em
fetchpuro useAbortSignal.timeout.
Atualizado em