Passed
Push — master ( 9d79d3...4bb85e )
by Esteban De La Fuente
08:41
created

SiiRequest::getTokenDefaultCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
dl 0
loc 3
c 1
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * LibreDTE: Biblioteca PHP (Núcleo).
7
 * Copyright (C) LibreDTE <https://www.libredte.cl>
8
 *
9
 * Este programa es software libre: usted puede redistribuirlo y/o modificarlo
10
 * bajo los términos de la Licencia Pública General Affero de GNU publicada por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (a su elección) cualquier versión posterior de la misma.
13
 *
14
 * Este programa se distribuye con la esperanza de que sea útil, pero SIN
15
 * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
16
 * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública
17
 * General Affero de GNU para obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de
20
 * GNU junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace libredte\lib\Core\Package\Billing\Component\Integration\Support;
26
27
use Derafu\Lib\Core\Common\Trait\OptionsAwareTrait;
28
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Contract\CertificateInterface;
29
use Derafu\Lib\Core\Support\Store\Contract\DataContainerInterface;
30
use libredte\lib\Core\Package\Billing\Component\Integration\Contract\SiiRequestInterface;
31
use libredte\lib\Core\Package\Billing\Component\Integration\Enum\SiiAmbiente;
32
use LogicException;
33
use RuntimeException;
34
use Symfony\Component\OptionsResolver\Options;
35
36
/**
37
 * Clase para administrar la solicitud al sitio web del SII de Chile.
38
 */
39
class SiiRequest implements SiiRequestInterface
40
{
41
    use OptionsAwareTrait;
42
43
    /**
44
     * Reglas de esquema de las opciones de la solicitud al SII.
45
     *
46
     * @var array
47
     */
48
    protected array $optionsSchema = [
49
        // Por defecto la conexión es a los servidores de producción del SII.
50
        'ambiente' => [
51
            'types' => SiiAmbiente::class,
52
            'default' => SiiAmbiente::PRODUCCION,
53
            'choices' => [
54
                SiiAmbiente::PRODUCCION,
55
                SiiAmbiente::CERTIFICACION,
56
            ],
57
        ],
58
59
        // Especifica cuántos reintentos se realizarán de manera automática al
60
        // hacer una solicitud al SII. El reintento se hará utilizando
61
        // "exponential backoff", por lo que un número muy grande implicará un
62
        // tiempo de ejecución alto.
63
        'reintentos' => [
64
            'types' => 'int',
65
            'default' => 10,
66
            'normalizer' => null, // Se asigna en el constructor como callback.
67
        ],
68
69
        // Especifica si se debe o no realizar la validación del certificado
70
        // SSL del SII. A veces, en pruebas sobre todo, ha resultado útil poder
71
        // desactivar esta validación. Sin embargo, se desaconseja hacerlo por
72
        // motivos de seguridad.
73
        'verificar_ssl' => [
74
            'types' => 'bool',
75
            'default' => true,
76
        ],
77
78
        // Opciones para el token que se utilizará en la solicitud.
79
        'token' => [
80
            'types' => 'array',
81
            'schema' => [
82
                // Esta es la caché por defecto que se utilizará al solicitar
83
                // una caché que implemente PSR-16 para almarcenar el token.
84
                'cache' => [
85
                    'types' => 'string',
86
                    'default' => 'memory',
87
                    'choices' => ['memory', 'filesystem'],
88
                ],
89
90
                // Formato de la clave en caché para guardar el token asociado a
91
                // un certificado. Se utiliza un placeholder que se reemplazará
92
                // con el ID del certificado.
93
                'key' => [
94
                    'types' => 'string',
95
                    'default' => 'libredte_lib_sii_auth_token_%s',
96
                ],
97
98
                // Tiempo en segundos que el token es válido desde que se
99
                // solicitó. Será el tiempo que se mantenga en caché.
100
                'ttl' => [
101
                    'types' => 'int',
102
                    'default' => 60,
103
                ],
104
            ],
105
        ],
106
107
108
    ];
109
110
    /**
111
     * Certificado digital que se utilizará en las consultas al SII.
112
     *
113
     * @var CertificateInterface|null
114
     */
115
    private ?CertificateInterface $certificate;
116
117
    /**
118
     * Constructor de la configuración de conexión.
119
     *
120
     * @param CertificateInterface|null $certificate
121
     * @param array|DataContainerInterface $options Opciones de la solicitud.
122
     */
123 1
    public function __construct(
124
        ?CertificateInterface $certificate = null,
125
        array|DataContainerInterface $options = []
126
    ) {
127 1
        $this->certificate = $certificate;
128
129
        // Resolver opciones de la solicitud.
130 1
        $this->optionsSchema['reintentos']['normalizer'] =
131 1
            fn (Options $options, int $value) => max(0, min(10, $value))
132 1
        ;
133 1
        $this->setOptions($options);
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139 1
    public function getAmbiente(): SiiAmbiente
140
    {
141 1
        return $this->options->get('ambiente');
142
    }
143
144
    /**
145
     * {@inheritDoc}
146
     */
147 1
    public function getReintentos(?int $reintentos = null): int
148
    {
149 1
        $reintentosInOptions = $this->options->get('reintentos');
150
151 1
        return max(0, min(
152 1
            $reintentos ?? $reintentosInOptions,
153 1
            $this->optionsSchema['reintentos']['default']
154 1
        ));
155
    }
156
157
    /**
158
     * {@inheritDoc}
159
     */
160 1
    public function getVerificarSsl(): bool
161
    {
162 1
        return $this->options->get('verificar_ssl');
163
    }
164
165
    /**
166
     * {@inheritDoc}
167
     */
168 1
    public function getTokenDefaultCache(): string
169
    {
170 1
        return $this->options->get('token.cache');
171
    }
172
173
    /**
174
     * {@inheritDoc}
175
     */
176 1
    public function getTokenKey(): string
177
    {
178 1
        $key = $this->options->get('token.key');
179
180 1
        if (!str_contains($key, '%s')) {
181
            throw new RuntimeException(
182
                'La clave del token debe permitir asignar el ID asociado al token.'
183
            );
184
        }
185
186 1
        if ($this->certificate === null) {
187
            throw new LogicException(
188
                'No hay certificado digital asociado a la solicitud.'
189
            );
190
        }
191
192 1
        return sprintf($key, $this->certificate->getId());
193
    }
194
195
    /**
196
     * {@inheritDoc}
197
     */
198
    public function getTokenTtl(): int
199
    {
200
        return $this->options->get('token.ttl');
201
    }
202
203
    /**
204
     * {@inheritDoc}
205
     */
206 1
    public function getCertificate(): ?CertificateInterface
207
    {
208 1
        return $this->certificate;
209
    }
210
}
211