Skip to content

Lemon Squeezy vs Stripe for a Symfony SaaS: the merchant-of-record decision

Who handles EU VAT, what the fees really buy, when each one wins, and why the sane architecture lets you switch without rewriting your billing.

Every European founder hits this question the week they want to charge money: a payment processor or a merchant of record? It looks like a pricing comparison. It's actually a question about who files your taxes, and an architecture question about whether you'll be able to change your mind. ShipAnvil ships both providers behind one interface, so I've implemented each twice over; here's the honest breakdown.

This comparison covers standard Stripe Payments and Billing versus Lemon Squeezy. Stripe also offers Managed Payments, a separate merchant-of-record option for eligible businesses and integrations. Check its current requirements before choosing a model. Provider information reviewed on September 29, 2026.

The actual difference: who is the seller

With standard Stripe Payments and Billing, your business remains the seller. Payment processing, subscription management and tax tooling are separate choices. Stripe Managed Payments uses a different merchant-of-record model; it should not be confused with the standard Stripe integration discussed here.

Lemon Squeezy is a merchant of record (MoR). Your customer legally buys from Lemon Squeezy, which resells your software and takes on the sale itself: payments, sales tax collection, refunds and chargebacks (see the scope of its MoR service). You deal with one counterparty that pays you out.

For a solo founder or small team selling worldwide, handing the consumer-facing sales tax of every sale to the seller of record is the pitch, and it's a good one. It does not remove your own business's tax and accounting obligations: check those with your accountant.

What you pay for it

Compare the cost of the payment model you will actually use. Standard Stripe costs depend on the account country, payment method and products enabled; consult the current Stripe pricing for your configuration.

Lemon Squeezy's published base platform fee is 5% + US$0.50 per transaction. Its fee documentation, checked on September 29, 2026, also lists additions for international payments outside the US (1.5%), PayPal (1.5%) and subscription payments (0.5%). The fee is calculated on the total order value, and payout fees can also apply.

Compare the full cost against your expected customer locations, payment methods and subscription mix. A base headline rate is not an all-in subscription price.

Where each one wins

Lemon Squeezy when: you're small, international from day one, allergic to tax paperwork, selling self-serve at price points where a percentage point is invisible. Bonus: built-in license keys (that's what ShipAnvil itself sells through: the store funnel runs on it).

Stripe when: you need the deepest billing toolbox (usage-based pricing, complex proration, multi-entity), B2B invoicing with your own paper trail, control over each paid add-on, or integrations that assume Stripe (most of fintech does). The API and docs remain the reference standard.

Both, sequenced, is the quiet pro move: launch on the MoR while you're too small to staff tax compliance, then add or move to another provider (or split: B2C on MoR, B2B invoiced via Stripe) when the volume justifies it, as a planned migration.

The architecture that keeps the choice reversible

The trap is letting provider vocabulary leak into your domain. If your code checks stripe_status === 'past_due' in forty places, the MoR evaluation will feel impossible whatever the spreadsheet says.

The structure that keeps you free, exactly how ShipAnvil's billing module is built:

  1. One interface for everything a provider does: create a checkout, open the customer portal, cancel, resume, change plan, verify and translate a webhook.
  2. Normalized state: your Subscription entity speaks your enum (active, trialing, past_due, canceled) and attaches to the organization. Nothing outside the adapter knows whose webhook fed it.
  3. One write path: every provider's webhooks funnel into the same updater (the webhook-inbox pattern: store once, process idempotently through Messenger).
  4. Plans in config, holding a stripe_price_id and a lemon_squeezy_variant_id. In ShipAnvil, one environment variable picks the provider used for new checkouts. Each existing subscription stays attached to the provider that created it (its portal, cancel, resume and plan changes still go there), and both webhook endpoints stay mounted so the two can run side by side.

Selecting a provider in configuration is not the same as migrating existing customers and subscriptions. Plan that migration separately, including customer payment details, recurring contracts and the webhooks that remain active during the transition.

Do this and "Stripe vs Lemon Squeezy" stops being a one-way door in your code. Skip it and you've made a five-year decision in week one.

ShipAnvil ships both adapters tested against signed webhook fixtures (full lifecycles, both providers), plus a local sandbox provider so the whole thing runs without any account at all. Walk the checkout in the live demo, or see the pricing if you'd rather not build billing twice like I did.