Commet
  • Pricing
Log InTry out

Migrate from Paddle to Commet

A practical guide to moving subscriptions, checkouts, and webhooks from Paddle Billing to Commet — with side-by-side code and an honest fee comparison.


Introduction

You chose Paddle to stop dealing with global sales tax — and that part worked. But your product is turning usage-based, your customers are in markets Paddle prices in USD, and every checkout runs through an overlay you don't control from the backend.

Commet is also a Merchant of Record, so you keep the tax and compliance coverage you migrated to Paddle for. What changes is the developer experience: plans, subscriptions, and usage metering are API primitives, not dashboard objects wired to a JavaScript overlay.

Jump ahead to any section:

  • History
  • Concepts
  • Official SDKs
  • Create customers & subscriptions via API
  • Track usage / metering
  • Webhooks
  • Taxes & compliance
  • Pricing
  • Conclusion

History

Paddle was founded in London in 2012 by Christian Owens and Harrison Rose. It pioneered the Merchant of Record model for software companies — Paddle sells to your customer, so tax registration, filing, and remittance across jurisdictions are Paddle's job, not yours. In 2022 it acquired ProfitWell, adding subscription analytics, and its current API generation is Paddle Billing.

Commet applies the same Merchant of Record model to a narrower problem: billing for SaaS and AI products that charge for consumption. Plans are the source of truth, usage events drive metered charges, and local-currency pricing covers markets — Latin America in particular — where USD checkout hurts conversion.

Because both platforms are Merchant of Record, this is a migration between equivalents on tax — the comparison that matters is API surface, metering, and fees. That's what the rest of this guide covers.

Concepts

Both platforms are Merchant of Record, so the seller-of-record concepts match. The billing objects differ:

PaddleCommet
Product + PricePlan (pricing, features, and intervals in one object)
Transaction + Paddle.js checkoutcheckoutUrl returned by subscriptions.create
SubscriptionSubscription
CustomerCustomer
DiscountsPromo codes
Usage tracked in your own databaseMetered features + usage events
Notifications (webhooks)Webhooks (optional — state is queryable)

The structural difference is where billing starts. In Paddle, a subscription begins client-side: you create a transaction or pass price IDs to the Paddle.js overlay, and the subscription materializes after checkout. In Commet, a subscription begins server-side: one API call creates it and hands back a checkout URL.

Subscription statuses

The lifecycles translate cleanly:

Commet statusGrants access?Closest Paddle status
pending_paymentNo— (transaction not yet completed)
trialingYestrialing
activeYesactive
past_dueYes (grace period)past_due
pausedNopaused
canceledNocanceled
expiredNo—

Gate access on active or trialing, and treat past_due as a grace period while payment retries run.

Consumption models

Each Commet plan uses exactly one consumption model — they are mutually exclusive per plan:

  • metered: pay-per-use, aggregated and invoiced at period end.
  • credits: customers buy credit packs upfront; usage draws them down.
  • balance: a prepaid monetary balance debited per event, including per-token AI pricing.

None of the three has a Paddle equivalent — this is the layer teams on Paddle build in their own database.

Official SDKs

SDK / toolPaddleCommet
Node.js / TypeScript@paddle/paddle-node-sdk@commet/node
Pythonpaddle-python-sdkcommet
Gopaddle-go-sdkcommet-go
Java—commet-java
PHPpaddle-php-sdkcommet-php
Browser / checkoutPaddle.js (required for checkout)Not required — checkout is a redirect URL
Next.js helpers—@commet/next (webhook handler, route helpers)
AI SDK integration—commet-ai-sdk (token usage tracking)
Auth integration—commet-better-auth
CLI—commet CLI (commet listen for local webhooks)

Install the Node.js SDK:

npm install @commet/node

Initialize the client:

lib/commet.ts
import { Commet } from '@commet/node'

export const commet = new Commet({
  apiKey: process.env.COMMET_API_KEY!,
})

API keys are environment-scoped: ck_sandbox_xxx keys hit your sandbox organization, so the same code runs in sandbox and production with only the key changing.

Create customers & subscriptions via API

In Paddle, you create the customer and a transaction server-side, then open the checkout with Paddle.js in the browser:

Paddle — server
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 — browser
Paddle.Checkout.open({
  transactionId: transaction.id,
})

In Commet, the whole flow is server-side. Plans are referenced by a readable code, and the response includes the checkout URL:

Commet
const customer = await commet.customers.create({
  email: 'billing@acme.com',
  id: 'user_123',
})

const subscription = await commet.subscriptions.create({
  customerId: 'user_123',
  planCode: 'pro',
})

// redirect to subscription.data.checkoutUrl

customers.create is idempotent — if a customer with the same id already exists, it returns the existing record. You pass your own user ID everywhere; no ID mapping table.

The parameters subscriptions.create accepts:

ParameterTypeDescription
customerIdstringCommet customer ID (cus_xxx) or your external ID
planCodestringPlan code (alternative to planId)
planIdstringPlan UUID (alternative to planCode)
billingIntervalstringweekly, monthly, quarterly, yearly, or one_time
initialSeatsobjectSeat type codes mapped to quantities
skipTrialbooleanSkip the plan's trial period
successUrlstringRedirect URL after successful payment

Checking access is a direct query, not a webhook-fed lookup:

Commet
const sub = await commet.subscriptions.getActive({ customerId: 'user_123' })

if (sub.data?.status === 'active') {
  // User has paid
}

Plan changes and proration

Where Paddle exposes proration modes you pick per update (prorated_immediately, full_next_billing_period, and so on), Commet ships one default policy: changes that benefit the customer apply immediately; changes that cost them apply at renewal.

Upgrades prorate and take effect right away; downgrades schedule for the next period. Customers can also change plans themselves through the Customer Portal, created automatically per customer.

Trials

Trials are defined on the plan, and skipTrial: true bypasses them per subscription. During a trial the card is captured but not charged, and Commet emits trial.started, trial.will_end (3 days before expiry), and trial.converted or trial.expired at the end.

Track usage / metering

Paddle has no native usage metering. Teams running usage-based pricing on Paddle typically track consumption in their own database, then bill it by adjusting subscription item quantities or issuing one-time charges at period end — the metering pipeline is yours to build and reconcile.

In Commet, metered billing is a plan feature. Your application sends events; Commet aggregates and invoices at period end:

Commet
await commet.usage.track({
  customerId: 'user_123',
  feature: 'api_calls',
  value: 1,
  idempotencyKey: 'req_abc123',
})

The full parameter list:

ParameterTypeRequiredDescription
featurestringYesEvent code of a metered feature (a-z0-9_)
customerIdstringYesCommet customer ID or your external ID
valuenumberNoQuantity consumed. Defaults to 1
idempotencyKeystringNoPrevents duplicate events
timestampstringNoISO 8601 datetime. Defaults to now
propertiesobjectNoKey-value metadata for debugging

The idempotencyKey prevents duplicate events — Commet rejects events with a key already recorded for the same customer.

Real-time entitlement checks come from the same plan definition, so gating happens before the invoice, not after:

Commet
const { data } = await commet.featureAccess.get({
  code: 'api_calls',
  customerId: 'user_123',
})

if (data.allowed) {
  // Serve the request
}

AI token billing

If you charge for AI usage, Commet maintains a catalog of 180+ model prices. Pass model, inputTokens, and outputTokens to the same track method, and Commet computes the cost, applies your configured margin, and debits the customer's balance:

Commet
await commet.usage.track({
  customerId: 'user_123',
  feature: 'ai_chat',
  model: 'gpt-4o',
  inputTokens: 1500,
  outputTokens: 300,
})

On Paddle, this entire pipeline — model price lookup, margin, balance debit, overage invoicing — lives in your codebase.

Plans also support credits and prepaid balance as first-class consumption models. See usage-based billing for how the three models compare.

Metered features can carry quotas and seat limits too: Commet emits quota.threshold_reached and quota.exceeded as customers approach their limits, and seats.updated / seats.limit_reached for seat-based features — signals you'd otherwise compute from your own usage tables.

Webhooks

Paddle delivers notifications signed with the Paddle-Signature header, and the SDK verifies and parses them:

Paddle
const event = await paddle.webhooks.unmarshal(
  rawBody,
  process.env.PADDLE_WEBHOOK_SECRET!,
  signature,
)

switch (event.eventType) {
  case 'subscription.activated':
    // Grant access
    break
  case 'subscription.canceled':
    // Revoke access
    break
}

Commet's shape is similar, and in Next.js the @commet/next handler removes the boilerplate — it verifies signatures and routes events to typed callbacks:

app/api/webhooks/commet/route.ts
import { Webhooks } from '@commet/next'

export const POST = Webhooks({
  webhookSecret: process.env.COMMET_WEBHOOK_SECRET!,

  onSubscriptionActivated: async (payload) => {
    // Grant access
  },

  onSubscriptionCanceled: async (payload) => {
    // Revoke access
  },
})

Outside Next.js, verify the HMAC-SHA256 signature with commet.webhooks.verifyAndParse({ rawBody, signature, secret }) — the signature arrives in the X-Commet-Signature header.

The events you handle today map almost one-to-one:

Paddle eventCommet event
subscription.createdsubscription.created
subscription.activatedsubscription.activated
subscription.updatedsubscription.updated / subscription.plan_changed
subscription.canceledsubscription.canceled
subscription.past_duesubscription.past_due
transaction.completedpayment.received
transaction.payment_failedpayment.failed
adjustment.created (refund)payment.refunded

Two operational details worth knowing:

  • Commet retries failed deliveries with exponential backoff — 8 attempts, from 1 minute to 6 hours — then emails you the endpoint, event, and last response.
  • For local development, commet listen localhost:3000/api/webhooks/commet forwards live events to your machine, no tunneling tools needed.
  • If three deliveries in a row fail, Commet auto-disables the endpoint and emails you a confirmation; re-enable it from Settings → Webhooks once your receiver is fixed.

And because subscription state is queryable via subscriptions.getActive, webhooks are notifications — not the mechanism that keeps your database correct.

Taxes & compliance

Here the platforms agree: both are Merchant of Record. Paddle and Commet each sell to your customer, calculate and remit tax, and absorb the compliance work. If you migrated to Paddle for that reason, you lose nothing by moving to Commet.

The differences are in coverage and payment surface:

  • Local currencies in Latin America. Commet prices plans in ARS, BRL, CLP, COP, PEN, UYU, PYG, BOB, MXN, CAD, and EUR, with a canonical USD price and currency auto-detected from the customer's billing country. If you sell into LatAm, charging in local currency directly affects conversion and approval rates.
  • Payment methods. Paddle supports cards plus wallets and PayPal. Commet checkout is card-only today — if wallet or PayPal volume matters to your revenue, factor that into the migration.
  • Refunds and disputes are handled by the Merchant of Record on both platforms. On Commet, refunds are free and disputes cost $15.
  • Payouts are part of the Merchant of Record deal on both sides: the platform collects from your customers and pays out your net revenue.

Pricing is regional by design. You define one canonical USD price per plan, add local prices only for the currencies you care about, and internal accounting stays in USD.

Pricing

Both platforms price as a single all-in fee — processing, tax, and Merchant of Record liability included:

PaddleCommet
Per successful transaction5% + $0.504.5% + $0.40
Payment processingIncludedIncluded
Tax calculation & remittanceIncludedIncluded
International cardsIncluded in base fee+1.5% (non-US cards)
DisputesPer Paddle's current policy$15 per dispute
Refunds—Free

On $1M of annual card revenue at a $50 average transaction, the headline difference — 0.5% plus $0.10 per transaction — is $7,000 per year. Run the numbers on your own distribution; the gap grows with transaction count.

Neither platform charges monthly platform fees — both are pure take-rate, so cost scales with revenue from the first transaction.

Full details on the pricing page.

Conclusion

Moving from one Merchant of Record to another is a smaller migration than leaving the model: your tax story is unchanged, and the work concentrates in the billing objects.

One thing does not migrate automatically, so plan for it: historical invoices. Keep Paddle access for past invoices; Commet generates invoices from the first migrated cycle forward.

Test the whole flow before touching production: every Commet account includes an isolated sandbox environment with test cards and a test clock, so you can simulate a full billing cycle — checkout, usage, renewal — in minutes.

  1. Model your Paddle Products and Prices as Commet plans with explicit features.
  2. Replace the transaction + Paddle.js overlay flow with subscriptions.create → redirect to checkoutUrl.
  3. Move your homegrown usage tracking to commet.usage.track and delete the reconciliation code.
  4. Port webhook handlers — the events map almost one-to-one.
  5. Cut over at renewal, running one cycle in parallel to compare invoices.

The payoff is concentrated in step 3: the usage tables, aggregation jobs, and end-of-period billing scripts you maintain around Paddle stop existing.

Start with the quickstart, or read the full Paddle vs Commet comparison first.

Developers

  • Documentation
  • Templates
  • GitHub

Frameworks

  • Next.js
  • Remix
  • Nuxt
  • SvelteKit
  • Astro
  • Express
  • Hono
  • Django
  • FastAPI

Resources

  • Blog
  • Changelog
  • Pricing

AI

  • Agents
  • MCP Server
  • Agent Skills
  • Claude Code
  • Codex
  • Cursor

Learn

  • Guides
  • Glossary
  • Solutions
  • Billing for AI Models
  • Comparison

Compare

  • Stripe alternative
  • Orb alternative
  • Recurly alternative
  • Paddle alternative
  • Chargebee alternative
  • Lago alternative

Company

  • About
  • Open Source
  • Terms
  • Privacy

Countries

  • Mexico
  • Argentina
  • Colombia
  • Chile
  • Peru
  • Ecuador
  • Uruguay
  • Paraguay
  • Bolivia
  • Panama
  • El Salvador
  • Brazil
XLinkedInGitHub