The CompanyRegistrationService provides validation for company registration numbers across multiple European countries. This service validates format, checksums, and provides detailed information about each registration type.
Different European countries use different systems for company registration:
use Darvis\UblPeppol\CompanyRegistrationService;
$service = new CompanyRegistrationService();
// Validate Dutch KVK number
$result = $service->validate('12345678', 'NL');
if ($result['valid']) {
echo "Valid KVK number: " . $result['formatted'];
echo "Type: " . $result['type_name'];
} else {
echo "Invalid: " . $result['error'];
}
All validation methods return a consistent array structure:
[
'valid' => true, // Boolean: number is valid
'country' => 'NL', // String: ISO country code
'country_name' => 'Netherlands', // String: Country name
'number' => '12345678', // String: Original input (cleaned)
'formatted' => '12345678', // String: Formatted number (null if invalid)
'type' => 'KVK', // String: Registration type code
'type_name' => 'Kamer van Koophandel', // String: Full type name
'error' => null // String|null: Error message if invalid
]
Format: 8 digits
$result = $service->validate('12345678', 'NL');
// Valid examples:
// 12345678
// 68184566
// Invalid examples:
// 1234567 (too short)
// 123456789 (too long)
// ABC12345 (contains letters)
Response:
[
'valid' => true,
'country' => 'NL',
'country_name' => 'Netherlands',
'number' => '12345678',
'formatted' => '12345678',
'type' => 'KVK',
'type_name' => 'Kamer van Koophandel',
'error' => null,
]
Format: 10 digits with mod97 checksum validation
The last 2 digits are a checksum: 97 - (first 8 digits mod 97)
$result = $service->validate('0681845662', 'BE');
// Valid example: 0681845662
// Calculation: 97 - (06818456 % 97) = 97 - 35 = 62 ✓
// Invalid examples:
// 0681845663 (wrong checksum)
// 068184566 (too short)
Response:
[
'valid' => true,
'country' => 'BE',
'country_name' => 'Belgium',
'number' => '0681845662',
'formatted' => '0681845662',
'type' => 'KBO',
'type_name' => 'Kruispuntbank van Ondernemingen',
'error' => null,
]
Format: 1 letter + 6 digits (usually starts with ‘B’)
$result = $service->validate('B123456', 'LU');
// Valid examples:
// B123456
// A999999
// b123456 (automatically uppercased)
// Invalid examples:
// 123456 (missing letter)
// BB12345 (two letters)
// B12345 (too short)
Response:
[
'valid' => true,
'country' => 'LU',
'country_name' => 'Luxembourg',
'number' => 'B123456',
'formatted' => 'B123456',
'type' => 'RCS',
'type_name' => 'Registre de Commerce et des Sociétés',
'error' => null,
]
SIREN Format: 9 digits (company identifier)
$result = $service->validate('732829320', 'FR');
// Response includes:
[
'valid' => true,
'country' => 'FR',
'country_name' => 'France',
'number' => '732829320',
'formatted' => '732829320',
'type' => 'SIREN',
'type_name' => 'Système d\'Identification du Répertoire des Entreprises',
'error' => null,
]
SIRET Format: 14 digits (establishment identifier = SIREN + NIC)
$result = $service->validate('73282932000074', 'FR');
// Response includes:
[
'valid' => true,
'country' => 'FR',
'country_name' => 'France',
'number' => '73282932000074',
'formatted' => '73282932000074',
'type' => 'SIRET',
'type_name' => 'Système d\'Identification du Répertoire des Établissements',
'siren' => '732829320', // First 9 digits
'nic' => '00074', // Last 5 digits
'error' => null,
]
Format: HRA or HRB + 1-6 digits
$result = $service->validate('HRB 12345', 'DE');
// Valid examples:
// HRB 12345
// HRB12345 (space optional)
// HRA 1
// hrb 999999 (case insensitive)
// Invalid examples:
// HR 12345 (missing A or B)
// HRC 12345 (invalid letter)
// HRB 1234567 (too many digits)
Response:
[
'valid' => true,
'country' => 'DE',
'country_name' => 'Germany',
'number' => 'HRB 12345',
'formatted' => 'HRB 12345',
'type' => 'HRB',
'type_name' => 'Handelsregister Abteilung B (Kapitalgesellschaften)',
'registration_number' => '12345',
'error' => null,
]
$countries = $service->getSupportedCountries();
// Returns:
[
'NL' => [
'name' => 'Netherlands',
'type' => 'KVK',
'type_name' => 'Kamer van Koophandel',
'format' => '8 digits',
'example' => '12345678',
],
'BE' => [...],
'LU' => [...],
'FR' => [...],
'DE' => [...],
]
The service automatically cleans input by removing:
Letters are preserved for Luxembourg (RCS) and Germany (Handelsregister).
// All these are equivalent:
$service->validate('12345678', 'NL');
$service->validate('12 34 56 78', 'NL');
$service->validate('12.34.56.78', 'NL');
$service->validate('12-34-56-78', 'NL');
Create a custom validation rule:
use Darvis\UblPeppol\KvkService;
$request->validate([
'kvk_number' => [
'required',
function ($attribute, $value, $fail) use ($country) {
$service = app(KvkService::class);
$result = $service->validate($value, $country);
if (!$result['valid']) {
$fail($result['error'] ?? 'Invalid company registration number');
}
}
]
]);
Bind to Laravel’s service container:
// In AppServiceProvider
use Darvis\UblPeppol\KvkService;
$this->app->singleton(KvkService::class, function ($app) {
return new KvkService();
});
// Use anywhere
$service = app(KvkService::class);
When validation fails, the response includes a descriptive error message:
$result = $service->validate('123', 'NL');
// Returns:
[
'valid' => false,
'country' => 'NL',
'country_name' => 'Netherlands',
'number' => '123',
'formatted' => null,
'type' => 'KVK',
'type_name' => 'Kamer van Koophandel',
'error' => 'Invalid format. Expected 8 digits.',
]
$result = $service->validate('12345', 'US');
// Returns:
[
'valid' => false,
'country' => 'US',
'number' => '12345',
'type' => null,
'error' => 'Unsupported country code: US',
]
Store registration numbers without formatting:
$result = $service->validate($input, $country);
if ($result['valid']) {
// Store the cleaned number
$company->registration_number = $result['number'];
$company->registration_type = $result['type'];
$company->save();
}
Use the formatted version for display:
$result = $service->validate($company->registration_number, $company->country);
echo $result['formatted']; // HRB 12345 (with space for DE)
Always validate before storing:
public function store(Request $request)
{
$service = app(KvkService::class);
$result = $service->validate($request->kvk_number, $request->country);
if (!$result['valid']) {
return back()->withErrors(['kvk_number' => $result['error']]);
}
// Continue with saving...
}
Show appropriate format hints based on country:
$countries = $service->getSupportedCountries();
foreach ($countries as $code => $info) {
echo "{$info['name']}: {$info['format']} (e.g., {$info['example']})";
}
| Country | Type | Format | Example | Checksum |
|---|---|---|---|---|
| NL | KVK | 8 digits | 12345678 | No |
| BE | KBO | 10 digits | 0681845662 | Yes (mod97) |
| LU | RCS | 1 letter + 6 digits | B123456 | No |
| FR | SIREN | 9 digits | 732829320 | No |
| FR | SIRET | 14 digits | 73282932000074 | No |
| DE | HR | HRA/HRB + 1-6 digits | HRB 12345 | No |