How to Validate Polish Tax Identification Number (NIP) in Laravel


To ensure a given NIP number is correct, it must be validated against specific rules. This article shows how to encapsulate these rules in the form of a ValidationRule that can be used in Laravel when validating request input.

Explanation

Polish NIP number has to consist of 10 digits. Each digit is assigned a fixed multiplier:

$weights = [6, 5, 7, 2, 3, 4, 5, 6, 7];

Next each digit is multiplied by its corresponding weight:

for ($i = 0; $i < 9; $i++) {
    $sum += intval($nip[$i]) * $weights[$i];
}

Then everything is added together as the $sum variable.

Lastly we perform the modulo operation twice. The first operation uses modulo 11, which is part of the official checksum algorithm for NIP numbers. It reduces the weighted sum to a value between 0 and 10 and helps detect common input errors such as digit swaps or typos.

The second operation (modulo 10) ensures the result is always a single digit, because the control digit in a NIP number must be a single digit. This step also handles the edge case where the modulo 11 result equals 10, which would otherwise be invalid.

Finally, this calculated value is compared to the last digit of the given number.

return ($sum % 11) % 10 === intval($nip[9]);

If they match, the NIP number is valid.

Solution

Following example shows a complete class.

<?php

namespace App\Rules;

use Closure;
use Exception;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;

class NIP implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $nip = Str::trim($value);
        $length = Str::length($nip);

        if ($length < 10) {
            $fail('The provided NIP number is too short. It must contain exactly 10 digits.');
        }

        if ($length > 10) {
            $fail('The provided NIP number is too long. It must contain exactly 10 digits.');
        }

        if (!$this->isValid($nip)) {
            $fail('The provided NIP number is invalid.');
        }
    }

    private function isValid(string $nip): bool
    {
        $validator = Validator::make(['value' => $nip], ['value' => 'digits:10']);

        if ($validator->fails()) {
            return false;
        }

        try {
            $weights = [6, 5, 7, 2, 3, 4, 5, 6, 7];
            $sum = 0;

            for ($i = 0; $i < 9; $i++) {
                $sum += intval($nip[$i]) * $weights[$i];
            }

            return ($sum % 11) % 10 === intval($nip[9]);
        } catch (Exception $e) {
            return false;
        }
    }
}

Usage

Using newly introduces rule against Request input is trivial.

<?php

namespace App\Http\Requests\Auth;

use App\Rules\NIP;
use Illuminate\Foundation\Http\FormRequest;

class RegisterRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'tax_id' => [
                'required',
                'string',
                new NIP(),
            ],
        ];
    }

    public function messages(): array
    {
        return [
            'tax_id.required' => 'The NIP number is required.',
        ];
    }
}

Examples

Here are to simple examples:

  • Valid NIP - 8567346215
  • Invalid NIP - 8567346214

Why is that?

Both numbers have the same first 9 digits. When you sum weights on 856734621, the correct control digit is 5. Hence the first example is valid.

Conclusion

This should be it to ensure given NIP number is real and meaningful.


Words: 462
Published in: Laravel · PHP

Related Articles