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.
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
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!