If you are building a B2B SaaS or an e-commerce platform in Europe with Laravel, you eventually hit the same wall: EU VAT validation.
To legally apply the reverse charge mechanism (0% VAT), you must ensure your customer's VAT number is valid using the European Commission's VIES system. But integrating directly with the official VIES API is a pain:
- It frequently goes down or times out.
- It's synchronous and slow, which hurts your checkout conversion rate.
- It limits concurrent requests.
Instead of writing complex fallback logic and caching mechanisms from scratch, let's build a clean, robust Laravel Custom Validation Rule using VatFlow, a serverless proxy that caches VIES data for instant responses.
Let's dive in! π οΈ
Prerequisites
- A Laravel application (v9, v10, or v11).
- A free VatFlow API Key via RapidAPI.
Step 1: Install the SDK
VatFlow provides an official, zero-dependency PHP wrapper that handles auto-retries out of the box. You can check out the source code on GitHub (feel free to drop a β!).
Install it via Composer:
composer require quicreatdev/vatflow-php
Step 2: Configure your API Key
Add your RapidAPI key to your .env file:
VATFLOW_API_KEY=your_rapidapi_key_here
Then, map it in your config/services.php file so Laravel can access it cleanly:
// config/services.php
return [
// ... other services
'vatflow' => [
'key' => env('VATFLOW_API_KEY'),
],
];
Step 3: Create the Custom Validation Rule
Laravel makes it incredibly easy to create custom rules. Run this artisan command:
php artisan make:rule ValidEuVat
Now, open the generated file app/Rules/ValidEuVat.php and implement the logic using the VatFlow client. We will configure it to use cached data (up to 7 days old) to guarantee a response time of a few milliseconds during checkout.
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use VatFlow\VatFlowClient;
use Illuminate\Support\Facades\Log;
class ValidEuVat implements ValidationRule
{
/**
* Run the validation rule.
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
// 1. Initialize the client with your key
$apiKey = config('services.vatflow.key');
if (!$apiKey) {
$fail('The VatFlow API key is missing.');
return;
}
$client = new VatFlowClient($apiKey);
// 2. Call the API (max cache age: 7 days, auto-retry: true)
$response = $client->validate($value, 7, true);
// 3. Handle network or API errors gracefully
if (!$response['success']) {
// We log the error, but we DO NOT fail the validation.
// It's better to temporarily accept the order and check manually later
// rather than losing a customer because the government API is down!
Log::warning('VAT Validation Service Unavailable: ' . $response['error']);
return;
}
// 4. Check if the VAT is actually valid
if (!$response['data']['is_valid']) {
$fail('The provided EU VAT number is invalid or inactive.');
}
}
}
Step 4: Use it in your Controller or Form Request
Now for the beautiful part. You can use your new rule just like any native Laravel rule!
Let's say you have a company registration form:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Rules\ValidEuVat;
class CheckoutController extends Controller
{
public function store(Request $request)
{
$validated = $request->validate([
'company_name' => ['required', 'string', 'max:255'],
'vat_number' => ['required', 'string', new ValidEuVat],
]);
// If the code reaches here, the VAT number is 100% valid!
// Proceed with user creation and 0% VAT billing...
return response()->json(['message' => 'Company registered successfully!']);
}
}
Wrapping up
By moving the VAT validation into a Custom Rule and using a cached proxy like VatFlow, your controllers stay incredibly clean, and your checkout process remains blazing fastβeven when the European Commission's servers are struggling.
Bonus tip: The API response also returns the company's official name and address. Since Laravel Validation Rules aren't meant to modify the Request data, if you want to use this data to auto-fill your customer's profile, you can simply call the VatFlowClient directly in your Controller or within a dedicated Action class right after validation!
If you want to try it out, you can get a free API key and check out the documentation on the VatFlow website. You can also find the PHP package directly on Packagist.
Have you ever struggled with the VIES API in your Laravel projects? Let me know in the comments! π
Top comments (0)