If you sell a software subscription from an EU country, the price on your pricing page is not the price. What you owe depends on who clicked subscribe, where they are, and whether they typed a VAT number into your checkout form.
The same EUR 49 plan, sold from a Bulgarian company, is taxed in four different ways:
| Buyer | Treatment | What your invoice says |
|---|---|---|
| Business in another EU state, valid VAT ID | Reverse charge, you charge nothing | 0%, with a reverse-charge note |
| Consumer in another EU state | VAT of their country | their national rate, reported through OSS |
| Anyone in Bulgaria | Bulgarian VAT | 20% |
| Business or consumer outside the EU | Usually outside EU VAT scope | 0% |
Four branches, one product. This is not a tax problem that lives in your accountant's spreadsheet. It lives in your checkout.
The branch your code gets wrong first
The EU B2B case looks like the easy one, no VAT, move on, but it has a precondition that is easy to skip: the customer's VAT number has to be valid, and you have to have checked it.
If it is valid, the reverse charge applies and the buyer accounts for the tax in their own country. If it is not valid, or you never checked, you are selling to what the rules treat as a consumer, and you owe VAT in the buyer's country. The difference between those two outcomes is one API call at checkout, and it is not one you can make retroactively when the customer is long gone.
So the first thing your billing logic needs is not a tax table. It is a validation step, and a stored record of what that validation returned and when.
The B2C case is the one that scales badly
Sell to a consumer in another EU country and you charge that country's rate, not yours. A consumer in Hungary pays Hungarian VAT; one in Luxembourg pays Luxembourgish VAT. Your price is the same, your liability is not.
Without the One Stop Shop scheme, that means registering for VAT in every member state where you have a single consumer. OSS collapses that into one return filed in your own country, and it is the difference between a solvable problem and an unmanageable one.
The engineering consequence: you need the customer's location established and stored as evidence, not merely inferred at render time. Pick which pieces of evidence you keep, billing address, IP country, card issuer country, and keep them with the transaction, not in a log that rotates out.
Non-EU is not automatically nothing
Selling to a business outside the EU generally falls outside EU VAT scope. Selling to a consumer outside the EU is where it stops being simple: many countries tax digital services supplied to their residents under their own rules, and those rules are not yours to ignore because you are elsewhere.
That is a monitoring obligation rather than a coding one, but it belongs in the same decision tree so that it is a deliberate branch rather than a fallthrough.
What this looks like as logic
if buyer.country == home_country:
charge(home_rate) # Bulgaria: 20%
elif buyer.in_eu:
if buyer.vat_id and validated(buyer.vat_id):
charge(0, note="reverse charge")
else:
charge(rate_of(buyer.country)) # report via OSS
else:
charge(0) # outside EU scope, check local rules separately
Four branches, and every one of them has an evidence requirement attached. The code is trivial. The record-keeping is the part that fails audits.
Deadlines are a systems constraint, not an accounting one
In Bulgaria, VAT returns are monthly and payment is due by the 14th of the following month. Records, invoices, the customer's location evidence, VAT number validation results, any adjustments, are kept for ten years.
Ten years is longer than most SaaS billing systems survive without a migration. If your evidence of why you charged 0% to a customer in 2026 lives only in a third-party service you later replace, you have a problem that surfaces years after anyone remembers the decision.
Export it. Store it with the invoice. Treat it as part of the financial record, not as telemetry.
The rate is the least interesting number
Most guides lead with 20%. The rate matters least. What decides your actual liability is:
- whether you validated a VAT number, and whether you kept the result
- whether you can prove where a consumer was
- whether you registered for OSS before you needed it rather than after
- whether your evidence outlives your billing provider
None of those are tax questions. They are all storage and validation questions, and they are all decided in code long before an accountant sees them.
I run a company formation and accounting practice in Bulgaria, so this is the jurisdiction I can write about at article level. The full version, including registration thresholds, OSS mechanics and the official source for each rule, is on our site: VAT for SaaS Companies in Bulgaria. The rules here derive from the EU VAT Directive as implemented in Bulgaria; specifics differ by member state and change, so check the current position for yours before shipping billing logic.