Completed
Pull Request — master (#72)
by
unknown
07:48
created

VatCalculator::forwardSoapFaults()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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
            'exceptions' => [
84
                // Overseas France
85
                'Reunion'    => 0.085,
86
                'Martinique' => 0.085,
87
                'Guadeloupe' => 0.085,
88
                'Guyane'     => 0,
89
                'Mayotte'    => 0,
90
            ],
91
        ],
92
        'GB' => [ // United Kingdom
93
            'rate'       => 0.20,
94
            'exceptions' => [
95
                // UK RAF Bases in Cyprus are taxed at Cyprus rate
96
                'Akrotiri' => 0.19,
97
                'Dhekelia' => 0.19,
98
            ],
99
        ],
100
        'GR' => [ // Greece
101
            'rate'       => 0.24,
102
            'exceptions' => [
103
                'Mount Athos' => 0,
104
            ],
105
        ],
106
        'HR' => [ // Croatia
107
            'rate' => 0.25,
108
        ],
109
        'HU' => [ // Hungary
110
            'rate' => 0.27,
111
        ],
112
        'IE' => [ // Ireland
113
            'rate' => 0.23,
114
        ],
115
        'IT' => [ // Italy
116
            'rate'       => 0.22,
117
            'exceptions' => [
118
                'Campione d\'Italia' => 0,
119
                'Livigno'            => 0,
120
            ],
121
        ],
122
        'LT' => [ // Lithuania
123
            'rate' => 0.21,
124
        ],
125
        'LU' => [ // Luxembourg
126
            'rate' => 0.17,
127
        ],
128
        'LV' => [ // Latvia
129
            'rate' => 0.21,
130
        ],
131
        'MT' => [ // Malta
132
            'rate' => 0.18,
133
        ],
134
        'NL' => [ // Netherlands
135
            'rate' => 0.21,
136
            'rates' => [
137
                'high' => 0.21,
138
                'low' => 0.09,
139
            ],
140
        ],
141
        'PL' => [ // Poland
142
            'rate' => 0.23,
143
        ],
144
        'PT' => [ // Portugal
145
            'rate'       => 0.23,
146
            'exceptions' => [
147
                'Azores'  => 0.18,
148
                'Madeira' => 0.22,
149
            ],
150
        ],
151
        'RO' => [ // Romania
152
            'rate' => 0.19,
153
        ],
154
        'SE' => [ // Sweden
155
            'rate' => 0.25,
156
        ],
157
        'SI' => [ // Slovenia
158
            'rate' => 0.22,
159
        ],
160
        'SK' => [ // Slovakia
161
            'rate' => 0.20,
162
        ],
163
164
        // Countries associated with EU countries that have a special VAT rate
165
        'MC' => [ // Monaco France
166
            'rate' => 0.20,
167
        ],
168
        'IM' => [ // Isle of Man - United Kingdom
169
            'rate' => 0.20,
170
        ],
171
172
        // Non-EU with their own VAT requirements
173
        'CH' => [ // Switzerland
174
            'rate' => 0.077,
175
            'rates' => [
176
                'high' => 0.077,
177
                'low' => 0.025,
178
            ],
179
        ],
180
        'TR' => [ // Turkey
181
            'rate' => 0.18,
182
        ],
183
        'NO' => [ // Norway
184
            'rate' => 0.25,
185
        ],
186
    ];
187
188
    /**
189
     * All possible postal code exceptions.
190
     *
191
     * @var array
192
     */
193
    protected $postalCodeExceptions = [
194
        'AT' => [
195
            [
196
                'postalCode' => '/^6691$/',
197
                'code'       => 'AT',
198
                'name'       => 'Jungholz',
199
            ],
200
            [
201
                'postalCode' => '/^699[123]$/',
202
                'city'       => '/\bmittelberg\b/i',
203
                'code'       => 'AT',
204
                'name'       => 'Mittelberg',
205
            ],
206
        ],
207
        'CH' => [
208
            [
209
                'postalCode' => '/^8238$/',
210
                'code'       => 'DE',
211
                'name'       => 'Büsingen am Hochrhein',
212
            ],
213
            [
214
                'postalCode' => '/^6911$/',
215
                'code'       => 'IT',
216
                'name'       => "Campione d'Italia",
217
            ],
218
            // The Italian city of Domodossola has a Swiss post office also
219
            [
220
                'postalCode' => '/^3907$/',
221
                'code'       => 'IT',
222
            ],
223
        ],
224
        'DE' => [
225
            [
226
                'postalCode' => '/^87491$/',
227
                'code'       => 'AT',
228
                'name'       => 'Jungholz',
229
            ],
230
            [
231
                'postalCode' => '/^8756[789]$/',
232
                'city'       => '/\bmittelberg\b/i',
233
                'code'       => 'AT',
234
                'name'       => 'Mittelberg',
235
            ],
236
            [
237
                'postalCode' => '/^78266$/',
238
                'code'       => 'DE',
239
                'name'       => 'Büsingen am Hochrhein',
240
            ],
241
            [
242
                'postalCode' => '/^27498$/',
243
                'code'       => 'DE',
244
                'name'       => 'Heligoland',
245
            ],
246
        ],
247
        'ES' => [
248
            [
249
                'postalCode' => '/^(5100[1-5]|5107[0-1]|51081)$/',
250
                'code'       => 'ES',
251
                'name'       => 'Ceuta',
252
            ],
253
            [
254
                'postalCode' => '/^(5200[0-6]|5207[0-1]|52081)$/',
255
                'code'       => 'ES',
256
                'name'       => 'Melilla',
257
            ],
258
            [
259
                'postalCode' => '/^(35\d{3}|38\d{3})$/',
260
                'code'       => 'ES',
261
                'name'       => 'Canary Islands',
262
            ],
263
        ],
264
        'FR' => [
265
            [
266
                'postalCode' => '/^971\d{2,}$/',
267
                'code'       => 'ES',
268
                'name'       => 'Guadeloupe',
269
            ],
270
            [
271
                'postalCode' => '/^972\d{2,}$/',
272
                'code'       => 'FR',
273
                'name'       => 'Martinique',
274
            ],
275
            [
276
                'postalCode' => '/^973\d{2,}$/',
277
                'code'       => 'ES',
278
                'name'       => 'Guyane',
279
            ],
280
            [
281
                'postalCode' => '/^974\d{2,}$/',
282
                'code'       => 'FR',
283
                'name'       => 'Reunion',
284
            ],
285
            [
286
                'postalCode' => '/^976\d{2,}$/',
287
                'code'       => 'ES',
288
                'name'       => 'Mayotte',
289
            ],
290
        ],
291
        'GB' => [
292
            // Akrotiri
293
            [
294
                'postalCode' => '/^BFPO57|BF12AT$/',
295
                'code'       => 'CY',
296
            ],
297
            // Dhekelia
298
            [
299
                'postalCode' => '/^BFPO58|BF12AU$/',
300
                'code'       => 'CY',
301
            ],
302
        ],
303
        'GR' => [
304
            [
305
                'postalCode' => '/^63086$/',
306
                'code'       => 'GR',
307
                'name'       => 'Mount Athos',
308
            ],
309
        ],
310
        'IT' => [
311
            [
312
                'postalCode' => '/^22060$/',
313
                'city'       => '/\bcampione\b/i',
314
                'code'       => 'IT',
315
                'name'       => "Campione d'Italia",
316
            ],
317
            [
318
                'postalCode' => '/^23030$/',
319
                'city'       => '/\blivigno\b/i',
320
                'code'       => 'IT',
321
                'name'       => 'Livigno',
322
            ],
323
        ],
324
        'PT' => [
325
            [
326
                'postalCode' => '/^9[0-4]\d{2,}$/',
327
                'code'       => 'PT',
328
                'name'       => 'Madeira',
329
            ],
330
            [
331
                'postalCode' => '/^9[5-9]\d{2,}$/',
332
                'code'       => 'PT',
333
                'name'       => 'Azores',
334
            ],
335
        ],
336
    ];
337
338
    /**
339
     * @var float
340
     */
341
    protected $netPrice = 0.0;
342
343
    /**
344
     * @var string
345
     */
346
    protected $countryCode;
347
348
    /**
349
     * @var string
350
     */
351
    protected $postalCode;
352
353
    /**
354
     * @var Repository
355
     */
356
    protected $config;
357
358
    /**
359
     * @var float
360
     */
361
    protected $taxValue = 0;
362
363
    /**
364
     * @var float
365
     */
366
    protected $taxRate = 0;
367
368
    /**
369
     * The calculate net + tax value.
370
     *
371
     * @var float
372
     */
373
    protected $value = 0;
374
375
    /**
376
     * @var bool
377
     */
378
    protected $company = false;
379
380
    /**
381
     * @var string
382
     */
383
    protected $businessCountryCode;
384
385
    /**
386
     * @var bool
387
     */
388
    protected $forwardSoapFaults = false;
389
390
    /**
391
     * @param \Illuminate\Contracts\Config\Repository
392
     */
393
    public function __construct($config = null)
394
    {
395
        $this->config = $config;
396
397
        $businessCountryKey = 'vat_calculator.business_country_code';
398
        if (isset($this->config) && $this->config->has($businessCountryKey)) {
399
            $this->setBusinessCountryCode($this->config->get($businessCountryKey, ''));
400
        }
401
402
        if (isset($this->config) && $this->config->get('vat_calculator.forward_soap_faults', false)) {
403
            $this->forwardSoapFaults();
404
        }
405
    }
406
407
    /**
408
     * Finds the client IP address.
409
     *
410
     * @return mixed
411
     */
412
    private function getClientIP()
413
    {
414
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR']) {
415
            $clientIpAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
416
        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR']) {
417
            $clientIpAddress = $_SERVER['REMOTE_ADDR'];
418
        } else {
419
            $clientIpAddress = '';
420
        }
421
422
        return $clientIpAddress;
423
    }
424
425
    /**
426
     * Returns the ISO 3166-1 alpha-2 two letter
427
     * country code for the client IP. If the
428
     * IP can't be resolved it returns false.
429
     *
430
     * @return bool|string
431
     */
432
    public function getIPBasedCountry()
433
    {
434
        $ip = $this->getClientIP();
435
        $url = self::GEOCODE_SERVICE_URL.$ip;
436
        $result = file_get_contents($url);
437
        switch ($result[0]) {
438
            case '1':
439
                $data = explode(';', $result);
440
441
                return $data[1];
442
                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...
443
            default:
444
                return false;
445
        }
446
    }
447
448
    /**
449
     * Determines if you need to collect VAT for the given country code.
450
     *
451
     * @param $countryCode
452
     *
453
     * @return bool
454
     */
455
    public function shouldCollectVAT($countryCode)
456
    {
457
        $taxKey = 'vat_calculator.rules.'.strtoupper($countryCode);
458
459
        return isset($this->taxRules[strtoupper($countryCode)]) || (isset($this->config) && $this->config->has($taxKey));
460
    }
461
462
    /**
463
     * Calculate the VAT based on the net price, country code and indication if the
464
     * customer is a company or not.
465
     *
466
     * @param int|float   $netPrice    The net price to use for the calculation
467
     * @param null|string $countryCode The country code to use for the rate lookup
468
     * @param null|string $postalCode  The postal code to use for the rate exception lookup
469
     * @param null|bool   $company
470
     * @param null|string $type        The type can be low or high
471
     *
472
     * @return float
473
     */
474
    public function calculate($netPrice, $countryCode = null, $postalCode = null, $company = null, $type = null)
475
    {
476
        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...
477
            $this->setCountryCode($countryCode);
478
        }
479
        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...
480
            $this->setPostalCode($postalCode);
481
        }
482
        if (!is_null($company) && $company !== $this->isCompany()) {
483
            $this->setCompany($company);
484
        }
485
        $this->netPrice = floatval($netPrice);
486
        $this->taxRate = $this->getTaxRateForLocation($this->getCountryCode(), $this->getPostalCode(), $this->isCompany(), $type);
487
        $this->taxValue = $this->taxRate * $this->netPrice;
488
        $this->value = $this->netPrice + $this->taxValue;
489
490
        return $this->value;
491
    }
492
493
    /**
494
     * Calculate the net price on the gross price, country code and indication if the
495
     * customer is a company or not.
496
     *
497
     * @param int|float   $gross       The gross price to use for the calculation
498
     * @param null|string $countryCode The country code to use for the rate lookup
499
     * @param null|string $postalCode  The postal code to use for the rate exception lookup
500
     * @param null|bool   $company
501
     * @param null|string $type        The type can be low or high
502
     *
503
     * @return float
504
     */
505
    public function calculateNet($gross, $countryCode = null, $postalCode = null, $company = null, $type = null)
506
    {
507
        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...
508
            $this->setCountryCode($countryCode);
509
        }
510
        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...
511
            $this->setPostalCode($postalCode);
512
        }
513
        if (!is_null($company) && $company !== $this->isCompany()) {
514
            $this->setCompany($company);
515
        }
516
517
        $this->value = floatval($gross);
518
        $this->taxRate = $this->getTaxRateForLocation($this->getCountryCode(), $this->getPostalCode(), $this->isCompany(), $type);
519
        $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...
520
        $this->netPrice = $this->value - $this->taxValue;
521
522
        return $this->netPrice;
523
    }
524
525
    /**
526
     * @return float
527
     */
528
    public function getNetPrice()
529
    {
530
        return $this->netPrice;
531
    }
532
533
    /**
534
     * @return string
535
     */
536
    public function getCountryCode()
537
    {
538
        return strtoupper($this->countryCode);
539
    }
540
541
    /**
542
     * @param mixed $countryCode
543
     */
544
    public function setCountryCode($countryCode)
545
    {
546
        $this->countryCode = $countryCode;
547
    }
548
549
    /**
550
     * @return string
551
     */
552
    public function getPostalCode()
553
    {
554
        return $this->postalCode;
555
    }
556
557
    /**
558
     * @param mixed $postalCode
559
     */
560
    public function setPostalCode($postalCode)
561
    {
562
        $this->postalCode = $postalCode;
563
    }
564
565
    /**
566
     * @return float
567
     */
568
    public function getTaxRate()
569
    {
570
        return $this->taxRate;
571
    }
572
573
    /**
574
     * @return bool
575
     */
576
    public function isCompany()
577
    {
578
        return $this->company;
579
    }
580
581
    /**
582
     * @param bool $company
583
     */
584
    public function setCompany($company)
585
    {
586
        $this->company = $company;
587
    }
588
589
    /**
590
     * @param string $businessCountryCode
591
     */
592
    public function setBusinessCountryCode($businessCountryCode)
593
    {
594
        $this->businessCountryCode = $businessCountryCode;
595
    }
596
597
    public function forwardSoapFaults()
598
    {
599
        $this->forwardSoapFaults = true;
600
    }
601
602
    /**
603
     * Returns the tax rate for the given country code.
604
     * This method is used to allow backwards compatibility.
605
     *
606
     * @param $countryCode
607
     * @param bool $company
608
     * @param string $type
609
     *
610
     * @return float
611
     */
612
    public function getTaxRateForCountry($countryCode, $company = false, $type = null)
613
    {
614
        return $this->getTaxRateForLocation($countryCode, null, $company, $type);
615
    }
616
617
    /**
618
     * Returns the tax rate for the given country code.
619
     * If a postal code is provided, it will try to lookup the different
620
     * postal code exceptions that are possible.
621
     *
622
     * @param string      $countryCode
623
     * @param string|null $postalCode
624
     * @param bool|false  $company
625
     * @param string|null $type
626
     *
627
     * @return float
628
     */
629
    public function getTaxRateForLocation($countryCode, $postalCode = null, $company = false, $type = null)
630
    {
631
        if ($company && strtoupper($countryCode) !== strtoupper($this->businessCountryCode)) {
632
            return 0;
633
        }
634
        $taxKey = 'vat_calculator.rules.'.strtoupper($countryCode);
635
        if (isset($this->config) && $this->config->has($taxKey)) {
636
            return $this->config->get($taxKey, 0);
637
        }
638
639
        if (isset($this->postalCodeExceptions[$countryCode]) && $postalCode !== null) {
640
            foreach ($this->postalCodeExceptions[$countryCode] as $postalCodeException) {
641
                if (!preg_match($postalCodeException['postalCode'], $postalCode)) {
642
                    continue;
643
                }
644
                if (isset($postalCodeException['name'])) {
645
                    return $this->taxRules[$postalCodeException['code']]['exceptions'][$postalCodeException['name']];
646
                }
647
648
                return $this->taxRules[$postalCodeException['code']]['rate'];
649
            }
650
        }
651
652
        if ($type !== null) {
653
            return isset($this->taxRules[strtoupper($countryCode)]['rates'][$type]) ? $this->taxRules[strtoupper($countryCode)]['rates'][$type] : 0;
654
        }
655
656
        return isset($this->taxRules[strtoupper($countryCode)]['rate']) ? $this->taxRules[strtoupper($countryCode)]['rate'] : 0;
657
    }
658
659
    /**
660
     * @return float
661
     */
662
    public function getTaxValue()
663
    {
664
        return $this->taxValue;
665
    }
666
667
    /**
668
     * @param $vatNumber
669
     *
670
     * @throws VATCheckUnavailableException
671
     *
672
     * @return bool
673
     */
674
    public function isValidVATNumber($vatNumber)
675
    {
676
        $details = self::getVATDetails($vatNumber);
677
678
        if ($details) {
679
            return $details->valid;
680
        } else {
681
            return false;
682
        }
683
    }
684
685
    /**
686
     * @param $vatNumber
687
     *
688
     * @throws VATCheckUnavailableException
689
     *
690
     * @return object|false
691
     */
692
    public function getVATDetails($vatNumber)
693
    {
694
        $vatNumber = str_replace([' ', '-', '.', ','], '', trim($vatNumber));
695
        $countryCode = substr($vatNumber, 0, 2);
696
        $vatNumber = substr($vatNumber, 2);
697
        $this->initSoapClient();
698
        $client = $this->soapClient;
699
        if ($client) {
700
            try {
701
                $result = $client->checkVat([
702
                    'countryCode' => $countryCode,
703
                    'vatNumber' => $vatNumber,
704
                ]);
705
                return $result;
706
            } catch (SoapFault $e) {
707
                if ($this->forwardSoapFaults) {
708
                    throw new VATCheckUnavailableException($e->getMessage(), $e->getCode(), $e->getPrevious());
709
                }
710
711
                return false;
712
            }
713
        }
714
        throw new VATCheckUnavailableException('The VAT check service is currently unavailable. Please try again later.');
715
    }
716
717
    /**
718
     * @throws VATCheckUnavailableException
719
     *
720
     * @return void
721
     */
722
    public function initSoapClient()
723
    {
724
        if (is_object($this->soapClient) || $this->soapClient === false) {
725
            return;
726
        }
727
        try {
728
            $this->soapClient = new SoapClient(self::VAT_SERVICE_URL);
729
        } catch (SoapFault $e) {
730
            if (isset($this->config) && $this->config->get('vat_calculator.forward_soap_faults')) {
731
                throw new VATCheckUnavailableException($e->getMessage(), $e->getCode(), $e->getPrevious());
732
            }
733
734
            $this->soapClient = false;
735
        }
736
    }
737
738
    /**
739
     * @param SoapClient $soapClient
740
     */
741
    public function setSoapClient($soapClient)
742
    {
743
        $this->soapClient = $soapClient;
744
    }
745
}
746