VAT Management in Adobe Commerce
During last years I worked quite a lot with different Commerce B2B projects, and every of them had a different requirements for taxation processes. One of the most important parts always was VAT management. And after answering same questions again and again I came up with a small overview how Adobe Commerce / Magento manage VAT.
What is VAT?
A value added tax identification number or VAT identification number is an identifier used in many countries, including the countries of the European Union, for value added tax purposes.
In the EU, a VAT identification number can be verified online at the EU's official VIES website. It confirms that the number is currently allocated and can provide the name or other identifying details of the entity to whom the identifier has been allocated. However, many national governments will not give out VAT identification numbers due to data protection laws.
Why is it important to validate the Tax Number of customers before a transaction takes place?
When selling products or services, you need to issue an invoice and send it to your customer. In it, you need to specify if your customer is a business customer or an end consumer (consumer). This is because transactions with business customers (B2B) typically have a different tax treatment compared to transactions with consumers (B2C). Some transactions, like cross-border services to business customers, often do not incur indirect taxes at all.
In order to determine if you need to charge indirect taxes (like VAT) to your customers in cross-border situations, you need to check if you’re dealing with a business entity or with a private individual. For this, you need to check (“validate”) the tax identification number of your customer. (https://www.fonoa.com/blog/why-is-it-essential-to-validate-the-tax-number-of-your-customers-before-a-transaction-takes-place)
Doing this wrong means you either charge tax when you shouldn’t and add unnecessary costs to your product, or that you don’t charge tax when you should which means you are not collecting the tax you should but still need to remit to the tax authorities. Both obvious situations that any business wants to avoid.
That’s why it is important to verify if your customers are business customers or consumers. One simple way of checking your customer’s tax status is to validate if your customer is VAT registered in VIES.
What is VIES?
VIES (the VAT Information Exchange System) is an online system developed for this purpose by the European Commission. Through VIES, businesses can verify individual VAT numbers across the EU online quickly and in a simple way.
VAT ID Validation in Magento
By default, Adobe Commerce uses the EU VIES service to validate VAT.
As any 3rd party service may happen, that API response time is slow or service is not available. But this is not influence general user experience. In this case validation will be marked as Failed and Admin can repeat it again later on.
VAT ID Validation automatically assigns one of the four default customer groups to customers according to VAT ID validation results:
Domestic
Intra-EU
Invalid VAT ID
Validation error
You can create new customer groups for VAT ID Validation or use existing groups, if they comply with your business logic. When configuring VAT ID Validation, you must assign each of the created customer groups as a default for customers with appropriate VAT ID validation results. (https://docs.magento.com/user-guide/v2.3/tax/vat-validation-configure.html)
If VAT validation is enabled, this may affect the Order Placement time for different countries, because the response time from VIES varies from country to country.
There are 2 fields for the VAT ID in Magento. The first one is related to the customer entity, which won't be checked against the VAT validation service "VIES". It has only some information character, but it has no impact to the customer group assignment.
The second VAT ID field is related to the customer address entity, which will be checked against the VIES service. If the VAT ID is valid, customer is assigned to defined customer group (e.g. "Valid VAT ID").
VAT ID Format
The full identifier starts with an ISO 3166-1 alpha-2 (2 letters) country code (except for Greece, which uses the ISO 639-1 language code EL for the Greek language, instead of its ISO 3166-1 alpha-2 country code GR, and Northern Ireland, which uses the code XI when trading with the EU) and then has between 2 and 13 characters. The identifiers are composed of numeric digits in most countries, but in some countries they may contain letters.
Foreign companies that trade with private individuals and non-business organisations in the EU may have a VATIN starting with "EU" instead of a country code, e.g. Godaddy EU826010755 and Amazon (AWS) EU826009064.
If user enters VAT ID with dashes, Magento escapes dashes before it sends customer VAT number to the validation service. Magento also escapes country code from the VAT number if country is in EU. For example, if customer enters one of the following VAT:
0123456-7
01234567
DE0123456-7
DE01234567
After sanitizing, Magento will transform all of them to the following format before sending: 01234567. And all of them will be valid all in all.
vendor/magento/module-customer/Model/Vat.php:190
$vatNumberSanitized = $this->isCountryInEU($countryCode)
? str_replace([' ', '-', $countryCodeForVatNumber], ['', '', ''], $vatNumber)
: str_replace([' ', '-'], ['', ''], $vatNumber);
- VAT will be saved in database in the format in which the user entered it.
- If customer country entered on checkout and 2 letters country code before VAT ID are not equal, the result of validation will be invalid
Adobe Commerce (Magento) Code
The following method is the main extension point of the VAT ID validation:
vendor/magento/module-customer/Model/Vat.php:162
/**
* Send request to VAT validation service and return validation result
*
* @param string $countryCode
* @param string $vatNumber
* @param string $requesterCountryCode
* @param string $requesterVatNumber
*
* @return DataObject
*/
public function checkVatNumber($countryCode, $vatNumber, $requesterCountryCode = '', $requesterVatNumber = '')
Vat Validation URL for EU Vat numbers is stored as a constant:
vendor/magento/module-customer/Model/Vat.php:47
/**
* WSDL of VAT validation service
*
*/
const VAT_VALIDATION_WSDL_URL = 'https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl';
Theoretically this may lead to situation that URL will be changed some time in the future and service will not be active anymore. Also that is the first entry point, where you, as developer, have to start to check if service is available.
Main cases when VAT ID validation is performed:
- Customer Address save action
vendor/magento/module-customer/Observer/AfterAddressSaveObserver.php:152
/**
* Address after save event handler
*
* @param Observer $observer
* @return void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function execute(Observer $observer)
{
...
$result = $this->_customerVat->checkVatNumber(
$customerAddress->getCountryId(),
$customerAddress->getVatId()
);
...
}
- Checkout. Set customer’s billing address
vendor/magento/module-quote/Observer/Frontend/Quote/Address/VatValidator.php:43
/**
* Validate VAT number
*
* @param \Magento\Quote\Model\Quote\Address $quoteAddress
* @param \Magento\Store\Model\Store|int $store
* @return \Magento\Framework\DataObject
*/
public function validate(\Magento\Quote\Model\Quote\Address $quoteAddress, $store)
- API requests to Magento that are setting billing address to customer’s quote:
GraphQL
mutation {
setBillingAddressOnCart(
input: {
cart_id: "{{cart_id}}"
billing_address: {
customer_address_id: {{address_id}}
same_as_shipping: true
}
}
) {
cart {
billing_address {
firstname
lastname
company
street
city
region{
code
label
}
postcode
telephone
country {
code
label
}
}
}
}
}
REST
/V1/guest-carts/:cartId/payment-information
/**
* Set payment information and place order for a specified cart.
*
* @param string $cartId
* @param string $email
* @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
* @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress
* @throws \Magento\Framework\Exception\CouldNotSaveException
* @return int Order ID.
*/
public function savePaymentInformationAndPlaceOrder(
$cartId,
$email,
\Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
);
Conclusion
I really hope that I helped somebody in understanding how VAT validation process works. If you need a support in Magento configuration, then please follow this documentation: VAT ID Validation | Adobe Commerce 2.4 User Guide
Related Articles
-
2021-11-01Integrating Adobe Commerce Inventory with external ERP SystemsSharing a success story of the most challenging integration of Magento MSI with an external ERP System.
-
2022-04-22Disaster Recovery in an Adobe Commerce Cloud environmentAdobe Commerce Cloud has an automated backup process by design. So, if you act quickly after a notice, you have a good chance to restore your system almost completely.
-
2021-12-29Recurring payments and Credit Card Management in Adobe CommerceStep-by-step guide on how to add recurring payments for subscription business in Adobe Commerce using Payone as PSP
-
2022-06-02Queue Systems integration reviewTaking a close look at existing queue solutions for Adobe Commerce
-
2021-08-19The E-Commerce Experience Wish ListLearn what consumers want most in a digital customer experience—and how you can deliver it to them.