Introducción
Elegiste Paddle para dejar de lidiar con impuestos globales, y esa parte funcionó. Pero tu producto se está volviendo usage-based, tus clientes están en mercados que Paddle cobra en USD, y cada checkout pasa por un overlay que no controlás desde el backend.
Commet también es Merchant of Record, así que mantenés la cobertura de impuestos y compliance por la que migraste a Paddle. Lo que cambia es la experiencia de desarrollo: planes, subscriptions y medición de uso son primitivas de API, no objetos de dashboard cableados a un overlay de JavaScript.
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
Paddle fue fundada en Londres en 2012 por Christian Owens y Harrison Rose. Fue pionera del modelo Merchant of Record para empresas de software: Paddle le vende a tu cliente, así que el registro, la presentación y la remisión de impuestos en cada jurisdicción son trabajo de Paddle, no tuyo. En 2022 adquirió ProfitWell, sumando analytics de subscriptions, y su generación actual de API es Paddle Billing.
Commet aplica el mismo modelo de Merchant of Record a un problema más acotado: billing para productos SaaS y de IA que cobran por consumo. Los planes son la fuente de verdad, los eventos de uso alimentan los cobros medidos, y el pricing en moneda local cubre mercados — Latinoamérica en particular — donde el checkout en USD lastima la conversión.
Como las dos plataformas son Merchant of Record, esta es una migración entre equivalentes en impuestos: la comparación que importa es superficie de API, metering y fees. De eso trata el resto de esta guía.
Conceptos
Las dos plataformas son Merchant of Record, así que los conceptos de vendedor de registro coinciden. Los objetos de billing difieren:
| Paddle | Commet |
|---|---|
| Product + Price | Plan (precio, features e intervalos en un solo objeto) |
| Transaction + checkout de Paddle.js | checkoutUrl que devuelve subscriptions.create |
| Subscription | Subscription |
| Customer | Customer |
| Discounts | Códigos promocionales |
| Uso trackeado en tu propia base de datos | Features medidas + eventos de uso |
| Notifications (webhooks) | Webhooks (opcionales — el estado es consultable) |
La diferencia estructural es dónde empieza el billing. En Paddle, una subscription nace del lado del cliente: creás una transaction o pasás price IDs al overlay de Paddle.js, y la subscription se materializa después del checkout. En Commet, una subscription nace del lado del servidor: una llamada a la API la crea y devuelve la URL de checkout.
Estados de subscription
Los ciclos de vida se traducen limpio:
| Estado en Commet | ¿Da acceso? | Estado más cercano en Paddle |
|---|---|---|
pending_payment | No | — (transaction aún no completada) |
trialing | Sí | trialing |
active | Sí | active |
past_due | Sí (período de gracia) | past_due |
paused | No | paused |
canceled | No | canceled |
expired | No | — |
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.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.
Ninguno de los tres tiene equivalente en Paddle — esta es la capa que los equipos sobre Paddle construyen en su propia base de datos.
SDKs oficiales
| SDK / herramienta | Paddle | Commet |
|---|---|---|
| Node.js / TypeScript | @paddle/paddle-node-sdk | @commet/node |
| Python | paddle-python-sdk | commet |
| Go | paddle-go-sdk | commet-go |
| Java | — | commet-java |
| PHP | paddle-php-sdk | commet-php |
| Browser / checkout | Paddle.js (requerido para checkout) | 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 Paddle, creás el customer y una transaction del lado del servidor, y después abrís el checkout con Paddle.js en el navegador:
import { Paddle } from '@paddle/paddle-node-sdk'
const paddle = new Paddle(process.env.PADDLE_API_KEY!)
const customer = await paddle.customers.create({
email: 'billing@acme.com',
})
const transaction = await paddle.transactions.create({
customerId: customer.id,
items: [{ priceId: 'pri_01gsz8x8sawmvhz1pv30nge1ke', quantity: 1 }],
})Paddle.Checkout.open({
transactionId: transaction.id,
})En Commet, todo el flujo es del lado del servidor. Los planes se referencian por un código legible, y la respuesta incluye la URL de checkout:
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. Usás tu propio ID de usuario en todos lados; sin tabla de mapeo de IDs.
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, no un lookup alimentado por webhooks:
const sub = await commet.subscriptions.getActive({ customerId: 'user_123' })
if (sub.data?.status === 'active') {
// El usuario pagó
}Cambios de plan y proration
Donde Paddle expone modos de proration que elegís por update (prorated_immediately, full_next_billing_period, etcétera), 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, 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
Paddle no tiene metering de uso nativo. Los equipos que corren pricing usage-based sobre Paddle suelen trackear el consumo en su propia base de datos y facturarlo ajustando cantidades de los items de la subscription o emitiendo cargos one-time a fin de período: el pipeline de medición lo construís y reconciliás vos.
En Commet, el billing medido es una feature del plan. Tu aplicación envía eventos; Commet agrega y factura a fin de período:
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 en tiempo real salen de la misma definición del plan, así que el gating pasa antes de la factura, no después:
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 Paddle, todo este pipeline — lookup de precio por modelo, margen, débito de balance, facturación de overage — vive en tu codebase.
Los planes también soportan créditos y balance prepago como modelos de consumo de primera clase. Mirá usage-based billing para comparar los tres modelos.
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
Paddle entrega notificaciones firmadas con el header Paddle-Signature, y el SDK las verifica y parsea:
const event = await paddle.webhooks.unmarshal(
rawBody,
process.env.PADDLE_WEBHOOK_SECRET!,
signature,
)
switch (event.eventType) {
case 'subscription.activated':
// Dar acceso
break
case 'subscription.canceled':
// Quitar acceso
break
}La forma en Commet es parecida, y en Next.js el handler de @commet/next elimina el boilerplate: verifica firmas y rutea eventos a callbacks tipados:
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 casi uno a uno:
| Evento de Paddle | Evento de Commet |
|---|---|
subscription.created | subscription.created |
subscription.activated | subscription.activated |
subscription.updated | subscription.updated / subscription.plan_changed |
subscription.canceled | subscription.canceled |
subscription.past_due | subscription.past_due |
transaction.completed | payment.received |
transaction.payment_failed | payment.failed |
adjustment.created (refund) | payment.refunded |
Dos detalles operativos que valen la pena:
- Commet reintenta entregas fallidas con backoff exponencial — 8 intentos, de 1 minuto a 6 horas — y después 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.
Y como el estado de la subscription es consultable vía subscriptions.getActive, los webhooks son notificaciones — no el mecanismo que mantiene tu base de datos correcta.
Impuestos y compliance
Acá las plataformas coinciden: las dos son Merchant of Record. Paddle y Commet le venden a tu cliente, calculan y remiten impuestos, y absorben el trabajo de compliance. Si migraste a Paddle por eso, no perdés nada al moverte a Commet.
Las diferencias están en cobertura 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. Si vendés en LatAm, cobrar en moneda local afecta directamente la conversión y las tasas de aprobación.
- Métodos de pago. Paddle soporta tarjetas más wallets y PayPal. El checkout de Commet hoy es solo con tarjeta: si el volumen por wallets o 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:
| Paddle | 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 Paddle | $15 por disputa |
| Refunds | — | Gratis |
Sobre $1M de revenue anual con tarjeta y una transacción promedio de $50, la diferencia de cabecera — 0.5% más $0.10 por transacción — son $7,000 por año. Corré los números con tu propia distribución; la brecha crece con la cantidad de transacciones.
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 de un Merchant of Record a otro es una migración más chica que abandonar el modelo: 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 históricas. Mantené acceso a Paddle para las facturas viejas; Commet genera facturas desde el primer ciclo migrado en adelante.
- Modelá tus Products y Prices de Paddle como planes de Commet con features explícitas.
- Reemplazá el flujo de transaction + overlay de Paddle.js por
subscriptions.create→ redirect alcheckoutUrl. - Mové tu tracking de uso casero a
commet.usage.tracky borrá el código de reconciliación. - Portá los handlers de webhooks: los eventos mapean casi uno a uno.
- Hacé el corte en la renovación, corriendo un ciclo en paralelo para comparar facturas.
La ganancia se concentra en el paso 3: las tablas de uso, los jobs de agregación y los scripts de facturación de fin de período que mantenés alrededor de Paddle dejan de existir.
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 leé primero la comparación completa entre Paddle y Commet.