Introducción
Lemon Squeezy te puso a vender rápido: checkout hosteado, impuestos resueltos, cero tarea de compliance. Pero desde la adquisición por Stripe venís mirando el roadmap de reojo, tus necesidades de billing superaron a los variants y usage records, y el único SDK oficial es de JavaScript.
Commet también es Merchant of Record, así que impuestos, compliance, refunds y payouts siguen fuera de tu plato. Lo que ganás es un modelo de billing construido para productos SaaS y de IA: planes como fuente de verdad, metering de uso nativo y SDKs en cinco lenguajes.
Saltá directo a cualquier sección:
- Historia
- Conceptos
- SDKs oficiales
- Crear customers y subscriptions vía API
- Medir uso / metering
- Webhooks
- Impuestos y compliance
- Precios
- Conclusión
Historia
Lemon Squeezy se lanzó en 2021 como Merchant of Record para productos digitales — licencias de software, descargas, membresías y subscriptions de SaaS — con un foco fuerte en desarrolladores indie que querían vender globalmente sin tocar compliance fiscal. En julio de 2024, Stripe adquirió Lemon Squeezy, y sus capacidades de Merchant of Record empezaron a alimentar el roadmap de Stripe.
Commet aplica el mismo modelo de Merchant of Record a un segmento específico: productos SaaS y de IA que cobran por consumo. Los planes definen precios y features, las subscriptions facturan solas, y los eventos de uso alimentan cobros medidos, por créditos o por balance, con pricing en moneda local para mercados donde el checkout en USD lastima la conversión.
Como las dos plataformas son Merchant of Record, la cobertura fiscal se traslada sin cambios: la comparación que importa es superficie de API, profundidad de metering y fees. De eso trata el resto de esta guía.
Conceptos
Las dos plataformas son Merchant of Record, así que el modelo de vendedor de registro se traslada. Los objetos de billing mapean así:
| Lemon Squeezy | Commet |
|---|---|
| Store | Tu organización |
| Product + Variant | Plan (precio, features e intervalos en un solo objeto) |
| Checkout (URL hosteada) | checkoutUrl que devuelve subscriptions.create |
| Subscription | Subscription |
| Customer (creado en el checkout) | Customer (creado vía API, idempotente) |
| Usage records sobre subscription items | Eventos de uso sobre features medidas |
| Discounts | Códigos promocionales |
| Webhooks | Webhooks (opcionales — el estado es consultable) |
La diferencia estructural: en Lemon Squeezy, el checkout es el punto de entrada y el customer se materializa después de la compra. En Commet, el customer y la subscription son objetos de API primero: el checkout es una URL que la API te devuelve.
Estados de subscription
Los ciclos de vida se traducen limpio:
| Estado en Commet | ¿Da acceso? | Estado más cercano en Lemon Squeezy |
|---|---|---|
pending_payment | No | — (checkout aún no completado) |
trialing | Sí | on_trial |
active | Sí | active |
past_due | Sí (período de gracia) | past_due |
paused | No | paused |
canceled | No | cancelled |
expired | No | expired |
Dale acceso con active o trialing, y tratá past_due como período de gracia mientras corren los reintentos de pago.
Modelos de consumo
Cada plan de Commet usa exactamente un modelo de consumo — son mutuamente excluyentes por plan:
metered: pago por uso, agregado y facturado a fin de período. Mapea a los variants usage-based de Lemon Squeezy.credits: los clientes compran paquetes de créditos por adelantado; el uso los consume.balance: un balance monetario prepago que se debita por evento, incluyendo pricing por token de IA.
Créditos y balance no tienen equivalente en Lemon Squeezy — son los modelos que los productos de IA necesitan cuando el metering plano queda corto.
SDKs oficiales
Lemon Squeezy publica un solo SDK oficial, en JavaScript. Commet cubre cinco lenguajes más integraciones de framework:
| SDK / herramienta | Lemon Squeezy | Commet |
|---|---|---|
| Node.js / TypeScript | @lemonsqueezy/lemonsqueezy.js | @commet/node |
| Python | — | commet |
| Go | — | commet-go |
| Java | — | commet-java |
| PHP | — | commet-php |
| Browser / checkout | Lemon.js (overlay, opcional) | No hace falta — el checkout es una URL de redirect |
| Helpers para Next.js | — | @commet/next (handler de webhooks, helpers de rutas) |
| Integración con AI SDK | — | commet-ai-sdk (tracking de tokens) |
| Integración de auth | — | commet-better-auth |
| CLI | — | CLI commet (commet listen para webhooks locales) |
Instalá el SDK de Node.js:
npm install @commet/nodeInicializá el cliente:
import { Commet } from '@commet/node'
export const commet = new Commet({
apiKey: process.env.COMMET_API_KEY!,
})Las API keys están separadas por entorno: las keys ck_sandbox_xxx pegan contra tu organización sandbox, así que el mismo código corre en sandbox y producción cambiando solo la key.
Crear customers y subscriptions vía API
En Lemon Squeezy, creás un checkout para un store y un variant; el customer y la subscription nacen cuando el comprador lo completa:
import { lemonSqueezySetup, createCheckout } from '@lemonsqueezy/lemonsqueezy.js'
lemonSqueezySetup({ apiKey: process.env.LEMONSQUEEZY_API_KEY! })
const checkout = await createCheckout(storeId, variantId, {
checkoutData: {
email: 'billing@acme.com',
custom: { user_id: 'user_123' },
},
})
// redirigir a checkout.data?.data.attributes.urlVincular esa compra con tu usuario implica hacer viajar el custom data ida y vuelta por webhooks.
En Commet, creás el customer explícitamente con tu propio ID, después la subscription, y la URL de checkout vuelve en la misma llamada:
const customer = await commet.customers.create({
email: 'billing@acme.com',
id: 'user_123',
})
const subscription = await commet.subscriptions.create({
customerId: 'user_123',
planCode: 'pro',
})
// redirigir a subscription.data.checkoutUrlcustomers.create es idempotente: si ya existe un customer con el mismo id, devuelve el registro existente. Tu ID de usuario funciona en todos lados; sin tabla de mapeo, sin ida y vuelta de custom data.
Los parámetros que acepta subscriptions.create:
| Parámetro | Tipo | Descripción |
|---|---|---|
customerId | string | ID de customer de Commet (cus_xxx) o tu ID externo |
planCode | string | Código del plan (alternativa a planId) |
planId | string | UUID del plan (alternativa a planCode) |
billingInterval | string | weekly, monthly, quarterly, yearly o one_time |
initialSeats | object | Códigos de tipo de seat mapeados a cantidades |
skipTrial | boolean | Saltear el período de trial del plan |
successUrl | string | URL de redirect después del pago exitoso |
Verificar acceso es una consulta directa:
const sub = await commet.subscriptions.getActive({ customerId: 'user_123' })
if (sub.data?.status === 'active') {
// El usuario pagó
}Cambios de plan y proration
Commet trae una sola política por defecto para cambios de plan: los cambios que benefician al cliente aplican inmediatamente; los que lo perjudican aplican en la renovación.
Los upgrades proratean y toman efecto al instante; los downgrades se programan para el próximo período. Los clientes también pueden cambiar de plan solos a través del Customer Portal — el equivalente en Commet del customer portal de Lemon Squeezy, creado automáticamente por cliente.
Trials
Los trials se definen en el plan, y skipTrial: true los saltea por subscription. Durante un trial la tarjeta se captura pero no se cobra, y Commet emite trial.started, trial.will_end (3 días antes del vencimiento) y trial.converted o trial.expired al final.
Medir uso / metering
Lemon Squeezy soporta billing por uso a través de usage records reportados contra un subscription item:
import { createUsageRecord } from '@lemonsqueezy/lemonsqueezy.js'
await createUsageRecord({
quantity: 1,
action: 'increment',
subscriptionItemId: 123456,
})Notá el identificador: el uso se ata a un subscription item ID, que tenés que buscar y almacenar por cliente.
En Commet, el uso se ata al customer y a un código de feature que definís vos, sin IDs intermedios:
await commet.usage.track({
customerId: 'user_123',
feature: 'api_calls',
value: 1,
idempotencyKey: 'req_abc123',
})La lista completa de parámetros:
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
feature | string | Sí | Código de evento de una feature medida (a-z0-9_) |
customerId | string | Sí | ID de customer de Commet o tu ID externo |
value | number | No | Cantidad consumida. Default 1 |
idempotencyKey | string | No | Evita eventos duplicados |
timestamp | string | No | Datetime ISO 8601. Default: ahora |
properties | object | No | Metadata clave-valor para debugging |
La idempotencyKey evita eventos duplicados: Commet rechaza eventos con una key ya registrada para el mismo customer.
Los chequeos de entitlements corren contra la misma definición del plan, así que podés hacer gating en tiempo real en lugar de descubrir el overage al facturar:
const { data } = await commet.featureAccess.get({
code: 'api_calls',
customerId: 'user_123',
})
if (data.allowed) {
// Servir el request
}Billing de tokens de IA
Si cobrás por uso de IA, Commet mantiene un catálogo de precios de más de 180 modelos. Pasá model, inputTokens y outputTokens al mismo método track, y Commet calcula el costo, aplica tu margen configurado y debita el balance del cliente:
await commet.usage.track({
customerId: 'user_123',
feature: 'ai_chat',
model: 'gpt-4o',
inputTokens: 1500,
outputTokens: 300,
})En Lemon Squeezy, este pipeline — lookup de precio por modelo, margen, débito de balance, facturación de overage — es código que escribís y mantenés actualizado cuando cambian los precios de los modelos.
Mirá usage-based billing para comparar metered, créditos y balance.
Las features medidas también pueden llevar cuotas y límites de seats: Commet emite quota.threshold_reached y quota.exceeded cuando los clientes se acercan a sus límites, y seats.updated / seats.limit_reached para features basadas en seats — señales que de otra forma calcularías desde tus propias tablas de uso.
Webhooks
Lemon Squeezy firma los webhooks con HMAC-SHA256 en el header X-Signature, y la verificación es manual:
import crypto from 'node:crypto'
const digest = crypto
.createHmac('sha256', process.env.LEMONSQUEEZY_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex')
if (!crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature))) {
throw new Error('Invalid signature')
}
const event = JSON.parse(rawBody)
switch (event.meta.event_name) {
case 'subscription_created':
// Dar acceso
break
case 'subscription_cancelled':
// Quitar acceso
break
}Commet verifica por vos. En Next.js, @commet/next trae un handler tipado:
import { Webhooks } from '@commet/next'
export const POST = Webhooks({
webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,
onSubscriptionActivated: async (payload) => {
// Dar acceso
},
onSubscriptionCanceled: async (payload) => {
// Quitar acceso
},
})Fuera de Next.js, commet.webhooks.verifyAndParse({ rawBody, signature, secret }) hace el chequeo HMAC y devuelve el payload tipado; la firma llega en el header X-Commet-Signature.
Los eventos que manejás hoy mapean directo:
| Evento de Lemon Squeezy | Evento de Commet |
|---|---|
subscription_created | subscription.created / subscription.activated |
subscription_updated | subscription.updated |
subscription_plan_changed | subscription.plan_changed |
subscription_cancelled | subscription.canceled |
subscription_payment_success | payment.received |
subscription_payment_failed | payment.failed |
subscription_payment_recovered | payment.recovered |
order_refunded | payment.refunded |
Detalles operativos:
- Las entregas fallidas se reintentan con backoff exponencial — 8 intentos, de 1 minuto a 6 horas — y después Commet te manda por email el endpoint, el evento y la última respuesta.
- Para desarrollo local,
commet listen localhost:3000/api/webhooks/commetreenvía eventos en vivo a tu máquina, sin herramientas de tunneling. - Si tres entregas seguidas fallan, Commet desactiva el endpoint automáticamente y te manda un email de confirmación; reactivalo desde Settings → Webhooks cuando tu receptor esté arreglado.
- El estado de la subscription es consultable vía
subscriptions.getActive, así que los webhooks son notificaciones — no el mecanismo que mantiene tu base de datos correcta.
Impuestos y compliance
Las dos plataformas son Merchant of Record: cada una le vende a tu cliente, calcula y remite impuestos, y absorbe el compliance. Si te sumaste a Lemon Squeezy por su cobertura fiscal, no perdés nada al moverte a Commet.
Las diferencias están en cobertura de mercados y superficie de pagos:
- Monedas locales en Latinoamérica. Commet cobra planes en ARS, BRL, CLP, COP, PEN, UYU, PYG, BOB, MXN, CAD y EUR, con un precio canónico en USD y la moneda auto-detectada según el país de facturación del cliente en el checkout. Para clientes de LatAm, pagar en moneda local afecta directamente la conversión y las tasas de aprobación de tarjetas.
- Métodos de pago. Lemon Squeezy soporta tarjetas más PayPal. El checkout de Commet hoy es solo con tarjeta: si el volumen por PayPal importa en tu revenue, tenelo en cuenta al planificar la migración.
- Refunds y disputas los maneja el Merchant of Record en ambas plataformas. En Commet, los refunds son gratis y las disputas cuestan $15.
- Payouts son parte del trato de Merchant of Record en los dos lados: la plataforma cobra a tus clientes y te liquida tu revenue neto.
El pricing es regional por diseño. Definís un precio canónico en USD por plan, agregás precios locales solo para las monedas que te importan, y la contabilidad interna queda en USD.
Precios
Las dos plataformas cobran un único fee todo-incluido: procesamiento, impuestos y responsabilidad de Merchant of Record:
| Lemon Squeezy | Commet | |
|---|---|---|
| Por transacción exitosa | 5% + $0.50 | 4.5% + $0.40 |
| Procesamiento de pagos | Incluido | Incluido |
| Cálculo y remisión de impuestos | Incluido | Incluido |
| Tarjetas internacionales | Incluidas en el fee base | +1.5% (tarjetas no estadounidenses) |
| Disputas | Según la política vigente de Lemon Squeezy | $15 por disputa |
| Refunds | — | Gratis |
Sobre $500K de revenue anual con tarjeta y una transacción promedio de $25, la diferencia de cabecera — 0.5% más $0.10 por transacción — son $4,500 por año. La brecha crece con la cantidad de transacciones, así que corrélo contra tu propia distribución.
Ninguna de las dos plataformas cobra fees mensuales de plataforma — las dos son take-rate puro, así que el costo escala con el revenue desde la primera transacción.
Detalles completos en la página de precios.
Conclusión
Moverse entre Merchants of Record es una migración contenida: tu historia de impuestos no cambia, y el trabajo se concentra en los objetos de billing.
Hay una cosa que no migra automáticamente, así que planificala: las facturas y órdenes históricas. Mantené acceso a Lemon Squeezy para los registros viejos; Commet genera facturas desde el primer ciclo migrado en adelante.
- Modelá tus Products y Variants como planes de Commet con features explícitas.
- Reemplazá la creación de checkouts hosteados por
customers.create+subscriptions.create→ redirect alcheckoutUrl. - Cambiá los usage records atados a subscription item IDs por
commet.usage.trackcon tu propio ID de usuario. - Portá los handlers de webhooks:
@commet/nextelimina la verificación HMAC manual. - Hacé el corte en la renovación, corriendo un ciclo en paralelo para comparar facturas.
La ganancia se concentra en los pasos 2 y 3: tus propios IDs de usuario fluyen por todo el sistema de billing, y las tablas de lookup de subscription items desaparecen.
Probá el flujo completo antes de tocar producción: cada cuenta de Commet incluye un entorno sandbox aislado con tarjetas de prueba y un test clock, así que podés simular un ciclo de billing completo — checkout, uso, renovación — en minutos.
Arrancá con el quickstart, o mirá cómo se compara Commet con Stripe Billing y Paddle.