# Take Money 3 Ways, Write Checkout Once

> Stripe, LemonSqueezy, and Dodo Payments behind a single interface. Switch payment gateways without touching your frontend.

Source: https://boringpush.com/blog/three-payment-rails

Every SaaS founder eventually hits the multi-gateway dilemma.

- **Stripe** has the most mature developer tools, but leaves global sales tax, VAT remittance and local Indian payment methods on your shoulders.
- **LemonSqueezy** acts as a merchant of record, handling global tax. It takes a higher percentage fee, and its payout schedule can lag.
- **Dodo Payments** is a merchant of record with strong support for UPI, Indian regional methods and global tax handling.

Hardcode one gateway into your Next.js app and switching later means tearing apart checkout buttons, database schemas, customer portal links and webhook handlers.

Keep all three behind a single interface. Verify each provider's current terms before you commit. Fees and payout schedules change.

## The three providers side by side

| Feature | Stripe | LemonSqueezy | Dodo Payments |
|---|---|---|---|
| **Role** | A direct payment processor | A merchant of record | A merchant of record |
| **Sales tax and VAT** | You handle it or pay extra | Handled for you | Handled for you |
| **India and regional methods** | Add-on setup | Varies | A strong suit |
| **Payout schedule** | Rolling payouts | Check the current schedule | Check the current schedule |
| **Switching effort here** | A settings change | A settings change | A settings change |

## Write checkout once. Switch with a setting.

Your pages and checkout buttons never import a payment vendor's scripts.

They call one endpoint, and the request goes to whichever provider is active. It says which product, which user and where to send the buyer afterward. Every provider answers in the same shape.

To switch from Stripe to Dodo, change one setting in your production settings and sync it with `bp secret sync`.

The checkout redirect, webhook parsing, order creation and paid-access switch work the same across all three.

## Webhooks that grant access without double charges

Each provider sends events in a different shape.

A completed Stripe checkout.

A LemonSqueezy order.

A Dodo payment.

Each looks different on the wire.

Standardize them into one internal result: grant access, revoke access or ignore. Along with the user, the email and the provider's own reference.

Before touching the database, check whether you have seen that reference already. Duplicate deliveries never create multiple licenses or double-charge customers.

Verify every event against the provider's signature before anything is read. An unsigned or tampered request is rejected, and nothing is written.

## Choosing between them

A short decision path.

- US buyers and subscriptions: start with Stripe.
- Global buyers and a lifetime deal: Dodo or LemonSqueezy, so someone else handles VAT.
- A mixed catalog: LemonSqueezy, for the subscription maturity.
- Not sure yet: start with Stripe, the fastest path to a first dollar, and switch when your buyer data arrives.

The interface is what makes "not sure yet" a safe answer. Nothing you build on top of it assumes a provider.

## Test it before you trust it

Run a full purchase in each provider's test mode before launch.

Pay.

Confirm access is granted.

Refund.

Confirm access is removed.

Then replay the same webhook twice and make sure only one order exists.

Less than an hour per provider. The cheapest insurance in the whole project, and you pay it once.

## What it does not do

BoringPush runs one active provider at a time. It does not route buyers between providers by region on its own. It does not reconcile payouts across dashboards.

It also does not email receipts for you. Decide whether your provider's receipts are enough.

## Know this first

One active provider at a time.

No regional routing.

No payout reconciliation.

No receipt emails.

Run each provider's test mode first.

## Why BoringPush is the way to do this

Write checkout once.

Stripe, LemonSqueezy and Dodo behind one interface.

Switch with a setting.

Every event is verified.

Every payment records once.

No rewrite when you change your mind.

## FAQ

### Can I sell lifetime deals on Dodo and monthly subscriptions on Stripe?

The underlying interface can address a specific provider, and the default checkout uses the active one. Running two at once for different products takes extra routing work on your side. Many founders start with one provider and add the second when the data says so.

### Do I need to register for global VAT with Dodo or LemonSqueezy?

No. As merchants of record, they calculate, collect and remit digital sales tax and VAT under their own registration. You still handle income tax and bookkeeping, so keep an accountant.

### How do I add a fourth provider?

Write one adapter that answers the same four questions as the others: start a checkout, verify an event, report the result and open a portal. Register it once. The rest of the app does not change.

### What happens to access when a payment is refunded?

The provider's refund event arrives, the kit checks whether anything else keeps the user paid, and access turns off if not. Test this in the provider's test mode before launch.

### Can I test all three without real money?

Yes. Each provider has a test mode, and the kit runs the same flow against each. Replay a webhook twice and confirm that only one order appears.

## Keep reading

- [Stripe vs LemonSqueezy vs Dodo: Pick Wrong, Pay for a Year](/blog/stripe-vs-lemonsqueezy-vs-dodo-nextjs) — the full comparison
- [Next.js Stripe Trap: The Retry That Double-Charges You](/blog/nextjs-stripe-boilerplate-webhooks) — the dedupe law all three share
- [Your Paywall Is 47 ifs. It Should Be 1 Boolean.](/blog/paywall-one-boolean) — the access side every provider feeds
