Passed
Push — main ( 089c5c...143cac )
by Pedro
02:10
created

Cliente::inserirProduto()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 2
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
        $responseDecoded = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

103
        $responseDecoded = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
104
        $this->tokenExpiration = time() + 3600; // Supondo que o token expira em 1 hora
105
106
        return $responseDecoded['token'];
107
    }
108
109
    /**
110
     * Verifica a validade do token e renova se necessário.
111
     */
112
    private function verificarToken()
113
    {
114
        if (time() >= $this->tokenExpiration) {
115
            $this->token = $this->obterToken();
116
        }
117
    }
118
119
    /**
120
     * Insere um produto na lista de produtos a serem consultados.
121
     *
122
     * @param string $coProduto Código do produto.
123
     * @param array $arrProduto Dados do produto.
124
     */
125
    public function inserirProduto($coProduto, array $arrProduto) 
126
    {
127
        $arrProduto['coProduto'] = $coProduto;
128
        $this->produtos[] = $arrProduto;
129
    }
130
131
    /**
132
     * Consulta os preços dos produtos inseridos.
133
     *
134
     * @return array Resposta da consulta de preços.
135
     * @throws ClienteException Em caso de erro na consulta de preços.
136
     */
137
    public function consultarPreco()
138
    {
139
        if (empty($this->produtos)) {
140
            throw new ClienteException('Nenhum produto inserido para consulta de preço.');
141
        }
142
143
        $this->verificarToken();
144
145
        $chunks = array_chunk($this->produtos, 5); // Divide os produtos em lotes de no máximo 5
146
147
        $responses = [];
148
149
        foreach ($chunks as $chunk) {
150
            $ch = curl_init();
151
            curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/preco/v1/nacional");
152
            curl_setopt($ch, CURLOPT_POST, 1);
153
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
154
                "idLote" => "1",
155
                "parametrosProduto" => $chunk
156
            ]));
157
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
158
                "Content-Type: application/json",
159
                "Authorization: Bearer {$this->token}"
160
            ]);
161
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
162
            $response = curl_exec($ch);
163
164
            if (curl_errno($ch)) {
165
                throw new ClienteException('Erro ao consultar preço: ' . curl_error($ch));
166
            }
167
168
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
169
            curl_close($ch);
170
171
            if ($httpCode != 200) {
172
                throw new ClienteException('Erro ao consultar preço', $httpCode, $response);
173
            }
174
175
            $responses[] = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
            $responses[] = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
176
        }
177
178
        $this->respostaPreco = array_merge(...$responses);
179
        return $this->respostaPreco;
180
    }
181
182
    /**
183
     * Consulta o preço total de um produto específico.
184
     *
185
     * @param string $coProduto Código do produto.
186
     * @return float Preço total do produto.
187
     * @throws ClienteException Em caso de erro na consulta de preços ou se preços não foram consultados ainda.
188
     */
189
    public function consultarPrecoTotal($coProduto)
190
    {
191
        if (empty($this->respostaPreco)) {
192
            throw new ClienteException('Preço não foi consultado ainda.');
193
        }
194
195
        $total = 0;
196
        foreach ($this->respostaPreco as $produto) {
197
            if ($produto['coProduto'] === $coProduto) {
198
                $total += (float)str_replace(',', '.', $produto['pcFinal']);
199
            }
200
        }
201
202
        return $total;
203
    }
204
205
    /**
206
     * Consulta os prazos de entrega dos produtos inseridos.
207
     *
208
     * @param string $dataPostagem Data de postagem no formato YYYY-MM-DD.
209
     * @param string $cepOrigem CEP de origem.
210
     * @param string $cepDestino CEP de destino.
211
     * @param string|null $dtEvento Data do evento no formato DD-MM-YYYY (opcional).
212
     * @return array Resposta da consulta de prazos.
213
     * @throws ClienteException Em caso de erro na consulta de prazos.
214
     */
215
    public function consultarPrazo($dataPostagem, $cepOrigem, $cepDestino, $dtEvento = null)
216
    {
217
        if (empty($this->produtos)) {
218
            throw new ClienteException('Nenhum produto inserido para consulta de preço.');
219
        }
220
221
        $this->verificarToken(); // Verifica se o token de autenticação é válido e, se necessário, renova o token.
222
223
        $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.
224
225
        // Divide os produtos em lotes de no máximo 5
226
        $chunks = array_chunk(array_map(function ($produto) use ($dataPostagem, $cepOrigem, $cepDestino, $dtEvento) {
227
            return [
228
                "coProduto" => $produto['coProduto'], // Código do produto
229
                "nuRequisicao" => $produto['nuRequisicao'], // Número de requisição do produto
230
                "dtEvento" => $dtEvento, // Data do evento (formato DD-MM-YYYY) 
231
                "cepOrigem" => $cepOrigem, // CEP de origem
232
                "cepDestino" => $cepDestino, // CEP de destino
233
                "dataPostagem" => $dataPostagem // Data de postagem (formato YYYY-MM-DD)
234
            ];
235
        }, $this->produtos), 5); // Aplica a função para cada produto no array $this->produtos e divide em lotes de 5
236
237
        $responses = [];
238
239
        foreach ($chunks as $chunk) {
240
            $ch = curl_init();
241
            curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/prazo/v1/nacional");
242
            curl_setopt($ch, CURLOPT_POST, 1);
243
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
244
                "idLote" => "1",
245
                "parametrosPrazo" => $chunk
246
            ]));
247
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
248
                "Content-Type: application/json",
249
                "Authorization: Bearer {$this->token}"
250
            ]);
251
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
252
            $response = curl_exec($ch);
253
254
            if (curl_errno($ch)) {
255
                throw new ClienteException('Erro ao consultar prazo: ' . curl_error($ch));
256
            }
257
258
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
259
            curl_close($ch);
260
261
            if ($httpCode != 200) {
262
                throw new ClienteException('Erro ao consultar prazo', $httpCode, $response);
263
            }
264
265
            $responses[] = json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

265
            $responses[] = json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
266
        }
267
268
        $this->respostaPrazo = array_merge(...$responses);
269
        return $this->respostaPrazo;
270
    }
271
272
    /**
273
     * Consulta o prazo total de entrega de um produto específico.
274
     *
275
     * @param string $coProduto Código do produto.
276
     * @return array Prazo total de entrega do produto.
277
     * @throws ClienteException Em caso de erro na consulta de prazos ou se prazos não foram consultados ainda.
278
     */
279
    public function consultarPrazoTotal($coProduto)
280
    {
281
        if (empty($this->respostaPrazo)) {
282
            throw new ClienteException('Prazo não foi consultado ainda.');
283
        }
284
285
        foreach ($this->respostaPrazo as $produto) {
286
            if ($produto['coProduto'] === $coProduto) {
287
                return $produto;
288
            }
289
        }
290
291
        throw new ClienteException('Produto não encontrado na resposta do prazo.');
292
    }
293
}
294