Handling EU VAT for Stripe and Braintree

Handling VAT taxation is something all European SaaS startups and companies have to go through at some point. Some time ago ScaleDrone became VAT eligible and we decided to go through the hassle ourselves as the companies that provide services to deal with VAT for electronic services did meet our needs.

Popular payment providers like Stripe and Braintree don't handle VAT for you and their recommended way was to use an online service like Taxamo. However, we found that all the online services that deal with VAT required us to drastically change our payment process. Thus we decided to manage VAT taxation on our own. There weren't any tutorials on this so here is ours.

Please note that this blog post does not specify all VAT rules, but only demonstrates technical solutions.

We're going to be using Node.js in the examples but you can apply the guide to any programming language.

Billing details form

Your billing details form should contain the following items:

  • Customer name
  • Company name
  • VAT number
  • Country (two letter code)
  • Street address
  • Postal code

Match VAT number and Country

Make sure that the selected VAT number and Country match by checking the first two letters of a VAT number and the selected country code.

const countryCode = req.body.vat.slice(0, 2); // First two letters of EE123456789
if (countryCode !== req.body.countryCode) {
   return handleError('VAT number does not match selected country');

Validate the VAT number

We're going to validate the customer's VAT number using the validate-vat npm module which calls a web service provided by VIES.

const validateVat = require('validate-vat');
const countryNumbers = req.body.vat.slice(2); // Numbers part of the VAT number
validateVat(countryCode, countryNumbers, (error, info) => {
   if (error) {
       return handleError('Unable to validate VAT number');
   if (!info.valid) {
       return handleError('Invalid VAT number');

Determine the customer’s location

A customer's location needs to be proved using two non-conflicting pieces of proof. We're going to determine the location using:

  • Customer's IP
  • Credit card country
  • Country from billing details page

Credit card country is provided by your payment provider.

Find customer's country using IP

We'll be using a MaxMind's GeoIP API for locating customer's IP address. We can use the node-geoip module for it.

const geoip = require('geoip-lite');
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const ipCountry = geoip.lookup(ip).country;

User's credit card country, selected country and VAT code country should be the same. Ideally the user's IP country would also be the same.

Apply the correct VAT rate

Please read on europa.eu which customers need VAT added to their invoices, for example VAT tax should not be added to EU companies with VAT numbers but should be added to EU customers without VAT numbers.

To find the correct VAT rate we'll use the vat-moss module. Since it's a bad practice to calculate money using numbers vat-moss returns the VAT rate as a big.js object, we can safely use the VAT rate to calculate the final price.

const vatMoss = require('vat-moss');
const vatRate = vatMoss.declaredResidence.calculateRate(countryCode).rate;
const price = '50'; //$50
const priceWithVat = vatRate.times(price).plus(price).toString();

Add VAT info to invoices

Lastly, we need to add the extra VAT related info to the invoices:

  • Customer's IP
  • Customer's VAT number
  • Your VAT number
  • Price without VAT
  • VAT rate
  • Total VAT amount
  • Price with VAT

Success! 🎉

You can now start accepting VAT payments!

As you can see it's not very difficult to start handling VAT yourself. If you're already handling payments it can be easier and cheaper to do it yourself than to use a third party service.

Read more about VAT from europa.eu.

Send realtime data to your users
We take care of complex realtime infrastructure problems so you can focus on what you enjoy - building awesome apps
Build realtime features now