A B2B shop was charging 23% VAT to customers from Germany and France who provided EU VAT numbers. Under the reverse charge principle, VAT should be 0% for EU businesses with a VAT number. The problem generated customer complaints and tax risk. Fix time: 8 hours.
Symptoms
- EU customers with VAT number pay full 23% VAT
- Invoices issued with incorrect VAT rate
- The “VAT number” field in checkout is visible but does not affect price
Configuration
# For B2B reverse charge you need a Customer Tax Class # Stores > Tax > Customer Tax Classes: add "EU B2B Customer" # Stores > Tax > Tax Rules: # - Customer Tax Class: EU B2B Customer # - Product Tax Class: Taxable Goods # - Tax Rate: 0% EU # - Priority: 0 (higher than standard VAT)
<?php
// Observer that changes tax class after EU VAT number validation
class VatValidationObserver implements \Magento\Framework\Event\ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer): void
{
$customer = $observer->getCustomer();
$vatNumber = $customer->getTaxvat();
if ($this->isValidEuVat($vatNumber)) {
$customer->setGroupId($this->getB2bGroupId());
$customer->setTaxClassId($this->getEuB2bTaxClassId());
}
}
private function isValidEuVat(string $vatNumber): bool
{
$client = new \SoapClient('https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl');
$countryCode = substr($vatNumber, 0, 2);
$number = substr($vatNumber, 2);
try {
$result = $client->checkVat(['countryCode' => $countryCode, 'vatNumber' => $number]);
return $result->valid;
} catch (\Exception $e) {
return false;
}
}
}
Takeaways
Reverse charge for EU B2B requires VAT number validation via VIES API, a separate tax class for B2B customers and an appropriate tax rule with 0% VAT. Always consult tax configuration with an accountant.
