Completed
Push — master ( 1ae344...86776d )
by Marcel
14s
created

VatCalculator   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 655
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 56
c 6
b 0
f 0
lcom 2
cbo 2
dl 0
loc 655
rs 5.8709

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 3
A getIPBasedCountry() 0 15 2
A shouldCollectVAT() 0 6 3
B calculate() 0 18 5
B getClientIP() 0 12 5
B calculateNet() 0 19 6
A getNetPrice() 0 4 1
A getCountryCode() 0 4 1
A setCountryCode() 0 4 1
A getPostalCode() 0 4 1
A setPostalCode() 0 4 1
A getTaxRate() 0 4 1
A isCompany() 0 4 1
A setCompany() 0 4 1
A setBusinessCountryCode() 0 4 1
A getTaxRateForCountry() 0 4 1
C getTaxRateForLocation() 0 25 11
A getTaxValue() 0 4 1
A isValidVATNumber() 0 10 2
A getVATDetails() 0 20 3
A initSoapClient() 0 11 4
A setSoapClient() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like VatCalculator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use VatCalculator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Mpociot\VatCalculator;
4
5
use Illuminate\Contracts\Config\Repository;
6
use Mpociot\VatCalculator\Exceptions\VATCheckUnavailableException;
7
use SoapClient;
8
use SoapFault;
9
10
class VatCalculator
11
{
12
    /**
13
     * VAT Service check URL provided by the EU.
14
     */
15
    const VAT_SERVICE_URL = 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl';
16
17
    /**
18
     * We're using the free ip2c service to lookup IP 2 country.
19
     */
20
    const GEOCODE_SERVICE_URL = 'http://ip2c.org/';
21
22
    protected $soapClient;
23
24
    /**
25
     * All available tax rules and their exceptions.
26
     *
27
     * Taken from: http://ec.europa.eu/taxation_customs/resources/documents/taxation/vat/how_vat_works/rates/vat_rates_en.pdf
28
     *
29
     * @var array
30
     */
31
    protected $taxRules = [
32
        'AT' => [ // Austria
33
            'rate'       => 0.20,
34
            'exceptions' => [
35
                'Jungholz'   => 0.19,
36
                'Mittelberg' => 0.19,
37
            ],
38
        ],
39
        'BE' => [ // Belgium
40
            'rate' => 0.21,
41
        ],
42
        'BG' => [ // Bulgaria
43
            'rate' => 0.20,
44
        ],
45
        'CY' => [ // Cyprus
46
            'rate' => 0.19,
47
        ],
48
        'CZ' => [ // Czech Republic
49
            'rate' => 0.21,
50
        ],
51
        'DE' => [ // Germany
52
            'rate'       => 0.19,
53
            'exceptions' => [
54
                'Heligoland'            => 0,
55
                'Büsingen am Hochrhein' => 0,
56
            ],
57
        ],
58
        'DK' => [ // Denmark
59
            'rate' => 0.25,
60
        ],
61
        'EE' => [ // Estonia
62
            'rate' => 0.20,
63
        ],
64
        'EL' => [ // Hellenic Republic (Greece)
65
            'rate'       => 0.24,
66
            'exceptions' => [
67
                'Mount Athos' => 0,
68
            ],
69
        ],
70
        'ES' => [ // Spain
71
            'rate'       => 0.21,
72
            'exceptions' => [
73
                'Canary Islands' => 0,
74
                'Ceuta'          => 0,
75
                'Melilla'        => 0,
76
            ],
77
        ],
78
        'FI' => [ // Finland
79
            'rate' => 0.24,
80
        ],
81
        'FR' => [ // France
82
            'rate' => 0.20,
83
        ],
84
        'GB' => [ // United Kingdom
85
            'rate'       => 0.20,
86
            'exceptions' => [
87
                // UK RAF Bases in Cyprus are taxed at Cyprus rate
88
                'Akrotiri' => 0.19,
89
                'Dhekelia' => 0.19,
90
            ],
91
        ],
92
        'GR' => [ // Greece
93
            'rate'       => 0.24,
94
            'exceptions' => [
95
                'Mount Athos' => 0,
96
            ],
97
        ],
98
        'HR' => [ // Croatia
99
            'rate' => 0.25,
100
        ],
101
        'HU' => [ // Hungary
102
            'rate' => 0.27,
103
        ],
104
        'IE' => [ // Ireland
105
            'rate' => 0.23,
106
        ],
107
        'IT' => [ // Italy
108
            'rate'       => 0.22,
109
            'exceptions' => [
110
                'Campione d\'Italia' => 0,
111
                'Livigno'            => 0,
112
            ],
113
        ],
114
        'LT' => [ // Lithuania
115
            'rate' => 0.21,
116
        ],
117
        'LU' => [ // Luxembourg
118
            'rate' => 0.17,
119
        ],
120
        'LV' => [ // Latvia
121
            'rate' => 0.21,
122
        ],
123
        'MT' => [ // Malta
124
            'rate' => 0.18,
125
        ],
126
        'NL' => [ // Netherlands
127
            'rate' => 0.21,
128
        ],
129
        'PL' => [ // Poland
130
            'rate' => 0.23,
131
        ],
132
        'PT' => [ // Portugal
133
            'rate'       => 0.23,
134
            'exceptions' => [
135
                'Azores'  => 0.18,
136
                'Madeira' => 0.22,
137
            ],
138
        ],
139
        'RO' => [ // Romania
140
            'rate' => 0.19,
141
        ],
142
        'SE' => [ // Sweden
143
            'rate' => 0.25,
144
        ],
145
        'SI' => [ // Slovenia
146
            'rate' => 0.22,
147
        ],
148
        'SK' => [ // Slovakia
149
            'rate' => 0.20,
150
        ],
151
152
        // Countries associated with EU countries that have a special VAT rate
153
        'MC' => [ // Monaco France
154
            'rate' => 0.20,
155
        ],
156
        'IM' => [ // Isle of Man - United Kingdom
157
            'rate' => 0.20,
158
        ],
159
160
        // Non-EU with their own VAT requirements
161
        'NO' => [ // Norway
162
            'rate' => 0.25,
163
        ],
164
    ];
165
166
    /**
167
     * All possible postal code exceptions.
168
     *
169
     * @var array
170
     */
171
    protected $postalCodeExceptions = [
172
        'AT' => [
173
            [
174
                'postalCode' => '/^6691$/',
175
                'code'       => 'AT',
176
                'name'       => 'Jungholz',
177
            ],
178
            [
179
                'postalCode' => '/^699[123]$/',
180
                'city'       => '/\bmittelberg\b/i',
181
                'code'       => 'AT',
182
                'name'       => 'Mittelberg',
183
            ],
184
        ],
185
        'CH' => [
186
            [
187
                'postalCode' => '/^8238$/',
188
                'code'       => 'DE',
189
                'name'       => 'Büsingen am Hochrhein',
190
            ],
191
            [
192
                'postalCode' => '/^6911$/',
193
                'code'       => 'IT',
194
                'name'       => "Campione d'Italia",
195
            ],
196
            // The Italian city of Domodossola has a Swiss post office also
197
            [
198
                'postalCode' => '/^3907$/',
199
                'code'       => 'IT',
200
            ],
201
        ],
202
        'DE' => [
203
            [
204
                'postalCode' => '/^87491$/',
205
                'code'       => 'AT',
206
                'name'       => 'Jungholz',
207
            ],
208
            [
209
                'postalCode' => '/^8756[789]$/',
210
                'city'       => '/\bmittelberg\b/i',
211
                'code'       => 'AT',
212
                'name'       => 'Mittelberg',
213
            ],
214
            [
215
                'postalCode' => '/^78266$/',
216
                'code'       => 'DE',
217
                'name'       => 'Büsingen am Hochrhein',
218
            ],
219
            [
220
                'postalCode' => '/^27498$/',
221
                'code'       => 'DE',
222
                'name'       => 'Heligoland',
223
            ],
224
        ],
225
        'ES' => [
226
            [
227
                'postalCode' => '/^(5100[1-5]|5107[0-1]|51081)$/',
228
                'code'       => 'ES',
229
                'name'       => 'Ceuta',
230
            ],
231
            [
232
                'postalCode' => '/^(5200[0-6]|5207[0-1]|52081)$/',
233
                'code'       => 'ES',
234
                'name'       => 'Melilla',
235
            ],
236
            [
237
                'postalCode' => '/^(35\d{3}|38\d{3})$/',
238
                'code'       => 'ES',
239
                'name'       => 'Canary Islands',
240
            ],
241
        ],
242
        'GB' => [
243
            // Akrotiri
244
            [
245
                'postalCode' => '/^BFPO57|BF12AT$/',
246
                'code'       => 'CY',
247
            ],
248
            // Dhekelia
249
            [
250
                'postalCode' => '/^BFPO58|BF12AU$/',
251
                'code'       => 'CY',
252
            ],
253
        ],
254
        'GR' => [
255
            [
256
                'postalCode' => '/^63086$/',
257
                'code'       => 'GR',
258
                'name'       => 'Mount Athos',
259
            ],
260
        ],
261
        'IT' => [
262
            [
263
                'postalCode' => '/^22060$/',
264
                'city'       => '/\bcampione\b/i',
265
                'code'       => 'IT',
266
                'name'       => "Campione d'Italia",
267
            ],
268
            [
269
                'postalCode' => '/^23030$/',
270
                'city'       => '/\blivigno\b/i',
271
                'code'       => 'IT',
272
                'name'       => 'Livigno',
273
            ],
274
        ],
275
        'PT' => [
276
            [
277
                'postalCode' => '/^9[0-4]\d[2,]$/',
278
                'code'       => 'PT',
279
                'name'       => 'Madeira',
280
            ],
281
            [
282
                'postalCode' => '/^9[5-9]\d[2,]$/',
283
                'code'       => 'PT',
284
                'name'       => 'Azores',
285
            ],
286
        ],
287
    ];
288
289
    /**
290
     * @var float
291
     */
292
    protected $netPrice = 0.0;
293
294
    /**
295
     * @var string
296
     */
297
    protected $countryCode;
298
299
    /**
300
     * @var string
301
     */
302
    protected $postalCode;
303
304
    /**
305
     * @var Repository
306
     */
307
    protected $config;
308
309
    /**
310
     * @var float
311
     */
312
    protected $taxValue = 0;
313
314
    /**
315
     * @var float
316
     */
317
    protected $taxRate = 0;
318
319
    /**
320
     * The calculate net + tax value.
321
     *
322
     * @var float
323
     */
324
    protected $value = 0;
325
326
    /**
327
     * @var bool
328
     */
329
    protected $company = false;
330
331
    /**
332
     * @var string
333
     */
334
    protected $businessCountryCode;
335
336
    /**
337
     * @param \Illuminate\Contracts\Config\Repository
338
     */
339
    public function __construct($config = null)
340
    {
341
        $this->config = $config;
342
343
        $businessCountryKey = 'vat_calculator.business_country_code';
344
        if (isset($this->config) && $this->config->has($businessCountryKey)) {
345
            $this->setBusinessCountryCode($this->config->get($businessCountryKey, ''));
346
        }
347
    }
348
349
    /**
350
     * Finds the client IP address.
351
     *
352
     * @return mixed
353
     */
354
    private function getClientIP()
0 ignored issues
show
Coding Style introduced by
getClientIP uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
355
    {
356
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
357
            $clientIpAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
358
        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR']) {
359
            $clientIpAddress = $_SERVER['REMOTE_ADDR'];
360
        } else {
361
            $clientIpAddress = '';
362
        }
363
364
        return $clientIpAddress;
365
    }
366
367
    /**
368
     * Returns the ISO 3166-1 alpha-2 two letter
369
     * country code for the client IP. If the
370
     * IP can't be resolved it returns false.
371
     *
372
     * @return bool|string
373
     */
374
    public function getIPBasedCountry()
375
    {
376
        $ip = $this->getClientIP();
377
        $url = self::GEOCODE_SERVICE_URL.$ip;
378
        $result = file_get_contents($url);
379
        switch ($result[0]) {
380
            case '1':
381
                $data = explode(';', $result);
382
383
                return $data[1];
384
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
385
            default:
386
                return false;
387
        }
388
    }
389
390
    /**
391
     * Determines if you need to collect VAT for the given country code.
392
     *
393
     * @param $countryCode
394
     *
395
     * @return bool
396
     */
397
    public function shouldCollectVAT($countryCode)
398
    {
399
        $taxKey = 'vat_calculator.rules.'.strtoupper($countryCode);
400
401
        return isset($this->taxRules[strtoupper($countryCode)]) || (isset($this->config) && $this->config->has($taxKey));
402
    }
403
404
    /**
405
     * Calculate the VAT based on the net price, country code and indication if the
406
     * customer is a company or not.
407
     *
408
     * @param int|float   $netPrice    The net price to use for the calculation
409
     * @param null|string $countryCode The country code to use for the rate lookup
410
     * @param null|string $postalCode  The postal code to use for the rate exception lookup
411
     * @param null|bool   $company
412
     *
413
     * @return float
414
     */
415
    public function calculate($netPrice, $countryCode = null, $postalCode = null, $company = null)
416
    {
417
        if ($countryCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $countryCode of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
418
            $this->setCountryCode($countryCode);
419
        }
420
        if ($postalCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $postalCode of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
421
            $this->setPostalCode($postalCode);
422
        }
423
        if (!is_null($company) && $company !== $this->isCompany()) {
424
            $this->setCompany($company);
425
        }
426
        $this->netPrice = floatval($netPrice);
427
        $this->taxRate = $this->getTaxRateForLocation($this->getCountryCode(), $this->getPostalCode(), $this->isCompany());
428
        $this->taxValue = $this->taxRate * $this->netPrice;
429
        $this->value = $this->netPrice + $this->taxValue;
430
431
        return $this->value;
432
    }
433
434
    /**
435
     * Calculate the net price on the gross price, country code and indication if the
436
     * customer is a company or not.
437
     *
438
     * @param int|float   $gross       The gross price to use for the calculation
439
     * @param null|string $countryCode The country code to use for the rate lookup
440
     * @param null|string $postalCode  The postal code to use for the rate exception lookup
441
     * @param null|bool   $company
442
     *
443
     * @return float
444
     */
445
    public function calculateNet($gross, $countryCode = null, $postalCode = null, $company = null)
446
    {
447
        if ($countryCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $countryCode of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
448
            $this->setCountryCode($countryCode);
449
        }
450
        if ($postalCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $postalCode of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
451
            $this->setPostalCode($postalCode);
452
        }
453
        if (!is_null($company) && $company !== $this->isCompany()) {
454
            $this->setCompany($company);
455
        }
456
457
        $this->value = floatval($gross);
458
        $this->taxRate = $this->getTaxRateForLocation($this->getCountryCode(), $this->getPostalCode(), $this->isCompany());
459
        $this->taxValue = $this->taxRate > 0 ? $this->value / (1 + $this->taxRate) * $this->taxRate : 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->taxRate > 0 ? $th...e) * $this->taxRate : 0 can also be of type integer. However, the property $taxValue is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
460
        $this->netPrice = $this->value - $this->taxValue;
461
462
        return $this->netPrice;
463
    }
464
465
    /**
466
     * @return float
467
     */
468
    public function getNetPrice()
469
    {
470
        return $this->netPrice;
471
    }
472
473
    /**
474
     * @return string
475
     */
476
    public function getCountryCode()
477
    {
478
        return strtoupper($this->countryCode);
479
    }
480
481
    /**
482
     * @param mixed $countryCode
483
     */
484
    public function setCountryCode($countryCode)
485
    {
486
        $this->countryCode = $countryCode;
487
    }
488
489
    /**
490
     * @return string
491
     */
492
    public function getPostalCode()
493
    {
494
        return $this->postalCode;
495
    }
496
497
    /**
498
     * @param mixed $postalCode
499
     */
500
    public function setPostalCode($postalCode)
501
    {
502
        $this->postalCode = $postalCode;
503
    }
504
505
    /**
506
     * @return float
507
     */
508
    public function getTaxRate()
509
    {
510
        return $this->taxRate;
511
    }
512
513
    /**
514
     * @return bool
515
     */
516
    public function isCompany()
517
    {
518
        return $this->company;
519
    }
520
521
    /**
522
     * @param bool $company
523
     */
524
    public function setCompany($company)
525
    {
526
        $this->company = $company;
527
    }
528
529
    /**
530
     * @param string $businessCountryCode
531
     */
532
    public function setBusinessCountryCode($businessCountryCode)
533
    {
534
        $this->businessCountryCode = $businessCountryCode;
535
    }
536
537
    /**
538
     * Returns the tax rate for the given country code.
539
     * This method is used to allow backwards compatibility.
540
     *
541
     * @param $countryCode
542
     * @param bool $company
543
     *
544
     * @return float
545
     */
546
    public function getTaxRateForCountry($countryCode, $company = false)
547
    {
548
        return $this->getTaxRateForLocation($countryCode, null, $company);
549
    }
550
551
    /**
552
     * Returns the tax rate for the given country code.
553
     * If a postal code is provided, it will try to lookup the different
554
     * postal code exceptions that are possible.
555
     *
556
     * @param string      $countryCode
557
     * @param string|null $postalCode
558
     * @param bool|false  $company
559
     *
560
     * @return float
561
     */
562
    public function getTaxRateForLocation($countryCode, $postalCode = null, $company = false)
563
    {
564
        if ($company && strtoupper($countryCode) !== strtoupper($this->businessCountryCode)) {
565
            return 0;
566
        }
567
        $taxKey = 'vat_calculator.rules.'.strtoupper($countryCode);
568
        if (isset($this->config) && $this->config->has($taxKey)) {
569
            return $this->config->get($taxKey, 0);
570
        }
571
572
        if (isset($this->postalCodeExceptions[$countryCode]) && $postalCode !== null) {
573
            foreach ($this->postalCodeExceptions[$countryCode] as $postalCodeException) {
574
                if (!preg_match($postalCodeException['postalCode'], $postalCode)) {
575
                    continue;
576
                }
577
                if (isset($postalCodeException['name'])) {
578
                    return $this->taxRules[$postalCodeException['code']]['exceptions'][$postalCodeException['name']];
579
                }
580
581
                return $this->taxRules[$postalCodeException['code']]['rate'];
582
            }
583
        }
584
585
        return isset($this->taxRules[strtoupper($countryCode)]['rate']) ? $this->taxRules[strtoupper($countryCode)]['rate'] : 0;
586
    }
587
588
    /**
589
     * @return float
590
     */
591
    public function getTaxValue()
592
    {
593
        return $this->taxValue;
594
    }
595
596
    /**
597
     * @param $vatNumber
598
     *
599
     * @throws VATCheckUnavailableException
600
     *
601
     * @return bool
602
     */
603
    public function isValidVATNumber($vatNumber)
604
    {
605
        $details = self::getVATDetails($vatNumber);
606
        
607
        if ($details) {
608
            return $details->valid;
609
        } else {
610
            return false;
611
        }
612
    }
613
    
614
    /**
615
     * @param $vatNumber
616
     *
617
     * @throws VATCheckUnavailableException
618
     *
619
     * @return object|false
620
     */
621
    public function getVATDetails($vatNumber)
622
    {
623
        $vatNumber = str_replace([' ', '-', '.', ','], '', trim($vatNumber));
624
        $countryCode = substr($vatNumber, 0, 2);
625
        $vatNumber = substr($vatNumber, 2);
626
        $this->initSoapClient();
627
        $client = $this->soapClient;
628
        if ($client) {
629
            try {
630
                $result = $client->checkVat([
631
                    'countryCode' => $countryCode,
632
                    'vatNumber' => $vatNumber,
633
                ]);
634
                return $result;
635
            } catch (SoapFault $e) {
636
                return false;
637
            }
638
        }
639
        throw new VATCheckUnavailableException('The VAT check service is currently unavailable. Please try again later.');
640
    }
641
642
    /**
643
     * @return void
644
     */
645
    public function initSoapClient()
646
    {
647
        if (is_object($this->soapClient) || $this->soapClient === false) {
648
            return;
649
        }
650
        try {
651
            $this->soapClient = new SoapClient(self::VAT_SERVICE_URL);
652
        } catch (SoapFault $e) {
653
            $this->soapClient = false;
654
        }
655
    }
656
657
    /**
658
     * @param SoapClient $soapClient
659
     */
660
    public function setSoapClient($soapClient)
661
    {
662
        $this->soapClient = $soapClient;
663
    }
664
}
665