Valvat
Validates european vat numbers. Standalone or as a ActiveModel validator.
Install / Use
/learn @yolk/ValvatREADME
valvat
Validates european vat numbers. Standalone or as a ActiveModel validator.
A note on Brexit
Valvat supports validating VAT-IDs from the UK by syntax, checksum and using the HMRC API v2.0. Validation against the VIES web service stopped working early 2021.
Sadly with the deprecation of the HMRC API v1 on January 2025 there is no open accessible web service to validate UK vat numbers anymore. To use the current v2 of the HMRC API you will need to create an account on the HMRC Developer Hub and then apply for production credentials. The second step is a little bit more involved and can take up to ten business days. See the configuration section below how to use valvat with your HMRC authentication credentials.
Northern Ireland received its own VAT number prefix - XI which is supported by VIES web service so any XI-prefixed VAT numbers should be validated as any EU VAT number.
Features
- Simple syntax verification
- Lookup via the VIES web service (for EU VAT numbers)
- Lookup via the HMRC web service (for UK VAT numbers)
- ActiveModel/Rails integration
- Works standalone without ActiveModel
- Minimal runtime dependencies
- I18n locales for language specific error messages in English, German, French, Spanish, Italian, Portuguese, Polish, Swedish, Dutch, Danish, Czech, Slovakian, Hungarian, Bulgarian, Romanian, Latvian, Catalan, Norwegian, and Finnish.
- Experimental checksum verification
valvat is tested and works with ruby MRI 2.6/2.7/3.0/3.1/3.2/3.3, jruby and ActiveModel 5/6/7/8. If you need support for ruby down to 1.9.3 and ActiveModel 3 and 4 use v1.0.1.
Installation
Add it to your Gemfile:
gem 'valvat'
And run:
$ bundle
Or install it yourself as:
$ gem install valvat
Validate the syntax of a VAT number
To verify the syntax of a vat number:
Valvat.new("DE345789003").valid?
# => true or false
It is also possible to bypass initializing a Valvat instance and check the syntax of a vat number string directly with:
Valvat::Syntax.validate("DE345789003")
# => true or false
Validate against the VIES / HMRC web service
To check if the given vat number exists via the VIES or HMRC web service:
Valvat.new("DE345789003").exists?
# => true or false or nil
Or to lookup a vat number string directly:
Valvat::Lookup.validate("DE345789003")
# => true or false or nil
Because the HMRC API requires authentication validation against HMRC is only performed when the option :uk is set to a hash containing your authentication credentials. You need to create an account on the HMRC Developer Hub and apply for production credentials to validate UK VAT numbers against HMRC.
Valvat::Lookup.validate(
"GB553557881",
uk: {
client_id: '<client_id>',
client_secret: '<client_secret>'
}
)
# => true or false or nil
When :uk is not set to a hash containing the required authentication credentials the lookup of UK VAT numbers always returns false.
IMPORTANT Keep in mind that the web service might be offline at some time for all or some member states. If this happens exists? or Valvat::Lookup.validate will return nil. See Handling of maintenance errors for further details.
Details & request identifier
If you need all details and not only if the VAT is valid, pass {detail: true} as second parameter to the lookup call.
Valvat.new("IE6388047V").exists?(detail: true)
=> {
:country_code=> "IE", :vat_number => "6388047V", :valid => true,
:request_date => Date.today, :name=> "GOOGLE IRELAND LIMITED",
:address=> "1ST & 2ND FLOOR ,GORDON HOUSE ,BARROW STREET ,DUBLIN 4"
} or false or nil
According to EU law, or at least as Austria sees it, it's mandatory to verify the VAT number of every new customer, but also to check the VAT number periodicaly. To prove that you have checked the VAT number, the web service can return a request_identifier.
To receive a request_identifier you need to pass your own VAT number in the options hash. In this example, Google (VAT IE6388047V) is checking the validity of eBays VAT number (LU21416127)
Valvat.new("LU21416127").exists?(requester: "IE6388047V")
=> {
:country_code=>"LU", :vat_number => "21416127", :valid => true,
:request_date => Date.today, :name=>"EBAY EUROPE S.A R.L.",
:address => "22, BOULEVARD ROYAL\nL-2449 LUXEMBOURG",
:company_type => nil, :request_identifier => "some_uniq_string"
} or false or nil
If the given requester is invalid, a Valvat::InvalidRequester error is thrown.
When requesting a request_identifier for a GB VAT number, the requester must be your own GB number; a EU VAT number won't work.
Note that when validating UK VAT numbers using the HMRC service, the detail output is modified to match the one from VIES more closely with slight differences remaining:
- The
request_datewill actually be a (more precise)Timeinstead of aDate - The
addressstring will join lines using\ninstead of,so it's more acurate and can be displayed nicely.
Handling of maintenance errors
From time to time the VIES web service for one or all member states is down for maintenance. To handle this kind of temporary errors, Valvat::Lookup#validate returns nil by default to indicate that there is no way at the moment to say if the given VAT is valid or not. You should revalidate the VAT later. If you prefer an error, use the raise_error option:
Valvat.new("IE6388047V").exists?(raise_error: true)
This raises Valvat::ServiceUnavailable or Valvat::MemberStateUnavailable instead of returning nil.
Visit https://ec.europa.eu/taxation_customs/vies/#/help for more accurate information at what time the service for a specific member state will be down.
Handling of other errors
All other errors accuring while validating against the web service are raised and must be handled by you. These include:
Valvat::InvalidRequesterValvat::BlockedErrorValvat::RateLimitErrorValvat::Timeout- all IO errors
If you want to suppress all known error cases. Pass in the raise_error option set to false:
Valvat.new("IE6388047V").exists?(raise_error: false)
This will return nil instead of raising a known error.
Set options for the Net::HTTP client
Use the :http key to set options for the http client. These options are directly passed to Net::HTTP.start.
For example to set timeouts:
Valvat.new("IE6388047V").exists?(http: { open_timeout: 10, read_timeout: 10 })
Skip local validation before lookup
To prevent unnecessary requests, valvat performs a local syntax check before making the request to the web service. If you want to skip this step (for any reason), set the :skip_local_validation option to true.
Experimental checksum verification
valvat allows to check vat numbers from AT, BE, BG, DE, DK, ES, FR, FI, GR, IE, IT, LU, NL, PL, PT, SE and SI against a checksum calculation. All other member states will fall back to a basic syntax check:
Valvat.new("DE345789003").valid_checksum?
# => true or false
These results are more valuable than a simple syntax check, but keep in mind: they can not replace a lookup via VIES or HMRC.
IMPORTANT This feature was tested against all vat numbers I could get my hand on, but it is still marked as experimental because these calculations are not documented and may return wrong results.
To bypass initializing a Valvat instance:
Valvat::Checksum.validate("DE345789003")
# => true or false
Configuration
Instead of passing in the same options again and again, Valvat allows to alter its default configuration. This feature is intended to be used when initializing your application (for example in a Rails initializer file).
Valvat.configure(
raise_error: true,
http: { read_timeout: 5 },
uk: {
sandbox: true, # Use sandbox mode
client_id: <client_id> # Required for HMRC API v2 authentication
client_secret: <client_secret> # Required for HMRC API v2 authentication
}
)
To see all options and the defaults, take a look at valvat/configuration.
Usage with ActiveModel / Rails
Loading
When the valvat gem is required and ActiveModel is already loaded, everything will work fine out of the box. If your load order differs just add
require 'active_model/validations/valvat_validator'
after ActiveModel has been loaded.
Simple syntax validation
To validate the attribute vat_number add this to your model:
class MyModel < ActiveRecord::Base
validates :vat_number, valvat: true
end
Additional lookup validation
To additionally perform an lookup via VIES:
validates :vat_number, valvat: { lookup: true }
To also perform an lookup via HMRC for UK VAT numbers:
validates :vat_number, valvat: { lookup: { uk: { client_id: '<client_id>', client_secret: '<client_secret>' } } }
By default this will validate to true if the web service is down. To fail in this case simply add the :fail_if_down option:
validates :vat_number, valvat: { lookup: { fail_if_down: true } }
You can pass in any options accepted by Valvat::Lookup#validate:
validates :vat_number, valvat: { lookup: { raise_error: true, http: { read_timeout: 12 } } }
Additional (and experimental) checksum validation
To additionally perform a checksum valid
