# Stripe Tax, and when you legally need to care

> With Stripe you are the merchant of record, so sales tax and VAT are your liability. What Stripe Tax does and does not do, the thresholds that trigger obligations, and when a merchant of record is the better answer.

The uncomfortable fact first: when you charge with Stripe, **you are the merchant
of record**. The contract is between you and the customer. Sales tax, VAT and GST
are your obligation to calculate, collect, file and remit, in every jurisdiction
where you have a tax obligation.

Stripe Tax makes that tractable. It does not make it someone else's problem.
That distinction is the entire subject of this page, and it is the one thing to
be clear about before you pick a payment provider.

Nothing here is legal or tax advice. It is the engineering shape of the problem
and the questions to take to an accountant.

## What Stripe Tax does

- **Calculates** the correct rate at checkout, from the customer's location, your
  registered locations and the product's tax category.
- **Collects** it as part of the payment, as a separate line on the invoice.
- **Monitors thresholds** and tells you when your activity in a jurisdiction is
  approaching the point where you must register.
- **Produces filing reports**, and through Stripe Tax's filing service (in
  supported jurisdictions) can file and remit for you.

It costs roughly 0.5% per taxed transaction on the pay-as-you-go plan, on top of
processing fees.

## What Stripe Tax does not do

- **Register you.** You must register with each tax authority yourself and add
  the registration in the dashboard. Until you do, Stripe Tax collects nothing
  for that jurisdiction. Correctly, because collecting tax you are not
  registered to collect is itself a problem.
- **Take on liability.** If the calculation is wrong because the product tax
  category was wrong, the shortfall is yours.
- **Decide where you have nexus.** That is a legal question about your business.
- **Backdate.** It cannot collect tax you should have collected last year.

## Turning it on

```ts
const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  customer: customerId,
  line_items: [{ price: priceId, quantity: 1 }],
  automatic_tax: { enabled: automaticTaxEnabled() },  // STRIPE_AUTOMATIC_TAX
  // Tax needs an address; let Checkout save the one the customer types
  // back onto the customer record so renewals stay correct.
  customer_update: { address: "auto", name: "auto" },
  // B2B in the EU: a valid VAT id shifts the liability to the buyer.
  tax_id_collection: { enabled: true },
  billing_address_collection: "auto",
});
```

That is exactly what the Stripe adapter's `createCheckout` in this project
does (`src/lib/billing/provider.ts`), for subscriptions and one-time payments
alike, except that the calculation is **off until you switch it on**, with
`STRIPE_AUTOMATIC_TAX=true` in the environment. That is not caution about tax;
it is that Stripe *rejects* a Checkout Session with automatic tax on an account
that has not activated Stripe Tax and registered an origin address. Shipping it
on would mean the first buy button in a freshly generated repo fails. (It would
fail gracefully, on `/billing` with "Stripe could not start the checkout", but
nobody could pay.) Turn it
on as soon as you have done the two dashboard steps, and `bun run verify` will
fail loudly if the flag and the account ever disagree.

Everything else in the snippet (`customer_update`, `tax_id_collection`,
`billing_address_collection`) is on from the first line of code, because those
are free to send and are what makes the *renewal* invoice calculable later.

Three details matter:

**`customer_update: { address: "auto" }` is not optional.** Without it the
address the customer enters at checkout is attached to the session and not to the
customer, so the *renewal* invoice a month later has no address and tax
calculation fails.

**Product tax categories.** Each Stripe product needs one (`txcd_10103000` for
SaaS, and a long list of others). The default is a generic category that is wrong
in enough jurisdictions to matter. Set it per product in the dashboard.

**Prices are tax-exclusive by default.** In the EU and UK, consumers expect the
displayed price to include VAT. If you display tax-inclusive prices, set the
price's `tax_behavior` to `inclusive`, and know that your net revenue per
customer then varies by country.

## When do you actually need it?

The honest sequencing for a small SaaS:

**Your home jurisdiction, from the first sale.** You are registered there, or
should be. Turn Stripe Tax on before you take money.

**Everywhere else, at the threshold.** Obligations are usually triggered by
economic activity, not by having a single customer:

- **US:** each state sets its own economic nexus threshold; $100,000 in sales or
  200 transactions in a state per year are common figures, and they vary.
  Several states tax SaaS, several do not.
- **EU:** for B2C digital services sold from outside the EU there is effectively
  no threshold: the first sale creates an obligation, handled through the
  non-Union OSS scheme. For intra-EU cross-border B2C there is a €10,000 annual
  threshold. B2B with a valid VAT id is reverse-charged to the buyer.
- **UK:** VAT registration threshold applies to UK-established businesses;
  non-established businesses have no threshold for digital services.
- **Everywhere else:** varies enormously. Canada, Australia, Norway, Japan,
  Switzerland, India and many others have digital-services rules with their own
  thresholds.

Stripe Tax's threshold monitoring exists precisely because tracking this by hand
across 40 jurisdictions is not a thing a founder can do. Turn it on early even if
you register nowhere yet. The monitoring is the value.

## The alternative: a merchant of record

Paddle, Lemon Squeezy, Polar and Dodo (the last two are alternatives in this
registry) sell your product **as the seller**. They take the tax liability, the
registrations and the filings. You receive a payout.

The trade:

| | Stripe + Stripe Tax | Merchant of record |
|---|---|---|
| Tax liability | yours | theirs |
| Registrations and filings | yours (or Stripe's filing service) | theirs |
| Rate | ~2.9% + 30c, +0.5% tax | typically 5%+ all-in |
| Control over checkout, invoices, dunning | complete | limited |
| Customer relationship | yours | theirs on the receipt |

The rule of thumb: at low revenue, a merchant of record is cheaper than an
accountant plus your time. At higher revenue, the percentage becomes a real
number and Stripe plus a tax professional wins. The crossover is different for
every business, but "I do not want to think about VAT" is a legitimate and
common reason to start with a merchant of record and migrate later.

Migrating later is real work: subscriptions do not transfer between processors,
so it means asking every customer to re-enter a card. Factor that in.

## Getting the data right

Whatever you choose, your database should keep enough to answer an auditor:

- The invoice id for every payment, and the tax amount as a separate figure.
- The customer's country and, where collected, their tax id.
- Which jurisdiction the tax was attributed to.

Stripe holds all of this, and Stripe's reports are the authoritative source. Do
not build a parallel tax ledger. What you want locally is the identifiers that
let you find the Stripe record fast.

## What to do this week

1. Add your origin address and home registration at
   https://dashboard.stripe.com/settings/tax, then set
   `STRIPE_AUTOMATIC_TAX=true`. `customer_update: { address: "auto" }` is
   already on in this project's checkout and needs nothing from you.
2. Set a product tax category on every product.
3. Add your home jurisdiction's registration in the dashboard.
4. Let threshold monitoring run, and put a calendar reminder to look at it
   quarterly.
5. Ask an accountant, once, where you have obligations today. It is a cheap
   conversation and the answer changes what you build.

---

Agentic Boilerplate: A Next.js repo your agent already knows. Free during launch, then $99 once.

- Site map for agents: https://agenticboilerplate.com/llms.txt
- Public API: https://agenticboilerplate.com/openapi.json
- Contact: agenticstudio@gmail.com
