Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Vatin Bundle Laravel Package

ddeboer/vatin-bundle

Symfony bundle integrating ddeboer/vatin. Validates EU VAT ID (VATIN) format via Symfony Validator and can optionally verify existence via the VIES SOAP service. Also exposes services for direct VATIN validation and VIES lookups.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package in your Laravel project (Symfony 8+ compatible):
    composer require ddeboer/vatin-bundle
    
  2. Register the bundle in config/app.php under providers:
    Ddeboer\VatinBundle\DdeboerVatinBundle::class,
    
  3. Publish configuration (if needed) via:
    php artisan vendor:publish --tag=vatin-config
    

First Use Case: Form Validation

Add the Vatin constraint to a Laravel Form Request or model property:

use Ddeboer\VatinBundle\Validator\Constraints\Vatin;

class StoreCompanyRequest extends FormRequest
{
    #[Vatin(checkExistence: true)] // Validates format + existence via VIES
    public string $vat_number;
}

Test with:

php artisan request:validate StoreCompanyRequest --input "vat_number=NL123456789B01"

Implementation Patterns

Core Workflows

1. Validation in Form Requests

Leverage Laravel’s built-in validation pipeline:

public function rules()
{
    return [
        'vat_number' => [
            'required',
            new Vatin(), // Format validation
            // new Vatin(checkExistence: true), // Format + existence (VIES API)
        ],
    ];
}

Tip: Use checkExistence: false by default to avoid API calls during development.

2. Manual Validation in Services

Inject the validator via Laravel’s container:

use Ddeboer\VatinBundle\Validator\VatValidatorInterface;

class CompanyService
{
    public function __construct(
        private VatValidatorInterface $validator
    ) {}

    public function validateVat(string $vatNumber): bool
    {
        return $this->validator->isValid($vatNumber);
    }
}

3. API Integration (VIES)

Use the VIES client for real-time checks:

use Ddeboer\VatinBundle\Vies\Client;

class VatApiService
{
    public function __construct(
        private Client $viesClient
    ) {}

    public function checkVatExistence(string $countryCode, string $vatNumber): bool
    {
        try {
            return $this->viesClient->checkVat($countryCode, $vatNumber)->isValid();
        } catch (\Exception $e) {
            // Log and handle API failures
            return false;
        }
    }
}

4. Custom Validation Rules

Extend Laravel’s validation rules for reusable logic:

use Ddeboer\VatinBundle\Validator\VatValidatorInterface;
use Illuminate\Contracts\Validation\Rule;

class ValidVat extends Rule
{
    public function __construct(
        private VatValidatorInterface $validator,
        private bool $checkExistence = false
    ) {}

    public function passes($attribute, $value): bool
    {
        return $this->validator->isValid($value, $this->checkExistence);
    }

    public function message(): string
    {
        return 'The :attribute is not a valid VAT number.';
    }
}

Usage:

'vat_number' => ['required', new ValidVat($validator, checkExistence: true)],

Integration Tips

Laravel-Specific Adjustments

  1. Service Binding: Bind the bundle’s services to Laravel’s container in AppServiceProvider:

    public function register()
    {
        $this->app->bind(
            \Ddeboer\VatinBundle\Validator\VatValidatorInterface::class,
            \Ddeboer\VatinBundle\Validator\VatValidator::class
        );
        $this->app->bind(
            \Ddeboer\VatinBundle\Vies\ClientInterface::class,
            \Ddeboer\VatinBundle\Vies\Client::class
        );
    }
    
  2. Configuration: Override default settings in config/vatin.php:

    'vies' => [
        'timeout' => 5, // Seconds for VIES API calls
        'cache' => true, // Enable caching for API responses
    ],
    
  3. Testing: Mock the VIES client in tests to avoid real API calls:

    $this->mock(Ddeboer\VatinBundle\Vies\Client::class)
         ->shouldReceive('checkVat')
         ->andReturn(new \Ddeboer\VatinBundle\Vies\Response(true));
    

Performance Optimization

  • Cache VIES Responses: Use Laravel’s cache to store VIES API responses:

    $cacheKey = "vies_{$countryCode}_{$vatNumber}";
    $response = Cache::remember($cacheKey, now()->addHours(1), function () use ($viesClient, $countryCode, $vatNumber) {
        return $viesClient->checkVat($countryCode, $vatNumber);
    });
    
  • Batch Validation: For bulk operations (e.g., importing companies), validate VAT numbers offline first, then sync with VIES in batches.


Gotchas and Tips

Pitfalls

  1. VIES API Unreliability:

    • The VIES service is known for downtime. Always wrap API calls in try-catch blocks:
      try {
          $response = $viesClient->checkVat('NL', '123456789B01');
      } catch (\Symfony\Component\Validator\Exception\ValidatorException $e) {
          // Fallback to local validation or notify admin
      }
      
    • Workaround: Implement a retry mechanism with exponential backoff.
  2. Symfony Dependency Conflicts:

    • If your Laravel project uses older Symfony components (e.g., symfony/http-client:^5.0), conflicts may arise with Symfony 8.
    • Solution: Pin compatible versions in composer.json:
      "symfony/http-client": "^6.0",
      "symfony/validator": "^6.0"
      
  3. Non-EU VAT Numbers:

    • The package primarily supports EU VAT numbers. Non-EU numbers (e.g., US, Canada) will fail validation.
    • Workaround: Add a custom rule to bypass validation for non-EU countries:
      public function passes($attribute, $value): bool
      {
          if (!Str::startsWith($value, ['US', 'CA', 'GB'])) {
              return $this->validator->isValid($value);
          }
          return true;
      }
      
  4. Attribute Syntax in Older PHP:

    • If using PHP < 8.0, replace attributes with annotations:
      /**
       * @Vatin(checkExistence=true)
       */
      protected string $vatNumber;
      
  5. Case Sensitivity:

    • VAT numbers are case-sensitive (e.g., NL123456789B01 vs. nl123456789b01). Normalize inputs:
      $vatNumber = strtoupper(trim($request->vat_number));
      

Debugging Tips

  1. Enable Debugging for VIES: Add debug logging to config/vatin.php:

    'debug' => env('APP_DEBUG', false),
    

    This will log VIES API requests/responses during development.

  2. Validate Format Locally: Test VAT number formats offline before hitting the VIES API:

    $validator = app(VatValidatorInterface::class);
    if (!$validator->isValid($vatNumber, false)) {
        // Format is invalid; no need to call VIES
        return false;
    }
    
  3. Handle Partial Failures: The VIES API may return partial failures (e.g., valid format but invalid number). Check the response:

    $response = $viesClient->checkVat('NL', '123456789B01');
    if ($response->isValid()) {
        // Valid
    } elseif ($response->isInvalid()) {
        // Invalid number
    } else {
        // Unknown (e.g., API error)
    }
    

Extension Points

  1. Custom Validation Logic: Extend the VatValidator to add business rules:

    class CustomVatValidator extends \Ddeboer\VatinBundle\Validator\VatValidator
    {
        public function isValid(string $vatNumber, bool $checkExistence = false): bool
        {
            // Add custom logic (e.g., blacklist certain VAT numbers)
            if (in_array($vatNumber, $this->blacklistedVats)) {
                return false;
            }
            return parent::isValid($vatNumber, $checkExistence);
        }
    }
    
  2. Override VIES Client: Replace the default VIES client with a custom implementation (e.g., for testing or caching):

    $this->app->bind(
        \Ddeboer\VatinBundle\Vies
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui