@boelabs/billete-sdk

中文文档
10.0.0发布于 2026/11/10MITTypeScriptES Module零依赖近 30 天下载 2666

Billete SDK: server client, browser client and webhook verification

原始使用文档
文档目录13 节
  1. Contenido
  2. Instalación
  3. Inicio rápido
  4. Configuración
  5. Recursos
  6. Ejemplo: plan con cuotas
  7. Montos
  8. Paginación
  9. Errores
  10. Idempotencia y reintentos
  11. Navegador
  12. Webhooks
  13. Licencia

Billete SDK

SDK oficial de TypeScript para la API de Billete.

Cobros con Pago Móvil, precios en bolívares o en divisas a tasa BCV y suscripciones, desde tu servidor y tu frontend.

npm Licencia: MIT Tipos incluidos Sin dependencias


  • Tipado de punta a punta: cada recurso, entrada y evento tiene su tipo.
  • Sin dependencias: solo fetch y Web Crypto; funciona en Node.js 20+, Bun, Deno y runtimes edge (Cloudflare Workers, Vercel). El cliente de navegador también funciona en React Native y Expo.
  • Seguro ante reintentos: cada mutación lleva una clave de idempotencia, así un reintento nunca cobra dos veces.

Contenido

Instalación

npm install @boelabs/billete-sdk

Cada versión del SDK habla una sola versión de la API, la que pide su opción apiVersion: una versión mayor nueva del SDK es una versión nueva de la API. Las versiones beta de la API tienen su SDK en @boelabs/billete-sdk@beta.

El paquete tiene tres puntos de entrada:

ImportDónde se usaPara qué
@boelabs/billete-sdkServidorToda la API, con tu clave secreta
@boelabs/billete-sdk/clientNavegadorCheckout y estado de un cliente, sin clave secreta
@boelabs/billete-sdk/webhooksServidorVerificar la firma de los webhooks

Inicio rápido

Crea una clave secreta en el dashboard (Desarrolladores → Claves API) y guárdala en BILLETE_API_KEY.

import { createBillete } from "@boelabs/billete-sdk";

const billete = createBillete({ apiVersion: "2026-11-10", environment: "test" });

const checkout = await billete.checkouts.create({
  plan: "plan_…",
  months: 1,
  customer: { email: "[email protected]", externalId: "user_123" },
  // {CHECKOUT_ID} se reemplaza por el ID del checkout.
  successUrl: "https://tuapp.com/gracias?checkout={CHECKOUT_ID}",
  cancelUrl: "https://tuapp.com/planes",
});

// Redirige a tu cliente a checkout.url para que pague.

Cuando el pago se confirma, Billete te avisa con el webhook checkout.completed (ver Webhooks).

Los productos físicos habilitan sus formas de entrega con fulfillmentMethods, por ejemplo ["pickup", "shipping"]; la lista está vacía por defecto. Cada método también debe estar activo en la configuración del negocio. Desactivarlo conserva la selección de los productos. El checkout ofrece «Acordar con el negocio» cuando los productos físicos tienen métodos habilitados y activos en el negocio. Sin entrega habilitada, no exige datos para entregarla; respeta los que el enlace solicite expresamente. En carritos, solo ofrece los demás métodos que todos los productos físicos admitan.

Configuración

const billete = createBillete({
  apiVersion: "2026-11-10",
  environment: "production",
  apiKey: process.env.BILLETE_API_KEY,
  timeoutMs: 30_000,
  maxRetries: 2,
});
OpciónPor defectoDescripción
environment— (obligatoria)"test" o "production". Debe coincidir con la clave.
apiKeyBILLETE_API_KEYClave secreta: sk_test_… o sk_prod_….
baseUrlBILLETE_BASE_URL, o https://api.billete.runURL de la API.
timeoutMs30000Tiempo máximo por intento.
maxRetries2Reintentos ante errores de red, 429 y 5xx.
fetchglobalThis.fetchImplementación de fetch propia (proxies, tests).

Si environment no coincide con el prefijo de la clave, createBillete lanza un error: una clave de prueba nunca opera en producción ni al revés. Por la misma razón, el cliente de servidor se niega a crearse en un navegador.

[!WARNING] La clave secreta da acceso completo a tu negocio. Úsala solo en el servidor y nunca la incluyas en código que llegue al navegador.

Recursos

Todos los métodos devuelven promesas con el recurso tipado. Los recursos con listado tienen create, retrieve, update, list e iterate, además de sus acciones propias:

RecursoAcciones propias
customers—
customerSessionscreate
portalSessionscreate (enlace al portal del cliente, válido 60 minutos)
productsdelete, deleteVariant
plansdelete
featuresdelete, check, isEnabled, listForCustomer, consume, release, set
couponsdelete
checkoutsexpire
paymentLinksdelete
invoicesvoid (cobros: renovaciones y cobros sueltos, con página de pago en url)
subscriptionscancel, resume
paymentsrefund, listRefunds
fulfillmentsdeliver, submitTracking (entregas de productos físicos; también retrieve, list e iterate)
webhookEndpointsdelete
eventsretrieve, list, iterate
exchangeRateslist
currencieslist

Ejemplo: plan con cuotas

await billete.features.create({
  key: "tokens",
  name: "Tokens",
  type: "metered",
  unit: "tokens",
  reset: { every: "billing" },
});

const plan = await billete.plans.create({
  name: "Pro",
  currency: "USD",
  prices: [
    { months: 1, amount: 1_000 },
    { months: 12, amount: 10_000 },
  ],
  features: { tokens: 100_000 },
});

// `customer` acepta el ID de Billete (cus_…) o tu propio externalId.
const { allowed } = await billete.features.consume("user_123", "tokens", 850);

consume es atómico: si el consumo supera el límite, no se descuenta nada y allowed es false.

Montos

Los montos son siempre enteros en unidad menor (céntimos), nunca decimales: 1_000 en USD son 10,00 USD y 1_000 en VES son 10,00 Bs. Las tasas de cambio llegan como texto decimal para no perder precisión.

Paginación

list devuelve una página ({ data, nextCursor }); iterate recorre todas por ti:

const page = await billete.payments.list({ limit: 50 });
const next = await billete.payments.list({ limit: 50, cursor: page.nextCursor ?? undefined });

for await (const payment of billete.payments.iterate()) {
  console.log(payment.id, payment.amount);
}

Toda lista acepta un rango de creación (createdFrom, createdTo, ISO 8601, inclusivos) y, donde tiene sentido, search: los pagos y los cobros por su cliente, los productos y planes por nombre, los cupones por código.

const october = await billete.payments.list({
  status: "succeeded",
  search: "[email protected]",
  createdFrom: "2026-10-01T04:00:00Z",
  createdTo: "2026-11-01T03:59:59Z",
});

Errores

Toda respuesta fallida lanza un BilleteError:

import { BilleteError } from "@boelabs/billete-sdk";

try {
  await billete.payments.refund("pay_…");
} catch (error) {
  if (error instanceof BilleteError && error.code === "payment_not_refundable") {
    // …
  }
  throw error;
}
PropiedadDescripción
typeCategoría: invalid_request, authentication, permission, not_found, conflict, validation, rate_limit, provider, internal o network.
codeCódigo estable, apto para decidir en tu código: uno del catálogo (BilleteErrorCode), o network_error / invalid_response del SDK.
messageDescripción legible del error.
statusCódigo HTTP (0 si no hubo respuesta).
paramCampo que causó el error, cuando aplica.
requestIdIdentificador de la petición; inclúyelo al pedir soporte.

Idempotencia y reintentos

Cada mutación del cliente de servidor envía una Idempotency-Key generada automáticamente, que se mantiene entre reintentos. Si quieres que un reintento tuyo (por ejemplo, de un job) también sea seguro, pasa una clave estable:

await billete.payments.refund("pay_…", {}, { idempotencyKey: `refund-${orderId}` });

Todos los métodos aceptan como último argumento { idempotencyKey?, signal? }; signal permite cancelar la petición con un AbortController.

Navegador

El cliente de navegador nunca usa tu clave secreta. Tu servidor crea una sesión de cliente de corta duración y se la entrega al frontend:

// Servidor
const session = await billete.customerSessions.create({ customer: "user_123" });
return { token: session.token };
// Navegador
import { createClient } from "@boelabs/billete-sdk/client";

const billete = createClient({ apiVersion: "2026-11-10", customerSession: token });

const { data: subscriptions } = await billete.customer.subscriptions();
const tokens = await billete.customer.feature("tokens");

Para no construir esa pantalla, envía a tu cliente al portal alojado:

const portal = await billete.portalSessions.create({
  customer: "user_123",
  returnUrl: "https://tuapp.com/cuenta",
});
// Redirige a portal.url: allí ve sus suscripciones y cobros, cambia su ciclo o cancela.

customerSession también acepta una función, útil cuando el token se renueva. Sin sesión, el cliente puede leer y pagar checkouts, cobros y links de pago, y consultar tasas y monedas.

El cliente de navegador solo necesita fetch, AbortController y setTimeout, así que funciona igual en React Native y Expo, sin polyfills. No envía Idempotency-Key: cada mutación de /v1/client ya rechaza su repetición por el estado de lo que cambia (un checkout con un pago en curso responde payment_in_progress), así que reintentar es seguro.

Para saber cuándo se confirma un pago, espera el siguiente cambio en vez de consultar en bucle: waitForChange responde en cuanto el checkout cambia, o el mismo checkout si en 25 segundos no pasó nada.

const next = await billete.checkouts.waitForChange(checkout);

Webhooks

Billete firma los webhooks con Standard Webhooks. Verifica la firma con el cuerpo exactamente como llegó, antes de parsearlo:

import { verifyWebhook, WebhookVerificationError } from "@boelabs/billete-sdk/webhooks";

export async function POST(request: Request) {
  try {
    const event = await verifyWebhook({
      payload: await request.text(),
      headers: request.headers,
      secret: process.env.BILLETE_WEBHOOK_SECRET!,
    });

    if (event.type === "checkout.completed") {
      // event.data es un Checkout
    }
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(null, { status: 400 });
    throw error;
  }
}

event es una unión discriminada por type, así que event.data tiene el tipo correcto en cada rama. Se rechazan las firmas con más de 5 minutos (toleranceSeconds).

Licencia

MIT

版本与发布记录

@boelabs/billete-sdk 共发布 34 个版本,首次发布于 2026/09/29;过去 12 个月发布了 34 个版本。

版本发布日期距今
10.0.0latest2026/11/10今天
10.0.0-beta.2beta2026/11/091 天前
10.0.0-beta.12026/11/073 天前
9.2.02026/11/064 天前
9.1.02026/11/046 天前
9.0.02026/10/2417 天前

关于 @boelabs/billete-sdk 的常见问题

@boelabs/billete-sdk 是什么?

Billete SDK: server client, browser client and webhook verification

如何安装 @boelabs/billete-sdk?

在项目目录运行 npm install @boelabs/billete-sdk,或使用 pnpm add / yarn add / bun add。

@boelabs/billete-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@boelabs/billete-sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@boelabs/billete-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@boelabs/billete-sdk 可以商用吗?

@boelabs/billete-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@boelabs/billete-sdk 的下载量有多少?

根据 npm 官方下载统计,@boelabs/billete-sdk 在 2026/10/09 – 2026/11/07 这 30 天内被下载 2,666 次,日均约 89 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@boelabs/billete-sdk 还在维护吗?

最新版本 10.0.0 发布于 2026/11/10(今天);至今共发布 34 个版本,其中过去 12 个月发布了 34 个;首次发布于 2026/09/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。