Skip to content

rodmarzavala/recurrente-sdk

Repository files navigation

@rodmarzavala/recurrente-sdk

SDK no oficial de TypeScript para la API REST de Recurrente
Hecho en Guatemala para developers guatemaltecos — y para cualquiera que quiera cobrar en quetzales.

npm version npm downloads License: MIT CI TypeScript Hecho en Guatemala

Documentación en español e inglés: rodmarzavala.github.io/recurrente-sdk


¿Por qué este SDK?

Recurrente es la plataforma de pagos y suscripciones líder en Guatemala. Este SDK te da acceso a toda su API desde TypeScript/JavaScript con una experiencia de desarrollo de primer nivel — sin peleas con fetch crudo, sin cobros dobles, sin webhooks inseguros.

Feature Detalle
Edge-first Usa solo Web APIs estándar — funciona en Cloudflare Workers, Vercel Edge, Deno, Bun y Node.js ≥ 18 sin cambios
Zero dependencias fetch nativo + Web Crypto API — nada en dependencies
Seguro por defecto Verificación de webhooks con crypto.subtle.verify (tiempo constante) + protección contra replay attacks (ventana 5 min)
Resiliente Reintentos con exponential backoff para 429 & 5xx · soporte Retry-After · timeout de 30s via AbortController
100% tipado TypeScript estricto en todo — noImplicitAny, sin any
Idempotente Idempotency-Key generado automáticamente y reutilizado en reintentos — sin cobros dobles
Paginación Todos los endpoints de lista retornan Page<T> con helpers pageIterator() y autoPagingToArray()
Developer Experience Incluye un Webhook Forwarder nativo (CLI) para recibir y re-firmar eventos localmente sin usar ngrok. Ahorra horas de setup y depuración en localhost.

Instalación

npm install @rodmarzavala/recurrente-sdk
# o
pnpm add @rodmarzavala/recurrente-sdk
# o
yarn add @rodmarzavala/recurrente-sdk

Requisito mínimo: Node.js ≥ 18, Deno ≥ 1.38, Bun ≥ 1.0, o cualquier runtime con Fetch API y Web Crypto API.


Asistente de Configuración (CLI)

El SDK incluye una herramienta de línea de comandos (CLI) interactiva para configurar tu proyecto en segundos. ¡Genera tus variables de entorno y tu ruta de webhooks (ej. para Next.js o Express) automáticamente!

npx @rodmarzavala/recurrente-sdk init

Webhook Forwarder Local (Efecto Stripe CLI)

Desarrollar webhooks en local no debería ser doloroso. No instales ngrok ni pagues por túneles. La CLI del SDK incluye un forwarder que envía los eventos de Recurrente directo a tu localhost, ¡y refirma criptográficamente el payload para que tu código local no falle al verificar las firmas!

npx @rodmarzavala/recurrente-sdk listen --forward-to http://localhost:3000/api/webhooks/recurrente

Quick Start

import { Recurrente } from "@rodmarzavala/recurrente-sdk";

const recurrente = new Recurrente({
  publicKey: process.env.RECURRENTE_PUBLIC_KEY!,
  secretKey: process.env.RECURRENTE_SECRET_KEY!,
});

// Crea un checkout y redirige al cliente
const checkout = await recurrente.checkouts.create({
  items: [
    {
      name: "Plan Pro",
      amount_in_cents: 29900, // Q299.00
      currency: "GTQ",
      quantity: 1,
    },
  ],
  success_url: "https://tudominio.com/gracias",
  cancel_url: "https://tudominio.com/cancelar",
});

redirect(checkout.checkout_url);

Módulos disponibles

Checkouts

const checkout = await recurrente.checkouts.create({ ... });
const checkout = await recurrente.checkouts.retrieve("ch_abc123");
const page     = await recurrente.checkouts.list({ page: 1, items: 20 });

Subscriptions

const { subscription, checkout_url } = await recurrente.subscriptions.create({ ... });
const sub  = await recurrente.subscriptions.retrieve("su_abc123");
const page = await recurrente.subscriptions.list();
await recurrente.subscriptions.cancel("su_abc123");

Refunds

// Reembolso total
const refund = await recurrente.refunds.create({ checkout_id: "ch_abc123" });
// Reembolso parcial
const partial = await recurrente.refunds.create({ checkout_id: "ch_abc123", amount_in_cents: 5000 });
const page = await recurrente.refunds.list({ checkout_id: "ch_abc123" });

Products

const page    = await recurrente.products.list();
const product = await recurrente.products.retrieve("prod_abc123");

// Puedes pasar `RequestOptions` (idempotencyKey, timeout) como último parámetro en cualquier método
const created = await recurrente.products.create(
  { name: "Plan Pro", ... },
  { idempotencyKey: "req_xyz_123", timeout: 15000 }
);

const updated = await recurrente.products.update("prod_abc123", { name: "Plan Pro v2" });
await recurrente.products.archive("prod_abc123");

Customers

const page     = await recurrente.customers.list();
const customer = await recurrente.customers.retrieve("cus_abc123");
const created  = await recurrente.customers.create({ email: "user@example.com" });

Webhook Endpoints

const endpoint = await recurrente.webhookEndpoints.create({
  url: "https://myapp.com/webhooks/recurrente",
});
console.log(endpoint.signing_secret); // ¡guárdalo — solo se muestra una vez!
await recurrente.webhookEndpoints.delete(endpoint.id);

Paginación

import { pageIterator, autoPagingToArray } from "@rodmarzavala/recurrente-sdk";

// Iterar página por página
for await (const page of pageIterator((p) => recurrente.products.list(p))) {
  page.data.forEach((p) => console.log(p.name));
}

// Obtener todos los registros de una sola vez
const all = await autoPagingToArray((p) => recurrente.customers.list(p));

Verificación de Webhooks

Verifica que los webhooks entrantes son auténticos y obtén tipado fuerte (Discriminated Union) para el evento.

import { RecurrenteWebhooks } from "@rodmarzavala/recurrente-sdk";

try {
  // `constructEvent` verifica la firma y retorna un `RecurrenteEvent` tipado
  const event = await RecurrenteWebhooks.constructEvent(
    rawBody,   // ⚠️ string crudo — NO JSON parseado
    {
      "svix-id":        req.headers["svix-id"],
      "svix-timestamp": req.headers["svix-timestamp"],
      "svix-signature": req.headers["svix-signature"],
    },
    process.env.RECURRENTE_WEBHOOK_SECRET! // "whsec_..."
  );

  switch (event.type) {
    case "checkout.succeeded":
      console.log(`Pagado: ${event.data.amount_in_cents}`); // event.data es CheckoutResponse
      break;
    case "subscription.canceled":
      console.log(`Cancelada: ${event.data.id}`); // event.data es SubscriptionResponse
      break;
  }
} catch (err) {
  return res.status(401).send("Unauthorized");
}

Manejo de errores

import { isRecurrenteError } from "@rodmarzavala/recurrente-sdk";

try {
  await recurrente.checkouts.retrieve("ch_nonexistent");
} catch (err) {
  if (isRecurrenteError(err)) {
    console.error(err.statusCode); // 404
    console.error(err.message);    // mensaje de error de la API
    console.error(err.body);       // body completo del error
  }
}

Reintentos automáticos

El cliente reintenta 429 (rate limit) y 5xx automáticamente con backoff exponencial (max 3 reintentos, cap 30s). Configurable:

const recurrente = new Recurrente({
  publicKey: "...",
  secretKey: "...",
  maxRetries: 5, // o 0 para deshabilitar
});

Documentación

rodmarzavala.github.io/recurrente-sdk — Documentación completa en español e inglés.

Guía Descripción
Inicio Rápido Instalación, primera request, sandbox vs producción
API Reference Todos los métodos, parámetros e interfaces TypeScript
Webhooks Verificación, tipos de eventos, ejemplos por framework
Frameworks Next.js, Astro, React
Docs de Recurrente Documentación oficial de la API de Recurrente

Compatibilidad

Runtime Versión mínima Estado
Node.js 18.0.0 ✅ Soportado
Cloudflare Workers Cualquiera ✅ Soportado
Vercel Edge Functions Cualquiera ✅ Soportado
Deno 1.38.0 ✅ Soportado
Bun 1.0.0 ✅ Soportado
Browser Moderno (ES2022+) ✅ Soportado

Contribuir

¡Todas las contribuciones son bienvenidas! Ya sea un fix de bug, un módulo nuevo, o una typo en los docs.

Lee CONTRIBUTING.md para empezar.

git clone https://github.com/rodmarzavala/recurrente-sdk.git
cd recurrente-sdk
npm install
npm test          # 31 tests, todos deben pasar
npm run typecheck # cero errores

License

MIT — ver LICENSE.


Disclaimer: Este es un proyecto open-source independiente y no está oficialmente afiliado ni respaldado por Recurrente.

About

Unofficial TypeScript SDK for Recurrente — payment gateway for Guatemala and Central America

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors