Skip to main content

Stripe vs Polar vs LemonSqueezy 2026

·StarterPick Team
Share:

Payment Processing Is Not a Commodity Decision

Choosing your payment provider is one of the highest-leverage decisions in a SaaS boilerplate. It affects:

  • Your take-home rate — fees vary from 2.9% to 9%+ depending on the provider
  • Geographic reach — not all providers serve all countries
  • Tax compliance — merchant of record services handle VAT/sales tax; Stripe does not
  • Developer experience — webhook handling, SDK quality, documentation
  • Boilerplate compatibility — most boilerplates support Stripe; fewer support others

TL;DR

  • Stripe: Use for most SaaS products. Maximum boilerplate support, best API, requires tax compliance work.
  • LemonSqueezy: Use for B2C products selling internationally. Merchant of record handles taxes. 8% fee.
  • Polar.sh: Use for open-source projects and developer tools. Usage-based billing, GitHub integration, generous free tier.
  • Paddle: Similar to LemonSqueezy for B2B international sales. Enterprise-focused.

Key Takeaways

  • Stripe has 2.9% + $0.30 base fee — the lowest transaction cost
  • LemonSqueezy is 8% per transaction — more expensive but handles all sales tax globally
  • Polar.sh has a 4% fee (with Stripe's 2.9%) — better for developer tools needing GitHub integration
  • Merchant of record (LemonSqueezy, Paddle, Polar) handle EU VAT, US sales tax, and global tax compliance — Stripe does not
  • If you sell to EU customers as a small SaaS, a merchant of record saves significant compliance work
  • All major boilerplates (ShipFast, OpenSaaS, Makerkit) support Stripe; fewer support the alternatives

Stripe: The Default

Stripe is the payment infrastructure standard in 2026. Every major SaaS boilerplate includes Stripe. The API is excellent, the documentation is best-in-class, and Stripe's ecosystem (Radar for fraud, Billing for subscriptions, Meters for usage-based billing) is unmatched.

Stripe Setup in Next.js

npm install stripe @stripe/stripe-js
// lib/stripe.ts
import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2025-10-28.acacia',
});

// Create a checkout session:
export async function createCheckoutSession({
  priceId,
  customerId,
  userId,
  successUrl,
  cancelUrl,
}: {
  priceId: string;
  customerId?: string;
  userId: string;
  successUrl: string;
  cancelUrl: string;
}) {
  return stripe.checkout.sessions.create({
    mode: 'subscription',
    payment_method_types: ['card'],
    customer: customerId,
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: successUrl,
    cancel_url: cancelUrl,
    metadata: { userId },
    allow_promotion_codes: true,
    subscription_data: {
      metadata: { userId },
    },
  });
}

// Customer portal (manage subscription):
export async function createPortalSession(customerId: string, returnUrl: string) {
  return stripe.billingPortal.sessions.create({
    customer: customerId,
    return_url: returnUrl,
  });
}
// app/api/webhooks/stripe/route.ts
import { stripe } from '@/lib/stripe';
import { headers } from 'next/headers';

export async function POST(req: Request) {
  const body = await req.text();
  const signature = headers().get('stripe-signature')!;

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    return new Response('Invalid signature', { status: 400 });
  }

  switch (event.type) {
    case 'checkout.session.completed': {
      const session = event.data.object;
      const userId = session.metadata?.userId;
      await db.user.update({
        where: { id: userId },
        data: {
          stripeCustomerId: session.customer as string,
          stripeSubscriptionId: session.subscription as string,
          plan: 'pro',
        },
      });
      break;
    }
    case 'customer.subscription.deleted': {
      const sub = event.data.object;
      await db.user.update({
        where: { stripeSubscriptionId: sub.id },
        data: { plan: 'free' },
      });
      break;
    }
  }

  return new Response('OK');
}

Stripe Downsides

  • No merchant of record — you handle EU VAT, US sales tax yourself
  • Tax compliance for EU: use Stripe Tax ($0.50 per transaction) or use TaxJar/Avalara
  • Stripe Tax is available but an add-on cost

LemonSqueezy: Merchant of Record

LemonSqueezy is a merchant of record — they handle all sales tax, VAT, and compliance globally. You sell through LemonSqueezy; they remit taxes in 100+ countries.

Fee: 8% + $0.50 per transaction (significantly higher than Stripe's 2.9%)

The 8% is worth paying when:

  • You sell to EU customers (VAT compliance is complex)
  • You are in a country with complex tax requirements
  • You want to minimize accounting overhead
npm install @lemonsqueezy/lemonsqueezy.js
// lib/lemonsqueezy.ts
import { lemonSqueezySetup, createCheckout } from '@lemonsqueezy/lemonsqueezy.js';

lemonSqueezySetup({ apiKey: process.env.LEMONSQUEEZY_API_KEY! });

export async function createLemonCheckout({
  variantId,
  userId,
  successUrl,
}: {
  variantId: string;
  userId: string;
  successUrl: string;
}) {
  const checkout = await createCheckout(
    process.env.LEMONSQUEEZY_STORE_ID!,
    variantId,
    {
      checkoutOptions: { embed: false },
      checkoutData: {
        custom: { userId },
      },
      productOptions: { redirectUrl: successUrl },
    }
  );

  return checkout.data?.data.attributes.url;
}
// Webhook handler:
export async function POST(req: Request) {
  const body = await req.text();
  const secret = process.env.LEMONSQUEEZY_WEBHOOK_SECRET!;

  const hmac = crypto.createHmac('sha256', secret);
  const digest = hmac.update(body).digest('hex');
  const signature = req.headers.get('x-signature');

  if (digest !== signature) return new Response('Invalid', { status: 401 });

  const event = JSON.parse(body);

  if (event.meta.event_name === 'subscription_created') {
    const userId = event.meta.custom_data.userId;
    await db.user.update({
      where: { id: userId },
      data: { plan: 'pro', lemonSqueezyCustomerId: event.data.attributes.customer_id },
    });
  }
}

Polar.sh: Developer Tools Billing

Polar.sh is designed for open-source maintainers and developer tools:

npm install @polar-sh/sdk
// lib/polar.ts
import { Polar } from '@polar-sh/sdk';

export const polar = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
});

// Create checkout for Polar:
export async function createPolarCheckout({
  productPriceId,
  userId,
  successUrl,
}: {
  productPriceId: string;
  userId: string;
  successUrl: string;
}) {
  const checkout = await polar.checkouts.create({
    productPriceId,
    successUrl,
    metadata: { userId },
  });
  return checkout.url;
}

Polar.sh unique features:

  • GitHub Issues integration — link subscriptions to GitHub repositories
  • Benefit system — automatically grant benefits (Discord roles, GitHub access) on subscription
  • Usage-based billing — meter API calls or usage
  • Free for open source — lower fees for OSS projects

Fee: 4% (Polar) + 2.9% (Stripe pass-through) = ~7% total


Comparison Table

FeatureStripeLemonSqueezyPolar.sh
Transaction fee2.9% + $0.308% + $0.50~4% + Stripe
Merchant of recordNoYesYes
Tax handlingManual (or Stripe Tax)AutomaticAutomatic
Boilerplate supportUniversalManyGrowing
Usage-based billingYes (Meters)LimitedYes
Developer focusNeutralNeutralStrong
GitHub integrationNoNoYes
EU VAT complianceManualIncludedIncluded
SubscriptionsExcellentGoodGood
API qualityExcellentGoodGood

Which Boilerplates Support What?

BoilerplateStripeLemonSqueezyPolar.sh
ShipFastYesYesNo
OpenSaaSYesYesYes
MakerkitYesYesNo
SaaSBoldYesYesYes (Paddle)
SupastarterYesYesNo
T3 StackManualManualManual

Decision Framework

Choose Stripe if:
  → US-focused B2B SaaS (fewer tax complications)
  → Need maximum boilerplate compatibility
  → Want usage-based billing (Stripe Meters)
  → Lowest transaction cost matters

Choose LemonSqueezy if:
  → B2C with global customers
  → EU market (VAT compliance)
  → Digital products / content
  → Tax headaches outweigh the fee premium

Choose Polar.sh if:
  → Open-source project monetization
  → Developer tool with GitHub integration
  → Want GitHub Issues linked to subscriptions
  → Community/supporter model

Use both Stripe + LemonSqueezy if:
  → Some plans are B2B (Stripe), some B2C (LemonSqueezy)
  → Complex billing needs different tools

Methodology

Based on publicly available pricing and documentation from Stripe, LemonSqueezy, Polar.sh, and boilerplate documentation as of March 2026.


Tax Compliance: The Deciding Factor for Most Indie Founders

For solo founders and small teams, the merchant of record question often decides the payment provider before any API comparison happens. Here is the concrete calculation.

If you sell a $99/month subscription to a customer in Germany, German VAT law requires you to collect 19% VAT and remit it to the German tax authorities quarterly. If you sell to France, it's 20% VAT. If you sell to Italy, 22%. Each EU country has different rates and different filing requirements. As a non-EU seller, you're required to register for VAT in the EU (via the OSS scheme) if your EU sales exceed €10,000 per year — a threshold you hit with as few as 10 European customers at that price.

With Stripe, this compliance is yours. You can add Stripe Tax (0.5% per transaction) to automate the calculation and collection, and then you still need to file the quarterly OSS return yourself or pay an accountant to do it. You're the seller of record.

With LemonSqueezy or Polar.sh, they are the seller of record. You sell a product, they handle all VAT collection and remittance in 40+ and 100+ countries respectively. Your quarterly routine is zero — you just withdraw your net proceeds. The higher fee (8% for LemonSqueezy, ~7% effective for Polar.sh) is the price of not thinking about international tax compliance.

The break-even calculation for a solo founder: if you'd spend more than $150/year on accounting or compliance services, and your products reach international customers, a merchant of record saves money from the first transaction. Most B2C SaaS selling globally hits this threshold within the first few months.

Subscription Lifecycle and Dunning: Where the Real Money Hides

Handling subscription lifecycle events correctly is worth more than the initial checkout. The webhook events that fire after the first payment — invoice.payment_failed, customer.subscription.past_due, customer.subscription.trial_will_end — determine how much revenue you recover from payment failures.

Stripe's dunning system (called Smart Retries) automatically retries failed payments using machine learning to predict the best retry timing. Stripe recovers a significant portion of initially failed payments through retries alone. The invoice.payment_failed webhook fires when a payment fails and you haven't recovered it; customer.subscription.past_due fires when the grace period begins. Your webhook handler should update the user's plan status and trigger a "payment failed" email sequence.

LemonSqueezy handles retries automatically but with less configurability than Stripe. The retry schedule is fixed (Stripe's is dynamic). For most indie SaaS products, the difference in recovery rate is negligible. The webhook events mirror Stripe's — subscription.payment_failed, subscription.expired — and the handler pattern is the same.

Polar.sh is newer and its dunning behavior is less documented than Stripe's. For products with a significant portion of revenue from subscriptions, this is a meaningful risk to evaluate. The tax compliance benefit is clear; the subscription failure recovery behavior is less battle-tested.

The practical implication: for high-volume subscription businesses where dunning recovery directly impacts revenue, Stripe's Smart Retries are a genuine advantage. For lifestyle businesses and developer tools where customer relationships are smaller in scale, LemonSqueezy's automatic handling is sufficient and the tax compliance benefit outweighs the dunning difference.

Webhook Reliability and Testing Patterns

Webhook handling reliability is where payment integrations most commonly fail in production.

Stripe webhooks are delivered with an exponential backoff retry schedule over 72 hours. If your endpoint returns a non-2xx response, Stripe retries. You must handle duplicate deliveries (use the event id as an idempotency key) because retries can create duplicate event processing. Stripe CLI provides a local webhook proxy (stripe listen --forward-to localhost:3000/api/webhooks/stripe) that replays real event types against your local development server — invaluable for testing subscription state changes without real payments.

LemonSqueezy webhooks deliver with retry but have shorter retry windows than Stripe. The HMAC signature verification pattern is similar. One common production issue: LemonSqueezy sends all events to a single webhook URL (unlike Stripe, which lets you filter event types per endpoint). Your handler must route all event types — subscription created, updated, cancelled, payment failed — from a single POST handler. This increases the complexity of the webhook route but is manageable.

Polar.sh uses the same HMAC verification approach. The validateEvent function from the SDK verifies the signature and returns a typed event object, which reduces boilerplate in the handler. For developers familiar with Stripe's pattern, Polar.sh feels familiar enough that migration is straightforward.

Testing payment webhooks without CLI tooling is painful. All three providers have dashboard-level replay functionality — you can trigger a test event type from the dashboard. For automated testing, Stripe's test mode is the most complete (every event type is triggerable). LemonSqueezy's sandbox mode covers the main subscription lifecycle events. Polar.sh's sandbox is newer but sufficient for the common subscription flows.


Usage-Based Billing in 2026

The three providers differ significantly in their usage-based billing support, which matters as more SaaS products move away from flat-rate subscriptions toward consumption-based pricing.

Stripe Meters (launched in 2024, now stable) is the most mature usage-based billing infrastructure. You define a meter (API calls, tokens used, active seats) and report usage events via the API. Stripe aggregates usage over the billing period and generates the invoice automatically. The Stripe Meters API supports both real-time event ingestion and batched reporting, which matters for high-volume metered events where sending individual API calls per event would be impractical.

LemonSqueezy's usage-based billing support is limited. The platform handles simple subscription billing well but doesn't have a native meter or usage event API equivalent to Stripe Meters. Teams using LemonSqueezy for a usage-based SaaS typically implement usage tracking themselves and create custom invoices via the LemonSqueezy API — significantly more work than Stripe's native meter integration.

Polar.sh's usage-based billing is one of its stronger differentiators for developer tools specifically. The benefit system lets you define usage limits tied to subscription tiers — a user on the Pro plan gets 10,000 API calls per month; the Starter plan gets 1,000. Polar.sh tracks benefit usage and handles the upgrade prompts and limit enforcement. This is well-suited to API-as-a-product businesses where the product is metered by API calls, tokens, or similar technical units.

For SaaS products with flat monthly pricing, the usage-based billing differences don't matter much. For products where the pricing page says "pay per X," evaluate Stripe Meters first — it's the most complete implementation — and consider Polar.sh if GitHub integration and the benefit system fit your product model.

Evaluating a Boilerplate's Billing Integration Quality

Before purchasing or forking a boilerplate based on its advertised payment provider support, check three implementation details that separate quality integrations from placeholder code.

Idempotency on webhooks. Production Stripe integrations should use the Stripe event id as an idempotency key to prevent double-processing. The pattern: before processing a webhook event, check a stripe_events table in the database for the event ID; if it exists, return 200 without re-processing; if not, insert the ID and process. Boilerplates without this check will double-process events on retries — charging users twice, creating duplicate subscriptions, or sending duplicate emails — on any webhook delivery retry. Look for either a processedEvents table in the schema or explicit idempotency logic in the webhook handler.

Trial period handling. SaaS products commonly offer free trials. The correct Stripe implementation creates a subscription with trial_period_days and handles the customer.subscription.trial_will_end webhook (fires 3 days before trial ends) to send a reminder email, and the customer.subscription.updated webhook (fires when trial converts to paid) to update the user's plan status. Boilerplates that only handle checkout.session.completed and customer.subscription.deleted miss the trial lifecycle entirely.

Customer portal integration. The Stripe Customer Portal handles subscription management (upgrade, downgrade, cancel) without you building any UI. A properly integrated boilerplate has a single API route that creates a portal session and redirects the user — typically a "Manage Subscription" button in the account settings. If the boilerplate doesn't include this, users have no self-service way to change their plan, and you'll handle every subscription change manually.

For finding boilerplates with all three of these implemented correctly, best SaaS boilerplates 2026 is the starting reference — the top-rated commercial starters (ShipFast, MakerKit, Supastarter) have mature Stripe integrations that include idempotency, trial handling, and portal support. The best free open-source SaaS boilerplates 2026 guide is useful for auditing open-source options where you can read the webhook code directly. And why SaaS boilerplates choose Next.js 2026 provides context on why the billing layer is often the primary factor in boilerplate architectural decisions.

Choosing payment infrastructure for your SaaS? StarterPick helps you find boilerplates pre-configured with the right payment provider for your market.

Compare Stripe and LemonSqueezy in detail: Stripe vs LemonSqueezy for SaaS 2026.

See which boilerplates include billing pre-configured in the best SaaS boilerplates guide.

Read the ShipFast review for a concrete example of how Stripe is integrated in a leading commercial boilerplate.

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.