Visit AppSignal.com

Handling VAT with Stripe: Follow up

Thijs Cadier on

Last year we wrote about our approach to handling European VAT rules with Stripe. I’m happy to report that Stripe has now implemented a much better tax handling system. Meanwhile we also learned some more about properly filing VAT tax returns. Let’s see how we can use this new knowledge to achieve a better result with less code.

Previously, we implemented adding VAT to an invoice for a subscription by adding an invoice line. Since API version 12/22/14 there’s a tax_percent field on the Subscription object. When you use this field, Stripe will automatically make the calculation and add the VAT amount to every invoice that’s generated for the subscription.

So the first step, if you use a similar approach as described in our earlier blog post, is removing the extra logic on the webhook for invoice.created. We don’t need to modify the invoice anymore. Instead we’ll add the tax_percent field to the subscription. We still need to have a way to determine if a customer needs to pay VAT though.

Our accountant informed us that even though we were applying the right VAT percentages to all invoices, we do need to separately categorize all the revenue from non-VAT paying EU customers. That’s called “reverse charged” in accountancy parlance. So we now use these two methods to determine the right tax status:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  # If a customer is based in the Netherlands, or within the EU while not having
  # supplied a valid VAT number.
  def vat_complicit?
    country == HOME_COUNTRY ||
      (
        AppsignalServer.eu_countries.include?(country_name) &&
        !valid_vat_number?
      )
  end

  # If a customer is within the EU, but not in the Netherlands, and has supplied
  # a valid VAT number.
  def vat_reverse_charged?
    country != HOME_COUNTRY &&
      AppsignalServer.eu_countries.include?(country_name) &&
      valid_vat_number?
  end

Disclaimer: This is only accurate if you run a service that sells to businesses primarily. Please check with your accountant: we can’t tell if things might be different in your specific situation. We’re not providing you with legal or bookkeeping advice, and can’t be hold accountable as such.

Now we know everything we need to know, and we can actually create a subscription. All the logic we needed earlier is gone, as we just add the tax_percent field when updating the subscription for a customer in Stripe:

1
2
3
4
5
stripe_customer.update_subscription(
  :plan        => plan.stripe_slug,
  :prorate     => plan_change_is_an_upgrade?,
  :tax_percent => vat_complicit? ? VAT_PERCENTAGE : nil
)

Whenever an invoice has been created and paid, we add it to our SaaS invoicing system MoneyBird. Instead of having one tax category for non-VAT invoices we now have two, and select the correct one like this:

1
2
3
4
5
6
7
8
tax_rate = if account.vat_complicit?
             :vat_complict_tax_rate_id
           elsif account.vat_reverse_charged?
             :vat_reverse_charged_tax_rate_id
           else
             :no_vat_tax_rate_id
           end
tax_rate_id = MONEYBIRD_CONFIG[tax_rate]

That’s all there is to it now. Big thanks to Stripe for making handling subscriptions nicer for their European customers!

10 latest articles

Go back

Subscribe to

Ruby Magic

Magicians never share their secrets. But we do. Sign up for our Ruby Magic email series and receive deep insights about garbage collection, memory allocation, concurrency and much more.