< blog />
SaaS billing: Stripe subscriptions, taxes and dunning
By Vinicius Ambrozio, founder of VTA Tecnologia · Published
Billing is the part of a SaaS that nobody wants to build and everyone gets wrong the first time. It looks like a checkout button. It is actually a state machine that runs for years, gets hit by expired cards, tax rules, upgrades in the middle of a cycle and customers who dispute a charge from eight months ago.
This guide covers the shape that works for a product built in Brazil and sold in the United States, and what changes if you also sell to Brazilian customers. Written by the VTA team: we built our own payment gateway, with KYC, payouts, checkout and a webhook API, and we run integrations in production with Stripe, Mercado Pago and three Brazilian processors. Nothing here is a Stripe endorsement; it is what we would build for ourselves.
Where does the money actually flow?
Before any code, decide which legal entity charges the customer. This is a business and tax question, not a technical one, and it changes everything downstream.
The common shape for a product built in Brazil and sold to US customers is a US entity, often a Delaware company set up through a service like Stripe Atlas, that holds the Stripe account, charges customers in dollars, and pays the Brazilian company for development. US customers see a US company on their statement, sales tax is handled in one jurisdiction, and the payment provider is operating in its home market.
The alternative is charging from a Brazilian entity. Stripe operates in Brazil, and a Brazilian account settles in reais, so a US customer paying in dollars is going through a currency conversion and a cross-border charge on every renewal. It works for early revenue; it gets uncomfortable as volume grows, and it makes your accountant's life harder in both countries. Talk to an accountant who knows both sides before choosing, and treat this article as the engineering half of that conversation.
How should subscriptions be modeled in Stripe?
Stripe's subscription model has four objects that matter: a Customer (one per tenant of your SaaS), Products (what you sell), Prices (how much and how often) and Subscriptions (a customer paying a price on a schedule). Almost every billing mistake we are asked to fix comes from reinventing one of these four in the application database and then trying to keep the two copies in sync.
The pattern that survives is simple. Your database stores the Stripe customer identifier on the tenant record and the Stripe subscription identifier with its current status. Stripe owns the truth about plans, prices, trials, proration and invoices. Your application asks one question on every request: is this tenant's subscription in a status that grants access? Everything else, including the pricing page, reads from Stripe.
For the checkout itself, use Stripe Checkout and the hosted customer portal in the first release. They handle card entry, 3D Secure, plan changes and cancellations without you writing a single form, and they are updated when regulations change. A custom checkout is a reasonable phase-two project once you know which parts of the hosted one actually cost you conversions.
Which webhooks matter, and how do you not lose them?
A subscription changes state without anyone clicking anything in your product: a renewal succeeds at 3am, a card fails, a trial ends, a dispute opens. Stripe tells you through webhooks, and your database has to reflect those events or your customers will be granted or denied access wrongly.
The events a SaaS cannot ignore are the ones that change access: subscription created, updated and deleted, invoice paid and invoice payment failed, and the checkout session completed event that ties a new subscription to a tenant. Each handler should be idempotent, meaning that receiving the same event twice does no harm, because Stripe retries deliveries and you will get duplicates.
Two engineering rules that prevent most incidents. First, verify the webhook signature on every request and reject anything that fails; an unsigned endpoint is an open door to granting yourself a free plan. Second, store every received event before processing it, so that a bug in a handler can be replayed after the fix rather than lost. The twelve questions we suggest asking a vendor include walking through a failed payment for exactly this reason: a team that has done it can describe these events by name.
What about sales tax and VAT?
Since the Supreme Court's 2018 decision in South Dakota v. Wayfair, a US state can require a seller with no physical presence to collect sales tax once it crosses that state's economic threshold, and many states now tax SaaS. The rules differ by state, some tax software subscriptions and some do not, and the thresholds are in dollars of sales or number of transactions. A SaaS selling nationwide will eventually have obligations in several states.
Stripe Tax can calculate the right tax on each invoice based on the customer's address and your registrations, and it can tell you when you are approaching a threshold in a new state. What it does not do is register you in that state or file the returns. Those remain your job or your accountant's, and the amount of it grows with revenue. Budget for a tax service or a firm from the first year rather than discovering the problem in an audit.
For customers outside the US, collect a tax identifier at checkout for businesses, so that invoices show it and reverse-charge rules can apply where they exist. Stripe stores customer tax IDs and prints them on invoices, which saves a support ticket per European customer per month.
What is dunning and how much does it matter?
Dunning is the set of things that happen after a renewal fails: retrying the card on a schedule, emailing the customer to update it, and eventually downgrading or cancelling. It matters because cards expire, get replaced after fraud and hit limits, and a subscription business that does nothing about failed renewals is quietly losing customers who never intended to leave.
Stripe's Smart Retries pick retry times based on when a charge is most likely to succeed, and Stripe's revenue recovery features add the emails and a hosted page where the customer updates the card. Network card updaters, which receive replacement card details from the networks, are part of the same machinery. We deliberately do not quote a recovery percentage here: Stripe publishes its own figures and they depend on your customer base. What we can say is that turning these on is configuration, not code, and that not turning them on is the most common billing mistake we see in products that arrive at our door.
What your application has to do is respond correctly to the states: keep access during the retry window, show a clear banner asking for a new card, and downgrade only when Stripe says the subscription is past due or cancelled. Cutting access on the first failed attempt is how you turn a card expiry into a churned customer.
What changes if you also sell in Brazil?
Brazilian customers pay differently. Pix, the instant payment system run by the Banco Central do Brasil, is the most used payment method in the country, settles in seconds and costs the merchant a fraction of a card fee. Boleto, a bank slip paid at any bank or app, is still common for business customers. Card subscriptions work, but the customer base expects the other two, and a SaaS that offers only card leaves money on the table.
Recurring Pix arrived in June 2025 as Pix Automático, which lets a customer authorize a merchant to pull recurring charges from their account, the way a direct debit does. It is the first time subscriptions in Brazil have had a native rail that does not depend on a card, and it is worth supporting for any Brazilian plan.
In practice this means a second processor for the Brazilian side. Mercado Pago's subscriptions API covers card recurrence and Pix, and Brazilian gateways cover Pix Automático and boleto with their own webhook models. The engineering consequence is that your access logic has to be provider-agnostic: a tenant's subscription record should say which provider it lives on and what its normalized status is, so the rest of the product never asks whether this customer is on Stripe or on Pix.
| Rail | Where | Recurring | Typical use in a SaaS | What to watch |
|---|---|---|---|---|
| Card via Stripe Billing | US and most countries | Native | Default for US customers; Checkout plus customer portal | Dunning configuration; sales tax by state |
| Card via a Brazilian processor | Brazil | Native | Brazilian customers who prefer card, charged in reais | Local installment expectations; a second webhook model |
| Pix | Brazil | Per charge, or through Pix Automático | Monthly plans paid on an invoice; annual plans | Settlement is instant; reconcile by the end-to-end identifier |
| Pix Automático | Brazil, since June 2025 | Native | Recurring plans without a card | Processor support is still uneven; check before promising it |
| Boleto | Brazil | Per charge | Business customers paying by invoice | Days to settle; expiry handling |
What we have built, and what we would build for you
Bias declared: we sell this. VTA built its own payment gateway, with KYC, payouts, checkout and a webhook API, and we maintain integrations in production with Stripe, Mercado Pago, BuckPay, Cakto and Paradise. That is where the opinions above come from, including the ones about idempotent handlers and storing events before processing them, which we learned the expensive way.
For a SaaS built in Brazil and sold in the US, we would build Stripe Billing with Checkout and the portal, webhooks stored and replayable, Stripe Tax on from the first invoice, and a provider-agnostic subscription record so that a Brazilian rail can be added later without touching access logic. If that is the shape of your product, the payment gateway integration page and the SaaS development page describe the work. For what the whole build tends to cost, we published public numbers.
The detail on the work itself
Frequently asked questions
How do I set up subscription billing for a SaaS with Stripe?
Create one Stripe Customer per tenant, define Products and Prices in Stripe, and use Stripe Checkout and the hosted customer portal for signup and plan changes. Store the customer and subscription identifiers in your database, let webhook events update the subscription status, and grant access based on that status alone.
Does a SaaS have to charge sales tax in the US?
In many states, yes, once sales cross that state's economic threshold, a rule that has applied to remote sellers since the 2018 Wayfair decision. Which states tax SaaS and at what threshold differs. Stripe Tax can calculate and monitor thresholds; registering and filing in each state remains your responsibility.
What is dunning in SaaS billing?
The process that follows a failed renewal: retrying the card on a schedule, emailing the customer to update it, and downgrading or cancelling if nothing works. Stripe's Smart Retries and revenue recovery emails handle most of it through configuration; your application has to keep access during the retry window and downgrade only when Stripe reports the subscription as past due or cancelled.
Should I charge US customers from a Brazilian company?
It works for early revenue, but every renewal becomes a cross-border charge with currency conversion, and it complicates accounting in both countries. The common shape is a US entity that holds the Stripe account and pays the Brazilian company for development. Decide this with an accountant who knows both jurisdictions before writing billing code.
Can Brazilian customers pay for a SaaS with Pix?
Yes. Pix is the most used payment method in Brazil and settles in seconds. For recurring plans, Pix Automático, launched by the central bank in June 2025, allows authorized recurring charges without a card, though processor support is still uneven. Most products support Pix per invoice and card recurrence through a Brazilian processor such as Mercado Pago.
Which Stripe webhooks does a SaaS need?
The ones that change access: checkout session completed, subscription created, updated and deleted, and invoice paid and invoice payment failed. Verify the signature on every request, make each handler idempotent because Stripe retries deliveries, and store every event before processing so a handler bug can be replayed after the fix.
Keep reading
Multi-tenant SaaS architecture explained for founders →
What multi-tenant means, the three ways to separate customer data, how row-level security works in Postgres, and the decisions that are expensive to reverse.
SaaS development cost in 2026: what US startups actually pay →
Public price bands for building a SaaS product, the recurring costs nobody puts in the quote, and the five decisions that move a build from five figures to six.
How to choose a SaaS development company: 12 questions →
Twelve questions to ask every SaaS development company you shortlist, with the weak answer and the strong answer to each, so proposals stop looking identical.
Want a number for your own project?
Tell us what you are building and you get a written scope, a fixed price and a delivery date within 24 hours.