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

Vat Calculator Laravel Package

mpociot/vat-calculator

EU VAT/MOSS calculator for Laravel/Cashier or standalone PHP. Calculate gross prices and tax rates by country/postal code, handle B2B/B2C rules, and validate EU VAT numbers. Simple API via facade or instance.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require mpociot/vat-calculator

For Laravel, no additional steps are required. For standalone PHP, instantiate the calculator:

$calculator = new \Mpociot\VatCalculator\VatCalculator();
$calculator->setBusinessCountryCode('DE'); // Set your business country
  1. First Use Case: Calculate VAT for a customer in Germany (DE):

    $grossPrice = \Mpociot\VatCalculator\Facades\VatCalculator::calculate(24.00, 'DE');
    // Returns 28.56 (24.00 + 19% VAT)
    
  2. Key Methods to Explore:

    • calculate($netPrice, $countryCode, $postalCode, $isCompany)
    • getTaxRateForLocation($countryCode, $postalCode)
    • isValidVATNumber($vatNumber)
    • getVATDetails($vatNumber)

Implementation Patterns

Core Workflows

1. Pricing Logic in E-Commerce

  • Cart Calculation: Iterate over cart items, calculate VAT per item based on customer location (country + postal code):
    foreach ($cartItems as $item) {
        $grossPrice = VatCalculator::calculate($item->netPrice, $item->country, $item->postalCode);
        $item->grossPrice = $grossPrice;
    }
    
  • Business vs. Individual: Use $isCompany flag to bypass VAT for B2B transactions (if customer is a valid company):
    $isCompany = VatCalculator::isValidVATNumber($customer->vatNumber);
    $grossPrice = VatCalculator::calculate($netPrice, $country, $postalCode, $isCompany);
    

2. VAT Validation in Checkout

  • Pre-Check: Validate if VAT collection is required for the country:
    if (VatCalculator::shouldCollectVAT($countryCode)) {
        // Proceed with VAT validation
    }
    
  • VAT Number Validation: Use in form requests or API validation:
    use Mpociot\VatCalculator\Rules\ValidVatNumber;
    
    $validator = Validator::make($request->all(), [
        'vat_number' => ['required', new ValidVatNumber],
    ]);
    
  • Fallback for Unavailable API: Handle VATCheckUnavailableException gracefully (e.g., cache results or show manual input field):
    try {
        $valid = VatCalculator::isValidVATNumber($vatNumber);
    } catch (VATCheckUnavailableException $e) {
        $valid = false; // Or use cached result
    }
    

3. Subscription Tax Rates (Cashier Integration)

  • Dynamic Tax Rates: Set tax rates per customer before subscription:
    $user->useTaxFrom('FR')->asBusiness(); // For B2B in France
    $user->subscription('monthly')->create($paymentMethod);
    
  • Model Trait: Extend Billable trait for EU-specific tax logic:
    use Mpociot\VatCalculator\Traits\BillableWithinTheEU;
    
    class User extends Model implements BillableContract {
        use Billable, BillableWithinTheEU {
            BillableWithinTheEU::taxPercentage insteadof Billable;
        }
    }
    

4. Frontend Integration

  • JavaScript Calculation: Use the included JS library to calculate VAT client-side:
    window.VatCalculator.calculate(24.00, 'IT', '00142', false)
        .then(result => console.log(result.grossPrice));
    
  • Real-Time Updates: Trigger VAT recalculation on country/postal code changes:
    document.getElementById('country').addEventListener('change', () => {
        VatCalculator.calculate(netPrice, countryValue, postalCode, isCompany)
            .then(updateUI);
    });
    

Integration Tips

Laravel-Specific

  • Service Providers: Bind the calculator to the container for dependency injection:
    $this->app->singleton(VatCalculator::class, function ($app) {
        $calculator = new VatCalculator();
        $calculator->setBusinessCountryCode(config('vat_calculator.business_country'));
        return $calculator;
    });
    
  • Configuration: Publish and customize VAT rules:
    php artisan vendor:publish --provider="Mpociot\VatCalculator\VatCalculatorServiceProvider"
    
    Update config/vat_calculator.php to override default rates or enable SOAP fault forwarding:
    'forward_soap_faults' => true,
    'soap_timeout' => 10,
    

Standalone PHP

  • Session Persistence: Store business country code in session for multi-request workflows:
    session(['business_country' => $countryCode]);
    $calculator->setBusinessCountryCode($_SESSION['business_country']);
    
  • Caching: Cache VAT rates and validation results to reduce API calls:
    $cacheKey = "vat_rate_{$countryCode}_{$postalCode}";
    $taxRate = cache()->remember($cacheKey, 3600, fn() =>
        VatCalculator::getTaxRateForLocation($countryCode, $postalCode)
    );
    

API Consumption

  • Rate Limiting: Implement retry logic for SOAP API failures (e.g., VIES or HMRC):
    $attempts = 0;
    while ($attempts < 3) {
        try {
            $details = VatCalculator::getVATDetails($vatNumber);
            break;
        } catch (VATCheckUnavailableException $e) {
            $attempts++;
            sleep(2 ** $attempts); // Exponential backoff
        }
    }
    
  • UK VAT Specifics: Register with HMRC Developer Hub and set credentials:
    HMRC_CLIENT_ID=your_client_id
    HMRC_CLIENT_SECRET=your_client_secret
    

Gotchas and Tips

Pitfalls

  1. SOAP API Dependencies:

    • VIES API Unavailability: The package relies on the EU’s VIES API for VAT validation. If unavailable, it throws VATCheckUnavailableException. Handle this gracefully (e.g., fallback to format validation or manual review).
      • Tip: Enable forward_soap_faults in config to debug SOAP errors.
    • UK VAT API: Requires HMRC registration. Without credentials, UK VAT validation fails silently (returns false).
      • Tip: Test UK VAT numbers in a sandbox environment first.
  2. Business Country Code:

    • Forgetting to set setBusinessCountryCode() leads to incorrect B2B calculations (returns 0% VAT).
      • Fix: Set it globally in config or per request:
        VatCalculator::setBusinessCountryCode('DE');
        
    • Edge Case: If your business operates in multiple countries, consider storing the code per user or order.
  3. Postal Code Sensitivity:

    • VAT rates can vary by region (e.g., Germany’s reduced rate for certain islands). Always pass the postal code for accurate calculations.
      • Example: VatCalculator::calculate(100, 'DE', '27474') may return a different rate than '27472'.
  4. Cashier Version Conflict:

    • The BillableWithinTheEU trait is incompatible with Cashier v13+ due to removed taxPercentage method.
      • Workaround: Downgrade Cashier or manually implement tax logic in createSubscription().
  5. Validation Rule Behavior:

    • The ValidVatNumber rule returns false if the VIES API is down, not null or Invalid.
      • Tip: Use sometimes or custom validation logic to handle this:
        $validator->sometimes('vat_number', 'required|valid_vat_number', function ($input) {
            return $input->has('country') && in_array($input->country, ['DE', 'FR', 'IT']);
        });
        
  6. Floating-Point Precision:

    • VAT calculations may result in floating-point inaccuracies (e.g., 24.00 * 1.19 = 28.559999999999996).
      • Fix: Round results to 2 decimal places:
        $grossPrice = round(VatCalculator::calculate($netPrice, $country), 2);
        
  7. Caching VAT Rates:

    • Cached rates may become stale if tax laws change. Implement a cache invalidation strategy (e
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky