Introducción
Tu lógica de billing está repartida entre Products, Prices, Subscriptions y metadata de Stripe, sostenida por una capa de webhooks que se desincroniza. Esta guía te muestra cómo moverla a Commet, paso a paso, con código lado a lado para cada operación.
Commet es una plataforma de billing para productos SaaS y de IA. Actúa como Merchant of Record, así que impuestos, compliance, refunds y payouts vienen incluidos: dejás de ser el vendedor de registro.
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
Stripe fue fundada en 2010 por Patrick y John Collison para hacer programables los pagos online. Stripe Billing llegó en 2018 como capa de subscriptions sobre esa infraestructura de pagos, y sirve a todos los modelos de negocio: desde diarios hasta gimnasios y SaaS.
Esa amplitud es la fortaleza de Stripe. También es la razón por la que armar billing usage-based sobre Stripe implica ensamblar Products, Prices, Meters, Entitlements y webhooks en un sistema que mantenés vos.
Commet fue construida para un solo trabajo: medir consumo y cobrarlo. Es una plataforma de billing plan-first para productos SaaS y de IA: los planes definen precios y features, las subscriptions facturan solas, y los eventos de uso alimentan los cobros medidos sin una capa de reconciliación de tu lado.
No son sustitutos directos. Stripe Billing es un toolkit donde el merchant seguís siendo vos; Commet es un sistema de billing completo donde el merchant es Commet. Esta guía cubre qué significa esa diferencia en cada capa de tu integración.
Conceptos
Stripe te da primitivas de pagos; Commet te da primitivas de billing. Así se mapean:
| Stripe Billing | Commet |
|---|---|
| Product + Price | Plan (precio, features e intervalos en un solo objeto) |
| Subscription | Subscription |
| Customer | Customer |
| Billing Meter + Meter Events | Feature medida + eventos de uso |
| Entitlements / Features API | Features del plan, consultadas con featureAccess.get |
| Coupons + Promotion Codes | Códigos promocionales |
| Checkout Session | checkoutUrl que devuelve subscriptions.create |
| Customer Portal | Customer Portal |
| Configuración de tax + Stripe Tax | Incluido — Commet es Merchant of Record |
La diferencia estructural más grande: en Stripe, la lógica del plan vive en parte en Prices, en parte en metadata y en parte en tu base de datos. En Commet, el plan es la única fuente de verdad: tu app lee los entitlements directo del plan en lugar de reconstruirlos desde eventos de webhook.
Estados de subscription
Los ciclos de vida se traducen limpio. Estos son los estados de Commet y qué significan para el acceso:
| Estado en Commet | ¿Da acceso? | Estado más cercano en Stripe |
|---|---|---|
pending_payment | No | incomplete |
trialing | Sí | trialing |
active | Sí | active |
past_due | Sí (período de gracia) | past_due |
paused | No | paused |
canceled | No | canceled |
expired | No | incomplete_expired |
La regla es la misma que usás hoy: 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 Billing Meters de Stripe.credits: los clientes compran paquetes de créditos por adelantado; el uso los consume. Sin equivalente en Stripe — es el sistema que los equipos construyen encima.balance: un balance monetario prepago que se debita por evento, incluyendo pricing por token de IA. Sin equivalente en Stripe.
SDKs oficiales
Las dos plataformas publican SDKs oficiales del lado del servidor. Commet suma integraciones de framework e IA:
| SDK / herramienta | Stripe | Commet |
|---|---|---|
| Node.js / TypeScript | stripe | @commet/node |
| Python | stripe | commet |
| Go | stripe-go | commet-go |
| Java | stripe-java | commet-java |
| PHP | stripe-php | commet-php |
| 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 | stripe 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 Stripe, suscribir a un cliente implica crear un Customer, adjuntar un método de pago o una Checkout Session, y referenciar un Price ID que buscás o hardcodeás.
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const customer = await stripe.customers.create({
email: 'billing@acme.com',
})
const session = await stripe.checkout.sessions.create({
customer: customer.id,
mode: 'subscription',
line_items: [{ price: 'price_1AbCdEfGh', quantity: 1 }],
success_url: 'https://acme.com/success',
cancel_url: 'https://acme.com/cancel',
})
// redirigir a session.urlEn Commet, el mismo flujo son dos llamadas. Los planes se referencian por un código legible que definís vos, y subscriptions.create devuelve la URL de checkout directamente:
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. Podés usar tu propio ID de usuario en todos lados en lugar de mantener una tabla de mapeo de cus_xxx.
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 si un usuario tiene acceso reemplaza tu tabla de estado alimentada por webhooks:
const sub = await commet.subscriptions.getActive({ customerId: 'user_123' })
if (sub.data?.status === 'active') {
// El usuario pagó
}Cambios de plan y proration
En Stripe, el comportamiento de proration es un set de flags (proration_behavior, billing_cycle_anchor) que configurás por llamada. Commet trae una sola política por defecto: 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 — la misma superficie de autoservicio que te da el Customer Portal de Stripe, creada automáticamente por cliente.
Medir uso / metering
El billing por uso de Stripe corre sobre Billing Meters. Creás un Meter en el dashboard, lo asociás a un Price y reportás meter events con el ID de Stripe del cliente:
await stripe.billing.meterEvents.create({
event_name: 'api_calls',
payload: {
stripe_customer_id: 'cus_NffrFeUfNV2Hib',
value: '1',
},
})Stripe agrega los eventos y factura a fin de período. Lo que no responde es si el usuario puede actuar ahora mismo: el gating en tiempo real (cuotas, créditos, balances prepagos) lo construís vos.
En Commet, las features medidas son parte del plan, y medir uso se ve así:
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. Reemplaza la deduplicación que en Stripe manejás con el identifier de los meter events.
Los chequeos de entitlements en tiempo real salen de la misma definición del plan:
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 Stripe, 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.
Más allá del billing medido, los planes de Commet soportan créditos y balance prepago como modelos de consumo de primera clase. En Stripe, eso son sistemas que construís sobre los meters. Mirá usage-based billing para comparar los tres modelos.
Webhooks
En Stripe, los webhooks son estructurales: estado de subscriptions, fallas de pago y cambios de plan llegan a tu app como eventos que tenés que consumir, verificar y volcar en tu base de datos.
const event = stripe.webhooks.constructEvent(
rawBody,
signature,
process.env.STRIPE_WEBHOOK_SECRET!,
)
switch (event.type) {
case 'customer.subscription.updated':
// Actualizar tu estado local de subscription
break
case 'invoice.payment_failed':
// Marcar la cuenta
break
}En Commet, los webhooks son notificaciones, no la fuente de verdad: siempre podés consultar el estado de la subscription directamente. Cuando sí los querés, @commet/next trae un handler tipado que verifica firmas y rutea eventos:
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, verificá la firma HMAC-SHA256 con commet.webhooks.verifyAndParse({ rawBody, signature, secret }); la firma llega en el header X-Commet-Signature.
Los eventos que manejás hoy mapean directo:
| Evento de Stripe | Evento de Commet |
|---|---|
customer.subscription.created | subscription.created |
customer.subscription.updated | subscription.updated / subscription.plan_changed |
customer.subscription.deleted | subscription.canceled |
customer.subscription.trial_will_end | trial.will_end |
invoice.created | invoice.created |
invoice.payment_succeeded | payment.received |
invoice.payment_failed | payment.failed |
charge.refunded | payment.refunded |
charge.dispute.created | payment.disputed |
Para desarrollo local, commet listen localhost:3000/api/webhooks/commet reenvía eventos en vivo a tu máquina — el mismo workflow que stripe listen, sin herramientas de tunneling.
Mecánica de entrega, lado a lado:
| Stripe | Commet | |
|---|---|---|
| Firma | Header Stripe-Signature, HMAC-SHA256 | Header X-Commet-Signature, HMAC-SHA256 |
| Reintentos | Backoff exponencial, hasta 3 días | Backoff exponencial, 8 intentos (1 min → 6 h) |
| Endpoints rotos | Se desactivan tras fallas sostenidas | Auto-desactivado tras 3 fallas consecutivas, con alerta por email |
| Desarrollo local | stripe listen --forward-to | commet listen localhost:3000/api/webhooks/commet |
Impuestos y compliance
Esta es la diferencia operativa más grande entre las dos plataformas.
Con Stripe Billing, el merchant of record sos vos. Stripe Tax calcula impuestos en el checkout (como fee adicional), pero registrarte en cada jurisdicción, presentar declaraciones y mantenerte en compliance cuando cambian los umbrales sigue siendo tu responsabilidad.
Con Commet, el Merchant of Record es Commet: le vende a tu cliente, así que el cálculo, la recaudación y la remisión de impuestos son problema de Commet, junto con refunds, disputas y payouts. No hay paso de configuración de impuestos en la migración; el tuyo lo borrás.
En concreto, pasarte a un Merchant of Record elimina cuatro tareas recurrentes:
- Seguir los umbrales de registro fiscal por jurisdicción a medida que crece tu revenue
- Presentar declaraciones y remitir los impuestos recaudados
- Procesar refunds y representar disputas como vendedor
- Reconciliar reportes de payout contra facturas para contabilidad
Commet también maneja pricing regional: definís precios canónicos en USD y, opcionalmente, precios en moneda local — ARS, BRL, CLP, COP, PEN, UYU, PYG, BOB, MXN, CAD y EUR — con la moneda auto-detectada según el país de facturación del cliente en el checkout.
Una limitación honesta para planificar: el checkout de Commet hoy es solo con tarjeta. Si una parte importante de tu revenue entra por débitos bancarios o wallets en Stripe, mantené ese flujo en Stripe hasta migrar tu segmento que paga con tarjeta.
Precios
| Stripe Billing | Commet | |
|---|---|---|
| Fee de Billing | 0.5%–0.8% del volumen facturado | Incluido |
| Procesamiento de pagos | 2.9% + $0.30 por transacción (tarjetas de EE.UU.) | Incluido |
| Cálculo de impuestos | Stripe Tax, fee adicional | Incluido |
| Presentación y remisión de impuestos | Tuya (o herramientas pagas) | Incluido — Merchant of Record |
| Total por transacción exitosa | ~3.4%–3.7% + $0.30, más tooling de tax | 4.5% + $0.40, todo incluido |
| Tarjetas internacionales | +1.5% | +1.5% (tarjetas no estadounidenses) |
| Disputas | $15 por disputa | $15 por disputa |
| Refunds | Los fees de procesamiento no se devuelven | Gratis |
La comparación correcta no es fee contra fee: es costo todo-incluido contra costo ensamblado. El 4.5% + $0.40 de Commet cubre procesamiento, lógica de billing, impuestos y la responsabilidad de Merchant of Record; el stack de Stripe llega a un número parecido cuando sumás Stripe Tax y el tiempo de ingeniería que mantiene la capa de billing.
Detalles completos en la página de precios.
Conclusión
Hay una cosa que no migra automáticamente, así que planificala: las facturas históricas. Mantené acceso a Stripe para las facturas viejas; Commet genera facturas desde el primer ciclo migrado en adelante.
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.
La migración en sí sigue una forma predecible:
- Modelá tus Products y Prices como planes de Commet: el sprawl de metadata se vuelve features explícitas.
- Cambiá
stripe.billing.meterEvents.createporcommet.usage.track. - Reemplazá las tablas de estado alimentadas por webhooks con
subscriptions.getActiveyfeatureAccess.get. - Borrá tu configuración de impuestos: el Merchant of Record es Commet.
- Hacé el corte en la renovación, corriendo un ciclo en paralelo para comparar facturas.
Tus clientes siguen pagando con tarjeta; lo que cambia es quién ensambla — y quién responde por — el sistema de billing.
Arrancá con el quickstart, o leé primero la comparación completa entre Stripe Billing y Commet.