Passed
Branch main (34a3f4)
by Pedro
02:38 queued 41s
created

Cliente::consultarPrecoEmChunks()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 24
c 1
b 0
f 0
dl 0
loc 38
rs 9.2248
cc 5
nc 5
nop 1
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 o preço dos produtos inseridos dividindo-os em lotes.
137
	 * 
138
	 * @return array A resposta da consulta de preço.
139
	 * @throws ClienteException Se ocorrer um erro durante a consulta.
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
		$chunks = $this->criarChunksDeProdutosPreco($this->produtos);
149
		$this->respostaPreco = $this->consultarPrecoEmChunks($chunks);
150
		return $this->respostaPreco;
151
	}
152
153
	/**
154
	 * Cria lotes de produtos divididos em grupos de no máximo 5 itens.
155
	 * 
156
	 * @param array $produtos Os produtos a serem divididos em lotes.
157
	 * @return array Os produtos divididos em lotes de no máximo 5 itens.
158
	 */
159
	private function criarChunksDeProdutosPreco(array $produtos)
160
	{
161
		return array_chunk($produtos, 5);
162
	}
163
164
	/**
165
	 * Consulta o preço dos produtos em lotes e retorna as respostas.
166
	 * 
167
	 * @param array $chunks Os lotes de produtos a serem consultados.
168
	 * @return array As respostas da consulta de preço.
169
	 * @throws ClienteException Se ocorrer um erro durante a consulta.
170
	 */
171
	private function consultarPrecoEmChunks(array $chunks)
172
	{
173
		$responses = [];
174
175
		foreach ($chunks as $chunk) {
176
			$ch = curl_init();
177
			curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/preco/v1/nacional");
178
			curl_setopt($ch, CURLOPT_POST, 1);
179
			curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
180
				"idLote" => "1",
181
				"parametrosProduto" => $chunk
182
			]));
183
			curl_setopt($ch, CURLOPT_HTTPHEADER, [
184
				"Content-Type: application/json",
185
				"Authorization: Bearer {$this->token}"
186
			]);
187
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
188
			$response = curl_exec($ch);
189
190
			if (curl_errno($ch)) {
191
				throw new ClienteException('Erro ao consultar preço: ' . curl_error($ch));
192
			}
193
194
			$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
195
			curl_close($ch);
196
197
			if ($httpCode != 200) {
198
				throw new ClienteException('Erro ao consultar preço', $httpCode, $response);
199
			}
200
201
			if (is_string($response)) {
202
				$responses[] = json_decode($response, true);
203
			} else {
204
				throw new ClienteException('Erro ao consultar preço: resposta inválida recebida.');
205
			}
206
		}
207
208
		return array_merge(...$responses);
209
	}
210
211
	/**
212
	 * Consulta o preço total de um produto específico.
213
	 *
214
	 * @param string $coProduto Código do produto.
215
	 * @return float Preço total do produto.
216
	 * @throws ClienteException Em caso de erro na consulta de preços ou se preços não foram consultados ainda.
217
	 */
218
	public function consultarPrecoTotal($coProduto)
219
	{
220
		if (empty($this->respostaPreco)) {
221
			throw new ClienteException('Preço não foi consultado ainda.');
222
		}
223
224
		$total = 0;
225
		foreach ($this->respostaPreco as $produto) {
226
			if ($produto['coProduto'] === $coProduto) {
227
				$total += (float)str_replace(',', '.', $produto['pcFinal']);
228
			}
229
		}
230
231
		return $total;
232
	}
233
234
	/**
235
	 * Consulta os prazos de entrega dos produtos inseridos.
236
	 *
237
	 * @param string $dataPostagem Data de postagem no formato YYYY-MM-DD.
238
	 * @param string $cepOrigem CEP de origem.
239
	 * @param string $cepDestino CEP de destino.
240
	 * @param string|null $dtEvento Data do evento no formato DD-MM-YYYY (opcional).
241
	 * @return array Resposta da consulta de prazos.
242
	 * @throws ClienteException Em caso de erro na consulta de prazos.
243
	 */
244
	public function consultarPrazo($dataPostagem, $cepOrigem, $cepDestino, $dtEvento = null)
245
	{
246
		if (empty($this->produtos)) {
247
			throw new ClienteException('Nenhum produto inserido para consulta de prazo.');
248
		}
249
250
		$this->verificarToken(); // Verifica se o token de autenticação é válido e, se necessário, renova o token.
251
		$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.
252
253
		$chunks = $this->criarChunksDeProdutosPrazo($dataPostagem, $cepOrigem, $cepDestino, $dtEvento);
254
255
		$responses = $this->consultarPrazoEmChunks($chunks);
256
257
		$this->respostaPrazo = array_merge(...$responses);
258
		return $this->respostaPrazo;
259
	}
260
261
	/**
262
	 * Cria lotes de produtos divididos em grupos de no máximo 5 itens.
263
	 * 
264
	 * @param string $dataPostagem A data de postagem no formato YYYY-MM-DD.
265
	 * @param string $cepOrigem O CEP de origem do envio.
266
	 * @param string $cepDestino O CEP de destino do envio.
267
	 * @param string $dtEvento A data do evento no formato DD-MM-YYYY.
268
	 * @return array Os produtos divididos em lotes de no máximo 5 itens.
269
	 */
270
	private function criarChunksDeProdutosPrazo($dataPostagem, $cepOrigem, $cepDestino, $dtEvento)
271
	{
272
		return array_chunk(array_map(function ($produto) use ($dataPostagem, $cepOrigem, $cepDestino, $dtEvento) {
273
			return [
274
				"coProduto" => $produto['coProduto'],
275
				"nuRequisicao" => $produto['nuRequisicao'],
276
				"dtEvento" => $dtEvento,
277
				"cepOrigem" => $cepOrigem,
278
				"cepDestino" => $cepDestino,
279
				"dataPostagem" => $dataPostagem
280
			];
281
		}, $this->produtos), 5);
282
	}
283
284
	/**
285
	 * Consulta o prazo de entrega dos produtos em lotes e retorna as respostas.
286
	 * 
287
	 * @param array $chunks Os lotes de produtos a serem consultados.
288
	 * @return array As respostas da consulta de prazo de entrega.
289
	 * @throws ClienteException Se ocorrer um erro durante a consulta.
290
	 */
291
	private function consultarPrazoEmChunks($chunks)
292
	{
293
		$responses = [];
294
295
		foreach ($chunks as $chunk) {
296
			$ch = curl_init();
297
			curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/prazo/v1/nacional");
298
			curl_setopt($ch, CURLOPT_POST, 1);
299
			curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
300
				"idLote" => "1",
301
				"parametrosPrazo" => $chunk
302
			]));
303
			curl_setopt($ch, CURLOPT_HTTPHEADER, [
304
				"Content-Type: application/json",
305
				"Authorization: Bearer {$this->token}"
306
			]);
307
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
308
			$response = curl_exec($ch);
309
310
			if (curl_errno($ch)) {
311
				throw new ClienteException('Erro ao consultar prazo: ' . curl_error($ch));
312
			}
313
314
			$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
315
			curl_close($ch);
316
317
			if ($httpCode != 200) {
318
				throw new ClienteException('Erro ao consultar prazo', $httpCode, $response);
319
			}
320
321
			if (is_string($response)) {
322
				$responses[] = json_decode($response, true);
323
			} else {
324
				throw new ClienteException('Erro ao consultar prazo: resposta inválida recebida.');
325
			}
326
		}
327
328
		return $responses;
329
	}
330
331
	/**
332
	 * Consulta o prazo total de entrega de um produto específico.
333
	 *
334
	 * @param string $coProduto Código do produto.
335
	 * @return array Prazo total de entrega do produto.
336
	 * @throws ClienteException Em caso de erro na consulta de prazos ou se prazos não foram consultados ainda.
337
	 */
338
	public function consultarPrazoTotal($coProduto)
339
	{
340
		if (empty($this->respostaPrazo)) {
341
			throw new ClienteException('Prazo não foi consultado ainda.');
342
		}
343
344
		foreach ($this->respostaPrazo as $produto) {
345
			if ($produto['coProduto'] === $coProduto) {
346
				return $produto;
347
			}
348
		}
349
350
		throw new ClienteException('Produto não encontrado na resposta do prazo.');
351
	}
352
}
353