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

A Polish NIP number consists of 10 digits. The first 9 digits are assigned fixed positional weights:

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

Next, each of the first 9 digits is multiplied by its corresponding weight:

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

Then, the results are added to produce 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

The 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 the newly introduced rule with aΒ 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 two examples:

  • Valid NIP - 8567346215
  • Invalid NIP - 8567346214

Why is that?

Both numbers share the same first 9 digits: 856734621. Let’s validate the correct one step by step.

We apply the weights:

8Γ—6 + 5Γ—5 + 6Γ—7 + 7Γ—2 + 3Γ—3 + 4Γ—4 + 6Γ—5 + 2Γ—6 + 1Γ—7

= 48 + 25 + 42 + 14 + 9 + 16 + 30 + 12 + 7

= 203

First modulo operation:

203 % 11 = 5

Second modulo operation:

5 % 10 = 5

The calculated control digit is 5, which matches the last digit of 8567346215, so this NIP is valid.

Conclusion

This validates the format and checksum of a NIP number, but does not guarantee that a business entity with this NIP exists.


Words: 507
Published in: Laravel · PHP
Related Articles