Node.js · @cauril/node
El cliente oficial para tu backend. Tipado de punta a punta, idempotente por diseño, con reintentos automáticos, auto-paginación y verificación de firmas de webhook. Cero dependencias en runtime.
Instalación
Requiere Node.js ≥ 20 (usa el fetch global). Funciona en ESM y CommonJS.
npm install @cauril/nodeInicialización
Pasá tu API key (sk_test_… o sk_live_…). El modo lo determina la key, no la URL. Nunca la pongas en el browser ni la subas al repo.
import { Cauril } from "@cauril/node";
const cauril = new Cauril(process.env.CAURIL_API_KEY);Opciones
const cauril = new Cauril(process.env.CAURIL_API_KEY, {
baseURL: "https://api.cauril.com", // default
apiVersion: "2026-06-01", // pinea el header Cauril-Version
maxRetries: 2, // reintentos en red/429/5xx (hasta 3 intentos)
timeout: 60_000, // timeout por intento, en ms
retryInitialDelayMs: 500, // primer backoff (duplica por intento, con jitter)
// fetch, // inyectá un fetch propio (tests / runtimes sin global)
});| Opción | Default | Qué hace |
|---|---|---|
baseURL | https://api.cauril.com | URL base de la API. |
apiVersion | 2026-06-01 | Fija el header Cauril-Version. |
maxRetries | 2 | Reintentos ante error de red, 429 y 5xx (respeta Retry-After). |
timeout | 60000 | Timeout por intento, en ms. |
retryInitialDelayMs | 500 | Primer paso de backoff; duplica por intento, con full jitter. |
fetch | global | Implementación de fetch a inyectar. |
Tu primer pago
Los métodos mutantes aceptan un segundo argumento con opciones por llamada. idempotencyKey es lo que hace que reintentar sea seguro: pasá un id estable (por ejemplo el id de tu orden) y la operación corre a lo sumo una vez.
const payment = await cauril.payments.create(
{
amount: 5000, // entero en unidad mínima → USD 50.00
currency: "USD",
payment_method: { type: "card", token: "pm_card_visa" },
},
{ idempotencyKey: order.id },
);
console.log(payment.id, payment.status); // pay_… "processing"idempotencyKey, el SDK genera uno fresco por llamada (un randomUUID). Eso te cubre de un reintento interno de red, pero no de que tu propio código llame dos veces. Para eso pasá una key estable. Ver Idempotencia.Opciones por llamada
Todo método acepta un último argumento RequestOptions:
| Campo | Qué hace |
|---|---|
idempotencyKey | Sobreescribe la key autogenerada (solo mutaciones). |
expand | Relaciones a inlinear, ej. ["attempts", "customer"]. |
signal | AbortSignal para cancelar la request. |
Recursos
Cada recurso es una propiedad del cliente. Los nombres de campo son los mismos que la API (snake_case), sin capa de traducción sorpresa.
| Recurso | Métodos |
|---|---|
payments | create, retrieve, list, listAutoPaging, refund |
refunds | retrieve |
customers | create, retrieve, list, listAutoPaging |
providerConnections | create, list |
routingRules | create, list, update, del |
webhookEndpoints | create, list |
webhookDeliveries | list, listAutoPaging, retrieve, resend |
events | list, listAutoPaging |
analytics | summary |
checkout.sessions | create, retrieve, cancel |
plans | create, retrieve, list, listAutoPaging |
subscriptions | create, retrieve, list, listAutoPaging, update, cancel |
invoices | retrieve, list, listAutoPaging |
webhooks | constructEvent, verifySignature, sign |
Paginación automática
Las listas devuelven una página (data + has_more + next_cursor). Para recorrer todo sin manejar el cursor a mano, usá listAutoPaging, que es un async-iterator y va trayendo páginas por demanda:
for await (const payment of cauril.payments.listAutoPaging({ status: "succeeded" })) {
console.log(payment.id);
}Verificar webhooks
cauril.webhooks no necesita API key: usa el secreto del endpoint (whsec_…). Pasá el cuerpo crudo de la request (nunca un objeto re-serializado) o la firma no va a coincidir. Lanza CaurilSignatureVerificationError ante cualquier fallo (firma faltante, malformada, vencida o que no coincide).
// Ejemplo con Express (body crudo). El secreto se devuelve UNA sola vez al crear el endpoint.
app.post("/webhooks/cauril", express.raw({ type: "*/*" }), (req, res) => {
try {
const event = cauril.webhooks.constructEvent(
req.body, // Buffer/string crudo
req.headers["cauril-signature"],
process.env.CAURIL_WEBHOOK_SECRET, // whsec_…
);
if (event.type === "payment.succeeded") fulfill(event.data);
res.sendStatus(200);
} catch {
res.sendStatus(400); // firma inválida → no confíes en el payload
}
});verifySignature(...) hace lo mismo sin parsear, y sign(rawBody, secret) produce un header válido, práctico para testear tu propio handler. Detalle del formato de la firma en Webhooks.Errores
Todo lo que lanza el SDK extiende CaurilError, así un solo catch te cubre. Usá CaurilError.is(e) o instanceof para distinguir el tipo.
import { CaurilError, CaurilAPIError, CaurilConnectionError } from "@cauril/node";
try {
await cauril.payments.create(params, { idempotencyKey: order.id });
} catch (e) {
if (e instanceof CaurilAPIError) {
// respuesta estructurada de la API (4xx/5xx)
console.error(e.statusCode, e.type, e.code, e.message, e.requestId);
if (e.isProviderError) console.error(e.providerError); // un PSP rechazó
} else if (e instanceof CaurilConnectionError) {
// no se pudo llegar a la API (DNS/TCP/TLS, timeout o abort)
} else if (CaurilError.is(e)) {
// CaurilSignatureVerificationError u otro del SDK
}
}| Clase | Cuándo |
|---|---|
CaurilAPIError | La API devolvió { error } (4xx/5xx). Trae statusCode, type, code, param, requestId, providerError, docUrl. |
CaurilConnectionError | No se pudo llegar a la API: fallo de red, timeout o request abortada. |
CaurilSignatureVerificationError | Una firma de webhook no verificó. |
CaurilError | Clase base abstracta de todas las anteriores. |
429 y 5xx con backoff y jitter. Como toda mutación lleva una Idempotency-Key, reintentar es seguro: nunca cobra dos veces (regla dura #4).Tipos
El paquete exporta todos los tipos de la wire (Payment, Refund, CheckoutSession, CaurilEvent, PaymentStatus, …) y los tipos de parámetros de cada método (CreatePaymentParams, ListPaymentsParams, …). Importalos directo de @cauril/node.
