Skip to content

Commit 031cd90

Browse files
committed
Try to fix vies valdiation api
1 parent d22b759 commit 031cd90

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

src/Resources/config/services.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,8 @@
2828
<tag name="sylius.taxation.calculation_strategy" type="italian_tax_calculation_strategy" label="Italian tax calculation" />
2929
</service>
3030

31+
<service id="app.validator.constraint.vat_number_validator" class="Webgriffe\SyliusItalianInvoiceableOrderPlugin\Validator\Constraints\VatNumberValidator" decorates="sandwich_vies.validator.constraint.vat_number_validator">
32+
<argument type="service" id="sandwich_vies.vies_api"/>
33+
</service>
3134
</services>
3235
</container>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Webgriffe\SyliusItalianInvoiceableOrderPlugin\Validator\Constraints;
6+
7+
use DragonBe\Vies\Vies;
8+
use DragonBe\Vies\ViesException;
9+
use DragonBe\Vies\ViesServiceException;
10+
use Sandwich\ViesBundle\Validator\Constraint\VatNumber;
11+
use Symfony\Component\Validator\Constraint;
12+
use Symfony\Component\Validator\ConstraintValidator;
13+
use Webgriffe\SyliusItalianInvoiceableOrderPlugin\Vies\HeartBeat;
14+
15+
/**
16+
* @psalm-suppress PropertyNotSetInConstructor
17+
*/
18+
final class VatNumberValidator extends ConstraintValidator
19+
{
20+
public function __construct(private Vies $viesApi)
21+
{
22+
}
23+
24+
public function validate(mixed $value, Constraint $constraint): void
25+
{
26+
if (!is_string($value) || '' === $value) {
27+
return;
28+
}
29+
30+
if (!$constraint instanceof VatNumber) {
31+
return;
32+
}
33+
34+
$this->viesApi->setHeartBeat(new HeartBeat());
35+
36+
if (!$this->viesApi->getHeartBeat()->isAlive()) {
37+
//VIES service is not available
38+
return;
39+
}
40+
41+
$format = $constraint->getFormat();
42+
$isValid = false;
43+
44+
try {
45+
$isValid = $this->viesApi->validateVat($format, str_replace($format, '', $value))->isValid();
46+
} catch (ViesServiceException $exception) {
47+
//There is probably a temporary problem with back-end VIES service
48+
return;
49+
} catch (ViesException $exception) {
50+
}
51+
52+
if ($isValid) {
53+
return;
54+
}
55+
56+
$this->context->addViolation($constraint->message, ['%format%' => $format]);
57+
}
58+
}

src/Vies/HeartBeat.php

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Webgriffe\SyliusItalianInvoiceableOrderPlugin\Vies;
6+
7+
use DragonBe\Vies\HeartBeat as BaseHeartBeat;
8+
use DragonBe\Vies\Vies;
9+
use InvalidArgumentException;
10+
use RuntimeException;
11+
12+
/**
13+
* @psalm-suppress PropertyNotSetInConstructor
14+
*/
15+
final class HeartBeat extends BaseHeartBeat
16+
{
17+
private const DEFAULT_TIMEOUT = 10;
18+
19+
public function isAlive(): bool
20+
{
21+
if (false === self::$testingEnabled) {
22+
return $this->reachOut();
23+
}
24+
25+
return self::$testingServiceIsUp;
26+
}
27+
28+
/**
29+
* A private routine to send a request over a socket to
30+
* test if the remote service is responding with a status
31+
* code of 200 OK. Now supports also proxy connections.
32+
*/
33+
private function reachOut(): bool
34+
{
35+
try {
36+
$data = $this->getSecuredResponse();
37+
} catch (RuntimeException $runtimeException) {
38+
return false;
39+
}
40+
41+
return
42+
(0 === strcmp('HTTP/1.1 200 OK', (string) $data[0])) ||
43+
(0 === strcmp('HTTP/1.1 307 Temporary Redirect', (string) $data[0]))
44+
;
45+
}
46+
47+
/**
48+
* This method will make a simple request inside a stream
49+
* resource to retrieve its contents. Useful inside secured
50+
* streams.
51+
*
52+
* @param resource|mixed $handle
53+
*/
54+
private function readContents($handle): array
55+
{
56+
if (!is_resource($handle)) {
57+
throw new InvalidArgumentException('Expecting a resource to be provided');
58+
}
59+
$response = '';
60+
$uri = sprintf('%s://%s/taxation_customs/vies', Vies::VIES_PROTO, $this->host);
61+
$stream = [
62+
'GET ' . $uri . ' HTTP/1.0',
63+
'Host: ' . $this->host,
64+
'Connection: close',
65+
];
66+
fwrite($handle, implode("\r\n", $stream) . "\r\n\r\n");
67+
while (!feof($handle)) {
68+
$response .= fgets($handle, 1024);
69+
}
70+
fclose($handle);
71+
$response = str_replace("\r\n", \PHP_EOL, $response);
72+
73+
return explode(\PHP_EOL, $response);
74+
}
75+
76+
/**
77+
* Will make a secured request over SSL/TLS where this
78+
* method will first create a secured stream before
79+
* making the request.
80+
*
81+
* @throws RuntimeException
82+
*
83+
* @see https://bytephunk.wordpress.com/2017/11/27/ssl-tls-stream-sockets-in-php-7/
84+
*/
85+
private function getSecuredResponse(): array
86+
{
87+
$streamOptions = [
88+
'ssl' => [
89+
'verify_peer' => true,
90+
'verify_peer_name' => true,
91+
'allow_self_signed' => false,
92+
],
93+
];
94+
$streamContext = stream_context_create($streamOptions);
95+
$socketAddress = sprintf(
96+
'tls://%s:%d',
97+
$this->host,
98+
$this->port,
99+
);
100+
$error = null;
101+
$errno = null;
102+
$stream = stream_socket_client(
103+
$socketAddress,
104+
$errno,
105+
$error,
106+
self::DEFAULT_TIMEOUT,
107+
\STREAM_CLIENT_CONNECT,
108+
$streamContext,
109+
);
110+
111+
if ($stream === false) {
112+
throw new RuntimeException('Can not create socket stream: ' . $error);
113+
}
114+
115+
return $this->readContents($stream);
116+
}
117+
}

0 commit comments

Comments
 (0)