Passed
Push — main ( 143cac...a4ae2c )
by Pedro
02:02
created

Cliente::consultarPrazo()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 8
c 3
b 0
f 0
dl 0
loc 15
rs 10
cc 3
nc 3
nop 4
1
<?php
2
3
namespace PedroQuezado\Code\Correios;
4
5
/**
6
 * Classe Cliente para integração com os serviços dos Correios.
7
 */
8
class Cliente
9
{
10
    /**
11
     * @var string $usuario Usuário de autenticação.
12
     */
13
    private $usuario;
14
15
    /**
16
     * @var string $senha Senha de autenticação.
17
     */
18
    private $senha;
19
20
    /**
21
     * @var string $numeroCartaoPostagem Número do cartão de postagem.
22
     */
23
    private $numeroCartaoPostagem;
24
25
    /**
26
     * @var string $token Token de autenticação.
27
     */
28
    private $token;
29
30
    /**
31
     * @var int $tokenExpiration Timestamp de expiração do token.
32
     */
33
    private $tokenExpiration;
34
35
    /**
36
     * @var string $baseUrl URL base da API dos Correios.
37
     */
38
    private $baseUrl;
39
40
    /**
41
     * @var array $produtos Lista de produtos a serem consultados.
42
     */
43
    private $produtos = [];
44
45
    /**
46
     * @var array $respostaPreco Resposta da consulta de preços.
47
     */
48
    private $respostaPreco;
49
50
    /**
51
     * @var array $respostaPrazo Resposta da consulta de prazos.
52
     */
53
    private $respostaPrazo;
54
55
    /**
56
     * Construtor da classe Cliente.
57
     *
58
     * @param string $usuario Usuário de autenticação.
59
     * @param string $senha Senha de autenticação.
60
     * @param string $numeroCartaoPostagem Número do cartão de postagem.
61
     * @param bool $producao Indica se é ambiente de produção (true) ou homologação (false).
62
     */
63
    public function __construct($usuario, $senha, $numeroCartaoPostagem, $producao = true)
64
    {
65
        $this->usuario = $usuario;
66
        $this->senha = $senha;
67
        $this->numeroCartaoPostagem = $numeroCartaoPostagem;
68
        $this->baseUrl = $producao ? "https://api.correios.com.br" : "https://apihom.correios.com.br";
69
        $this->token = $this->obterToken();
70
    }
71
72
    /**
73
     * Obtém o token de autenticação.
74
     *
75
     * @return string Token de autenticação.
76
     * @throws ClienteException Em caso de erro na obtenção do token.
77
     */
78
    private function obterToken()
79
    {
80
        $credenciais = base64_encode("{$this->usuario}:{$this->senha}");
81
        $ch = curl_init();
82
        curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/token/v1/autentica/cartaopostagem");
83
        curl_setopt($ch, CURLOPT_POST, 1);
84
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["numero" => $this->numeroCartaoPostagem]));
85
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
86
            "Content-Type: application/json",
87
            "Authorization: Basic $credenciais"
88
        ]);
89
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
90
        $response = curl_exec($ch);
91
92
        if (curl_errno($ch)) {
93
            throw new ClienteException('Erro ao obter token: ' . curl_error($ch));
94
        }
95
96
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
97
        curl_close($ch);
98
99
        if ($httpCode != 201) {
100
            throw new ClienteException('Erro ao obter token', $httpCode, $response);
101
        }
102
103
        if (is_string($response)) {
104
            $responseDecoded = json_decode($response, true);
105
            $this->tokenExpiration = time() + 3600; // Supondo que o token expira em 1 hora
106
        } else {
107
            throw new ClienteException('Erro ao obter token: resposta inválida recebida.');
108
        }
109
110
        return $responseDecoded['token'];
111
    }
112
113
    /**
114
     * Verifica a validade do token e renova se necessário.
115
     */
116
    private function verificarToken()
117
    {
118
        if (time() >= $this->tokenExpiration) {
119
            $this->token = $this->obterToken();
120
        }
121
    }
122
123
    /**
124
     * Insere um produto na lista de produtos a serem consultados.
125
     *
126
     * @param string $coProduto Código do produto.
127
     * @param array $arrProduto Dados do produto.
128
     */
129
    public function inserirProduto($coProduto, array $arrProduto) 
130
    {
131
        $arrProduto['coProduto'] = $coProduto;
132
        $this->produtos[] = $arrProduto;
133
    }
134
135
    /**
136
     * Consulta os preços dos produtos inseridos.
137
     *
138
     * @return array Resposta da consulta de preços.
139
     * @throws ClienteException Em caso de erro na consulta de preços.
140
     */
141
    public function consultarPreco()
142
    {
143
        if (empty($this->produtos)) {
144
            throw new ClienteException('Nenhum produto inserido para consulta de preço.');
145
        }
146
147
        $this->verificarToken();
148
149
        $chunks = array_chunk($this->produtos, 5); // Divide os produtos em lotes de no máximo 5
150
151
        $responses = [];
152
153
        foreach ($chunks as $chunk) {
154
            $ch = curl_init();
155
            curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/preco/v1/nacional");
156
            curl_setopt($ch, CURLOPT_POST, 1);
157
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
158
                "idLote" => "1",
159
                "parametrosProduto" => $chunk
160
            ]));
161
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
162
                "Content-Type: application/json",
163
                "Authorization: Bearer {$this->token}"
164
            ]);
165
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
166
            $response = curl_exec($ch);
167
168
            if (curl_errno($ch)) {
169
                throw new ClienteException('Erro ao consultar preço: ' . curl_error($ch));
170
            }
171
172
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
173
            curl_close($ch);
174
175
            if ($httpCode != 200) {
176
                throw new ClienteException('Erro ao consultar preço', $httpCode, $response);
177
            }
178
179
            if (is_string($response)) {
180
                $responses[] = json_decode($response, true);
181
            } else {
182
                throw new ClienteException('Erro ao consultar preço: resposta inválida recebida.');
183
            }
184
        }
185
186
        $this->respostaPreco = array_merge(...$responses);
187
        return $this->respostaPreco;
188
    }
189
190
    /**
191
     * Consulta o preço total de um produto específico.
192
     *
193
     * @param string $coProduto Código do produto.
194
     * @return float Preço total do produto.
195
     * @throws ClienteException Em caso de erro na consulta de preços ou se preços não foram consultados ainda.
196
     */
197
    public function consultarPrecoTotal($coProduto)
198
    {
199
        if (empty($this->respostaPreco)) {
200
            throw new ClienteException('Preço não foi consultado ainda.');
201
        }
202
203
        $total = 0;
204
        foreach ($this->respostaPreco as $produto) {
205
            if ($produto['coProduto'] === $coProduto) {
206
                $total += (float)str_replace(',', '.', $produto['pcFinal']);
207
            }
208
        }
209
210
        return $total;
211
    }
212
213
    /**
214
     * Consulta os prazos de entrega dos produtos inseridos.
215
     *
216
     * @param string $dataPostagem Data de postagem no formato YYYY-MM-DD.
217
     * @param string $cepOrigem CEP de origem.
218
     * @param string $cepDestino CEP de destino.
219
     * @param string|null $dtEvento Data do evento no formato DD-MM-YYYY (opcional).
220
     * @return array Resposta da consulta de prazos.
221
     * @throws ClienteException Em caso de erro na consulta de prazos.
222
     */
223
    public function consultarPrazo($dataPostagem, $cepOrigem, $cepDestino, $dtEvento = null)
224
    {
225
        if (empty($this->produtos)) {
226
            throw new ClienteException('Nenhum produto inserido para consulta de prazo.');
227
        }
228
229
        $this->verificarToken(); // Verifica se o token de autenticação é válido e, se necessário, renova o token.
230
        $dtEvento = $dtEvento ? $dtEvento : date("d-m-Y", strtotime($dataPostagem)); // Define a data do evento. Se $dtEvento não for fornecido, usa a data de postagem convertida para o formato DD-MM-YYYY.
231
232
        $chunks = $this->criarChunksDeProdutos($dataPostagem, $cepOrigem, $cepDestino, $dtEvento);
233
234
        $responses = $this->consultarPrazoEmChunks($chunks);
235
236
        $this->respostaPrazo = array_merge(...$responses);
237
        return $this->respostaPrazo;
238
    }
239
240
    /**
241
     * Cria lotes de produtos divididos em grupos de no máximo 5 itens.
242
     * 
243
     * @param string $dataPostagem A data de postagem no formato YYYY-MM-DD.
244
     * @param string $cepOrigem O CEP de origem do envio.
245
     * @param string $cepDestino O CEP de destino do envio.
246
     * @param string $dtEvento A data do evento no formato DD-MM-YYYY.
247
     * @return array Os produtos divididos em lotes de no máximo 5 itens.
248
     */
249
    private function criarChunksDeProdutos($dataPostagem, $cepOrigem, $cepDestino, $dtEvento)
250
    {
251
        return array_chunk(array_map(function ($produto) use ($dataPostagem, $cepOrigem, $cepDestino, $dtEvento) {
252
            return [
253
                "coProduto" => $produto['coProduto'],
254
                "nuRequisicao" => $produto['nuRequisicao'],
255
                "dtEvento" => $dtEvento,
256
                "cepOrigem" => $cepOrigem,
257
                "cepDestino" => $cepDestino,
258
                "dataPostagem" => $dataPostagem
259
            ];
260
        }, $this->produtos), 5);
261
    }
262
263
    /**
264
     * Consulta o prazo de entrega dos produtos em lotes e retorna as respostas.
265
     * 
266
     * @param array $chunks Os lotes de produtos a serem consultados.
267
     * @return array As respostas da consulta de prazo de entrega.
268
     * @throws ClienteException Se ocorrer um erro durante a consulta.
269
     */
270
    private function consultarPrazoEmChunks($chunks)
271
    {
272
        $responses = [];
273
274
        foreach ($chunks as $chunk) {
275
            $ch = curl_init();
276
            curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/prazo/v1/nacional");
277
            curl_setopt($ch, CURLOPT_POST, 1);
278
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
279
                "idLote" => "1",
280
                "parametrosPrazo" => $chunk
281
            ]));
282
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
283
                "Content-Type: application/json",
284
                "Authorization: Bearer {$this->token}"
285
            ]);
286
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
287
            $response = curl_exec($ch);
288
289
            if (curl_errno($ch)) {
290
                throw new ClienteException('Erro ao consultar prazo: ' . curl_error($ch));
291
            }
292
293
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
294
            curl_close($ch);
295
296
            if ($httpCode != 200) {
297
                throw new ClienteException('Erro ao consultar prazo', $httpCode, $response);
298
            }
299
300
            if (is_string($response)) {
301
                $responses[] = json_decode($response, true);
302
            } else {
303
                throw new ClienteException('Erro ao consultar prazo: resposta inválida recebida.');
304
            }
305
        }
306
307
        return $responses;
308
    }
309
310
    /**
311
     * Consulta o prazo total de entrega de um produto específico.
312
     *
313
     * @param string $coProduto Código do produto.
314
     * @return array Prazo total de entrega do produto.
315
     * @throws ClienteException Em caso de erro na consulta de prazos ou se prazos não foram consultados ainda.
316
     */
317
    public function consultarPrazoTotal($coProduto)
318
    {
319
        if (empty($this->respostaPrazo)) {
320
            throw new ClienteException('Prazo não foi consultado ainda.');
321
        }
322
323
        foreach ($this->respostaPrazo as $produto) {
324
            if ($produto['coProduto'] === $coProduto) {
325
                return $produto;
326
            }
327
        }
328
329
        throw new ClienteException('Produto não encontrado na resposta do prazo.');
330
    }
331
}
332