Passed
Push — master ( ce07c6...8257c0 )
by Roberto
04:28 queued 02:08
created

Tools::sefazEPEC()   C

Complexity

Conditions 10
Paths 50

Size

Total Lines 73

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 73
ccs 0
cts 59
cp 0
rs 6.7224
c 0
b 0
f 0
cc 10
nc 50
nop 2
crap 110

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace NFePHP\NFe;
4
5
/**
6
 * Class responsible for communication with SEFAZ extends
7
 * NFePHP\NFe\Common\Tools
8
 *
9
 * @category  NFePHP
10
 * @package   NFePHP\NFe\Tools
11
 * @copyright NFePHP Copyright (c) 2008-2020
12
 * @license   http://www.gnu.org/licenses/lgpl.txt LGPLv3+
13
 * @license   https://opensource.org/licenses/MIT MIT
14
 * @license   http://www.gnu.org/licenses/gpl.txt GPLv3+
15
 * @author    Roberto L. Machado <linux.rlm at gmail dot com>
16
 * @link      http://github.com/nfephp-org/sped-nfe for the canonical source repository
17
 */
18
19
use NFePHP\Common\Strings;
20
use NFePHP\Common\Signer;
21
use NFePHP\Common\UFList;
22
use NFePHP\NFe\Common\Tools as ToolsCommon;
23
use RuntimeException;
24
use InvalidArgumentException;
25
26
class Tools extends ToolsCommon
27
{
28
    const EVT_CONFIRMACAO = 210200; //only one per nfe seq=n
29
    const EVT_CIENCIA = 210210; //only one per nfe seq=1
30
    const EVT_DESCONHECIMENTO = 210220; //only one per nfe seq=n
31
    const EVT_NAO_REALIZADA = 210240; //only one per nfe but seq=n
32
    const EVT_CCE = 110110; //many seq=n
33
    const EVT_CANCELA = 110111; //only seq=1
34
    const EVT_CANCELASUBSTITUICAO = 110112;
35
    const EVT_EPEC = 110140; //only seq=1
36
    const EVT_ATORINTERESSADO = 110150; //many seq=n
37
    const EVT_COMPROVANTE_ENTREGA = 110130; //many seq=n
38
    const EVT_CANCELAMENTO_COMPROVANTE_ENTREGA = 110131; ///many seq=n
39
    const EVT_PRORROGACAO_1 = 111500;
40
    const EVT_PRORROGACAO_2 = 111501;
41
    const EVT_CANCELA_PRORROGACAO_1 = 111502;
42
    const EVT_CANCELA_PRORROGACAO_2 = 111503;
43
44
    /**
45
     * Request authorization to issue NFe in batch with one or more documents
46
     * @param array $aXml array of nfe's xml
47
     * @param string $idLote lote number
48
     * @param int $indSinc flag to use synchronous communication
49
     * @param bool $compactar flag to compress data with gzip
50
     * @param array $xmls array with xmls substitutes if contigency is on
51
     * @return string soap response xml
52
     * @throws InvalidArgumentException
53
     */
54
    public function sefazEnviaLote(
55
        $aXml,
56
        $idLote = '',
57
        $indSinc = 0,
58
        $compactar = false,
59
        &$xmls = []
60
    ) {
61
        if (!is_array($aXml)) {
62
            throw new InvalidArgumentException('Envia Lote: XMLs de NF-e deve ser um array!');
63
        }
64
        if ($indSinc == 1 && count($aXml) > 1) {
65
            throw new InvalidArgumentException('Envio sincrono deve ser usado para enviar '
66
                . 'uma UNICA nota por vez. Você está tentando enviar varias.');
67
        }
68
        $servico = 'NfeAutorizacao';
69
        $this->checkContingencyForWebServices($servico);
70
        if ($this->contingency->type != '') {
71
            // Em modo de contingencia esses XMLs deverão ser modificados e re-assinados e retornados
72
            // no parametro $xmls para serem armazenados pelo aplicativo pois serão alterados.
73
            foreach ($aXml as $doc) {
74
                //corrigir o xml para o tipo de contigência setado
75
                $xmls[] = $this->correctNFeForContingencyMode($doc);
76
            }
77
            $aXml = $xmls;
78
        }
79
        $ax = [];
80
        foreach ($aXml as $xml) {
81
            //verifica se o modelo do XML é o mesmo setado na classe
82
            //gera um exception se não for
83
            $this->checkModelFromXml($xml);
84
            $ax[] = trim(preg_replace("/<\?xml.*?\?>/", "", $xml));
85
        }
86
        $sxml = trim(implode("", $ax));
87
        $this->servico($servico, $this->config->siglaUF, $this->tpAmb);
88
        $request = "<enviNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
89
            . "<idLote>$idLote</idLote>"
90
            . "<indSinc>$indSinc</indSinc>"
91
            . "$sxml"
92
            . "</enviNFe>";
93
        $this->isValid($this->urlVersion, $request, 'enviNFe');
94
        $this->lastRequest = $request;
95
        //montagem dos dados da mensagem SOAP
96
        $parameters = ['nfeDadosMsg' => $request];
97
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
98
        if ($compactar) {
99
            $gzdata = base64_encode(gzencode($request, 9, FORCE_GZIP));
100
            $parameters = ['nfeDadosMsgZip' => $gzdata];
101
            $body = "<nfeDadosMsgZip xmlns=\"$this->urlNamespace\">$gzdata</nfeDadosMsgZip>";
102
        }
103
        $this->lastResponse = $this->sendRequest($body, $parameters);
104
        return $this->lastResponse;
105
    }
106
107
    /**
108
     * Check status of Batch of NFe sent by receipt of this shipment
109
     * @param string $recibo
110
     * @param int $tpAmb
111
     * @return string
112
     * @throws InvalidArgumentException
113
     */
114 1
    public function sefazConsultaRecibo($recibo, $tpAmb = null)
115
    {
116 1
        if (empty($recibo)) {
117 1
            throw new InvalidArgumentException('Consulta Recibo: numero do recibo vazio!');
118
        }
119
        if (empty($tpAmb)) {
120
            $tpAmb = $this->tpAmb;
121
        }
122
        //carrega serviço
123
        $servico = 'NfeRetAutorizacao';
124
        $this->checkContingencyForWebServices($servico);
125
        $this->servico($servico, $this->config->siglaUF, $tpAmb);
126
        if ($this->urlService == '') {
127
            $msg = "A consulta de NFe nao esta disponivel na SEFAZ {$this->config->siglaUF}!";
128
            throw new RuntimeException($msg);
129
        }
130
        $request = "<consReciNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
131
            . "<tpAmb>$tpAmb</tpAmb>"
132
            . "<nRec>$recibo</nRec>"
133
            . "</consReciNFe>";
134
        $this->isValid($this->urlVersion, $request, 'consReciNFe');
135
        $this->lastRequest = $request;
136
        $parameters = ['nfeDadosMsg' => $request];
137
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
138
        $this->lastResponse = $this->sendRequest($body, $parameters);
139
        return $this->lastResponse;
140
    }
141
142
    /**
143
     * Check the NFe status for the 44-digit key and retrieve the protocol
144
     * @param string $chave
145
     * @param int $tpAmb
146
     * @return string
147
     * @throws InvalidArgumentException
148
     */
149 3
    public function sefazConsultaChave($chave, $tpAmb = null)
150
    {
151 3
        if (empty($chave)) {
152 1
            throw new InvalidArgumentException('Consulta chave: a chave esta vazia!');
153
        }
154 2
        if (strlen($chave) != 44 || !is_numeric($chave)) {
155 2
            throw new InvalidArgumentException("Consulta chave: chave \"$chave\" invalida!");
156
        }
157
        $uf = UFList::getUFByCode(substr($chave, 0, 2));
158
        if (empty($tpAmb)) {
159
            $tpAmb = $this->tpAmb;
160
        }
161
        //carrega serviço
162
        $servico = 'NfeConsultaProtocolo';
163
        $this->checkContingencyForWebServices($servico);
164
        $this->servico($servico, $uf, $tpAmb);
165
        $request = "<consSitNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
166
            . "<tpAmb>$tpAmb</tpAmb>"
167
            . "<xServ>CONSULTAR</xServ>"
168
            . "<chNFe>$chave</chNFe>"
169
            . "</consSitNFe>";
170
        $this->isValid($this->urlVersion, $request, 'consSitNFe');
171
        $this->lastRequest = $request;
172
        $parameters = ['nfeDadosMsg' => $request];
173
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
174
        $this->lastResponse = $this->sendRequest($body, $parameters);
175
        return $this->lastResponse;
176
    }
177
178
    /**
179
     * Request to disable one or an NFe sequence of a given series
180
     * @param int $nSerie
181
     * @param int $nIni
182
     * @param int $nFin
183
     * @param string $xJust
184
     * @param int $tpAmb
185
     * @param string $ano
186
     * @return string
187
     * @throws InvalidArgumentException
188
     */
189
    public function sefazInutiliza($nSerie, $nIni, $nFin, $xJust, $tpAmb = null, $ano = null)
190
    {
191
        if (empty($nIni) || empty($nFin) || empty($xJust)) {
192
            throw new InvalidArgumentException('Inutilizacao: parametros incompletos!');
193
        }
194
        if (empty($tpAmb)) {
195
            $tpAmb = $this->tpAmb;
196
        }
197
        $xJust = Strings::replaceUnacceptableCharacters($xJust);
198
        $servico = 'NfeInutilizacao';
199
        $this->checkContingencyForWebServices($servico);
200
        //carrega serviço
201
        $this->servico($servico, $this->config->siglaUF, $tpAmb);
202
        $cnpj = $this->config->cnpj;
203
        $strAno = $ano;
204
        if (empty($ano)) {
205
            $strAno = (string) date('y');
206
        }
207
        $strSerie = str_pad($nSerie, 3, '0', STR_PAD_LEFT);
208
        $strInicio = str_pad($nIni, 9, '0', STR_PAD_LEFT);
209
        $strFinal = str_pad($nFin, 9, '0', STR_PAD_LEFT);
210
        $idInut = "ID"
211
            . $this->urlcUF
212
            . $strAno
213
            . $cnpj
214
            . $this->modelo
215
            . $strSerie
216
            . $strInicio
217
            . $strFinal;
218
        //limpa os caracteres indesejados da justificativa
219
        $xJust = Strings::replaceUnacceptableCharacters($xJust);
220
        //montagem do corpo da mensagem
221
        $msg = "<inutNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">" .
222
            "<infInut Id=\"$idInut\">" .
223
            "<tpAmb>$tpAmb</tpAmb>" .
224
            "<xServ>INUTILIZAR</xServ>" .
225
            "<cUF>$this->urlcUF</cUF>" .
226
            "<ano>$strAno</ano>" .
227
            "<CNPJ>$cnpj</CNPJ>" .
228
            "<mod>$this->modelo</mod>" .
229
            "<serie>$nSerie</serie>" .
230
            "<nNFIni>$nIni</nNFIni>" .
231
            "<nNFFin>$nFin</nNFFin>" .
232
            "<xJust>$xJust</xJust>" .
233
            "</infInut></inutNFe>";
234
        //assina a solicitação
235
        $request = Signer::sign(
236
            $this->certificate,
237
            $msg,
238
            'infInut',
239
            'Id',
240
            $this->algorithm,
241
            $this->canonical
242
        );
243
        $request = Strings::clearXmlString($request, true);
244
        $this->isValid($this->urlVersion, $request, 'inutNFe');
245
        $this->lastRequest = $request;
246
        $parameters = ['nfeDadosMsg' => $request];
247
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
248
        $this->lastResponse = $this->sendRequest($body, $parameters);
249
        return $this->lastResponse;
250
    }
251
252
    /**
253
     * Search for the registration data of an NFe issuer,
254
     * if in contingency mode this service will cause a
255
     * Exception and remember not all Sefaz have this service available,
256
     * so it will not work in some cases.
257
     * @param string $uf federation unit (abbreviation)
258
     * @param string $cnpj CNPJ number (optional)
259
     * @param string $iest IE number (optional)
260
     * @param string $cpf CPF number (optional)
261
     * @return string xml soap response
262
     * @throws InvalidArgumentException
263
     */
264
    public function sefazCadastro($uf, $cnpj = '', $iest = '', $cpf = '')
265
    {
266
        $filter = '';
267
        if (!empty($cnpj)) {
268
            $filter = "<CNPJ>$cnpj</CNPJ>";
269
        } elseif (!empty($iest)) {
270
            $filter = "<IE>$iest</IE>";
271
        } elseif (!empty($cpf)) {
272
            $filter = "<CPF>$cpf</CPF>";
273
        }
274
        if (empty($uf) || empty($filter)) {
275
            throw new InvalidArgumentException('Sigla UF esta vazia ou CNPJ+IE+CPF vazios!');
276
        }
277
        //carrega serviço
278
        $servico = 'NfeConsultaCadastro';
279
        $this->checkContingencyForWebServices($servico);
280
        $this->servico($servico, $uf, $this->tpAmb, true);
281
        $request = "<ConsCad xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
282
            . "<infCons>"
283
            . "<xServ>CONS-CAD</xServ>"
284
            . "<UF>$uf</UF>"
285
            . "$filter"
286
            . "</infCons>"
287
            . "</ConsCad>";
288
        if (strtoupper($uf) == 'MT') {
289
            $request = "<nfeDadosMsg>$request</nfeDadosMsg>" ;
290
        }
291
        $this->isValid($this->urlVersion, $request, 'consCad');
292
        $this->lastRequest = $request;
293
        $parameters = ['nfeDadosMsg' => $request];
294
        if ($this->urlVersion === '2.00') {
295
            $this->objHeader = new \SOAPHeader(
296
                $this->urlNamespace,
297
                'nfeCabecMsg',
298
                ['cUF' => $this->urlcUF, 'versaoDados' => $this->urlVersion]
299
            );
300
        }
301
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
302
        $this->lastResponse = $this->sendRequest($body, $parameters);
303
        return $this->lastResponse;
304
    }
305
306
    /**
307
     * Check services status SEFAZ/SVC
308
     * If $uf is empty use normal check with contingency
309
     * If $uf is NOT empty ignore contingency mode
310
     * @param string $uf  initials of federation unit
311
     * @param int $tpAmb
312
     * @return string xml soap response
313
     */
314
    public function sefazStatus($uf = '', $tpAmb = null)
315
    {
316
        if (empty($tpAmb)) {
317
            $tpAmb = $this->tpAmb;
318
        }
319
        $ignoreContingency = true;
320
        if (empty($uf)) {
321
            $uf = $this->config->siglaUF;
322
            $ignoreContingency = false;
323
        }
324
        $servico = 'NfeStatusServico';
325
        $this->checkContingencyForWebServices($servico);
326
        $this->servico($servico, $uf, $tpAmb, $ignoreContingency);
327
        $request = "<consStatServ xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
328
            . "<tpAmb>$tpAmb</tpAmb>"
329
            . "<cUF>$this->urlcUF</cUF>"
330
            . "<xServ>STATUS</xServ>"
331
            . "</consStatServ>";
332
        $this->isValid($this->urlVersion, $request, 'consStatServ');
333
        $this->lastRequest = $request;
334
        $parameters = ['nfeDadosMsg' => $request];
335
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
336
        $this->lastResponse = $this->sendRequest($body, $parameters);
337
        return $this->lastResponse;
338
    }
339
340
    /**
341
     * Service for the distribution of summary information and
342
     * electronic tax documents of interest to an actor.
343
     * @param integer $ultNSU  last NSU number recived
344
     * @param integer $numNSU  NSU number you wish to consult
345
     * @param string $fonte data source 'AN' and for some cases it may be 'RS'
346
     * @return string
347
     */
348
    public function sefazDistDFe($ultNSU = 0, $numNSU = 0, $fonte = 'AN')
349
    {
350
        //carrega serviço
351
        $servico = 'NfeDistribuicaoDFe';
352
        $this->checkContingencyForWebServices($servico);
353
        $this->servico($servico, $fonte, $this->tpAmb, true);
354
        $cUF = UFList::getCodeByUF($this->config->siglaUF);
355
        $cnpj = $this->config->cnpj;
356
        $ultNSU = str_pad($ultNSU, 15, '0', STR_PAD_LEFT);
357
        $tagNSU = "<distNSU><ultNSU>$ultNSU</ultNSU></distNSU>";
358
        if ($numNSU != 0) {
359
            $numNSU = str_pad($numNSU, 15, '0', STR_PAD_LEFT);
360
            $tagNSU = "<consNSU><NSU>$numNSU</NSU></consNSU>";
361
        }
362
        //monta a consulta
363
        $consulta = "<distDFeInt xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
364
            . "<tpAmb>".$this->tpAmb."</tpAmb>"
365
            . "<cUFAutor>$cUF</cUFAutor>";
366
        if ($this->typePerson === 'J') {
367
            $consulta .= "<CNPJ>$cnpj</CNPJ>";
368
        } else {
369
            $consulta .= "<CPF>$cnpj</CPF>";
370
        }
371
        $consulta .= "$tagNSU"
372
            . "</distDFeInt>";
373
        //valida o xml da requisição
374
        $this->isValid($this->urlVersion, $consulta, 'distDFeInt');
375
        $this->lastRequest = $consulta;
376
        //montagem dos dados da mensagem SOAP
377
        $request = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$consulta</nfeDadosMsg>";
378
        $parameters = ['nfeDistDFeInteresse' => $request];
379
        $body = "<nfeDistDFeInteresse xmlns=\"$this->urlNamespace\">"
380
            . $request
381
            . "</nfeDistDFeInteresse>";
382
        //este webservice não requer cabeçalho
383
        $this->objHeader = null;
384
        $this->lastResponse = $this->sendRequest($body, $parameters);
385
        return $this->lastResponse;
386
    }
387
388
    /**
389
     * Request authorization for Letter of Correction
390
     * @param string $chave
391
     * @param string $xCorrecao
392
     * @param int $nSeqEvento
393
     * @return string
394
     * @throws InvalidArgumentException
395
     */
396
    public function sefazCCe($chave, $xCorrecao, $nSeqEvento = 1)
397
    {
398
        if (empty($chave) || empty($xCorrecao)) {
399
            throw new InvalidArgumentException('CC-e: chave ou motivo da correcao vazio!');
400
        }
401
        $uf = $this->validKeyByUF($chave);
402
        $xCorrecao = Strings::replaceUnacceptableCharacters(substr(trim($xCorrecao), 0, 1000));
403
        $xCondUso = 'A Carta de Correcao e disciplinada pelo paragrafo '
404
            . '1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 '
405
            . 'e pode ser utilizada para regularizacao de erro ocorrido '
406
            . 'na emissao de documento fiscal, desde que o erro nao esteja '
407
            . 'relacionado com: I - as variaveis que determinam o valor '
408
            . 'do imposto tais como: base de calculo, aliquota, '
409
            . 'diferenca de preco, quantidade, valor da operacao ou da '
410
            . 'prestacao; II - a correcao de dados cadastrais que implique '
411
            . 'mudanca do remetente ou do destinatario; III - a data de '
412
            . 'emissao ou de saida.';
413
        $tagAdic = "<xCorrecao>"
414
            . $xCorrecao
415
            . "</xCorrecao><xCondUso>$xCondUso</xCondUso>";
416
        return $this->sefazEvento($uf, $chave, self::EVT_CCE, $nSeqEvento, $tagAdic);
417
    }
418
    
419
    /**
420
     * Evento do Ator Interessado
421
     * NOTA: NT2020.007_v1.00a
422
     * @param \stdClass $std
423
     * @return string
424
     */
425
    public function sefazAtorInteressado(\stdClass $std)
426
    {
427
        $xCondUso = 'O emitente ou destinatário da NF-e, declara que permite o '
428
            . 'transportador declarado no campo CNPJ/CPF deste evento a '
429
            . 'autorizar os transportadores subcontratados ou redespachados a '
430
            . 'terem acesso ao download da NF-e';
431
        if (empty($std->verAplic) && !empty($this->verAplic)) {
432
            $std->verAplic = $this->verAplic;
433
        }
434
        $cUF = UFList::getCodeByUF($this->config->siglaUF);
435
        $tagAdic = "<cOrgaoAutor>{$cUF}</cOrgaoAutor>"
436
            . "<tpAutor>{$std->tpAutor}</tpAutor>"
437
            . "<verAplic>{$std->verAplic}</verAplic>"
438
            . "<autXML>";
439
        $tagAdic .=  !empty($std->CNPJ)
440
            ? "<CNPJ>{$std->CNPJ}</CNPJ>"
441
            : "<CPF>{$std->CPF}</CPF>";
442
        $tagAdic .= "</autXML>"
443
            . "<tpAutorizacao>{$std->tpAutorizacao}</tpAutorizacao>"
444
            . "<xCondUso>$xCondUso</xCondUso>";
445
        return $this->sefazEvento(
446
            'AN',
447
            $std->chNFe,
448
            self::EVT_ATORINTERESSADO,
449
            $std->nSeqEvento,
450
            $tagAdic
451
        );
452
    }
453
454
    /**
455
     * Request extension of the term of return of products of an NF-e of
456
     * consignment for industrialization to order with suspension of ICMS
457
     * in interstate operations
458
     * @param string $chNFe
459
     * @param string $nProt
460
     * @param integer $tipo 1-primerio prazo, 2-segundo prazo
461
     * @param array $itens
462
     * @param integer $nSeqEvento
463
     * @return string
464
     */
465
    public function sefazEPP(
466
        $chNFe,
467
        $nProt,
468
        $itens = [],
469
        $tipo = 1,
470
        $nSeqEvento = 1
471
    ) {
472
        $uf = UFList::getUFByCode(substr($chNFe, 0, 2));
473
        //pedido de prorrogação primero prazo
474
        $tpEvento = 111500;
475
        if ($tipo == 2) {
476
            //pedido de prorrogação segundo prazo
477
            $tpEvento = 111501;
478
        }
479
        $tagAdic = "<nProt>$nProt</nProt>";
480
        foreach ($itens as $item) {
481
            $tagAdic .= "<itemPedido numItem=\""
482
                . $item[0]
483
                . "\"><qtdeItem>"
484
                . $item[1]
485
                ."</qtdeItem></itemPedido>";
486
        }
487
        return $this->sefazEvento(
488
            $uf,
489
            $chNFe,
490
            $tpEvento,
491
            $nSeqEvento,
492
            $tagAdic
493
        );
494
    }
495
496
    /**
497
     * Request the cancellation of the request for an extension of the term
498
     * of return of products of an NF-e of consignment for industrialization
499
     * by order with suspension of ICMS in interstate operations
500
     * @param string $chave
501
     * @param string $nProt
502
     * @param integer $tipo 1-primerio prazo, 2-segundo prazo
503
     * @param integer $nSeqEvento
504
     * @return string
505
     * @throws InvalidArgumentException
506
     */
507
    public function sefazECPP($chave, $nProt, $tipo, $nSeqEvento = 1)
508
    {
509
        if (empty($chave) || empty($nProt)) {
510
            throw new InvalidArgumentException('A chave ou o numero do protocolo estão vazios!');
511
        }
512
        $uf = UFList::getUFByCode(substr($chave, 0, 2));
513
        $tpEvento = self::EVT_CANCELA_PRORROGACAO_1; //111502;
514
        $origEvent = self::EVT_PRORROGACAO_1; //111500;
515
        if ($tipo == 2) {
516
            //pedido de cancelamento do segundo prazo
517
            $tpEvento = self::EVT_CANCELA_PRORROGACAO_2; //111503;
518
            $origEvent = self::EVT_PRORROGACAO_2; //111501;
519
        }
520
        $sSeqEvento = str_pad($nSeqEvento, 2, "0", STR_PAD_LEFT);
521
        $idPedidoCancelado = "ID{$origEvent}{$chave}{$sSeqEvento}";
522
        $tagAdic = "<idPedidoCancelado>"
523
                . "$idPedidoCancelado"
524
                . "</idPedidoCancelado>"
525
                . "<nProt>$nProt</nProt>";
526
        return $this->sefazEvento($uf, $chave, $tpEvento, $nSeqEvento, $tagAdic);
527
    }
528
529
    /**
530
     * Requires nfe cancellation
531
     * @param  string $chave key of NFe
532
     * @param  string $xJust justificative 255 characters max
533
     * @param  string $nProt protocol number
534
     * @return string
535
     * @throws InvalidArgumentException
536
     */
537
    public function sefazCancela($chave, $xJust, $nProt)
538
    {
539
        if (empty($chave) || empty($xJust) || empty($nProt)) {
540
            throw new InvalidArgumentException('Cancelamento: chave, just ou numprot vazio!');
541
        }
542
        $uf = $this->validKeyByUF($chave);
543
        $xJust = Strings::replaceUnacceptableCharacters(substr(trim($xJust), 0, 255));
544
        $nSeqEvento = 1;
545
        $tagAdic = "<nProt>$nProt</nProt><xJust>$xJust</xJust>";
546
        return $this->sefazEvento($uf, $chave, self::EVT_CANCELA, $nSeqEvento, $tagAdic);
547
    }
548
549
    /**
550
     * Requires nfe cancellation by substitution
551
     * @param string $chave key of NFe
552
     * @param string $xJust justificative 255 characters max
553
     * @param string $nProt protocol number
554
     * @param string $chNFeRef key of New NFe
555
     * @param string $verAplic version of applicative
556
     * @return string
557
     * @throws InvalidArgumentException
558
     */
559
    public function sefazCancelaPorSubstituicao($chave, $xJust, $nProt, $chNFeRef, $verAplic = null)
560
    {
561
        if ($this->modelo != 65) {
562
            throw new InvalidArgumentException(
563
                'Cancelamento pro Substituição deve ser usado apenas para '
564
                . 'operações com modelo 65 NFCe'
565
            );
566
        }
567
        if (empty($verAplic) && !empty($this->verAplic)) {
568
            $verAplic = $this->verAplic;
569
        }
570
        if (empty($chave) || empty($xJust) || empty($nProt)
571
            || empty($chNFeRef) || empty($verAplic)) {
572
            throw new InvalidArgumentException(
573
                'CancelamentoPorSubs: chave da NFCe cancelada, justificativa, '
574
                . 'protocolo, chave da NFCe substituta, ou versão do aplicativo '
575
                . 'emissor não podem ser vazios!'
576
            );
577
        }
578
        $uf = $this->validKeyByUF($chave);
579
        $xJust = Strings::replaceUnacceptableCharacters(substr(trim($xJust), 0, 255));
580
        $nSeqEvento = 1;
581
        $cOrgao = substr($chave, 0, 2);
582
        $tagAdic = "<cOrgaoAutor>$cOrgao</cOrgaoAutor>"
583
            . "<tpAutor>1</tpAutor>"
584
            . "<verAplic>$verAplic</verAplic>"
585
            . "<nProt>$nProt</nProt>"
586
            . "<xJust>$xJust</xJust>"
587
            . "<chNFeRef>$chNFeRef</chNFeRef>";
588
        return $this->sefazEvento($uf, $chave, self::EVT_CANCELASUBSTITUICAO, $nSeqEvento, $tagAdic);
589
    }
590
591
    /**
592
     * Request the registration of the manifestation of recipient
593
     * @param string $chave
594
     * @param int $tpEvento
595
     * @param string $xJust Justification for not carrying out the operation
596
     * @param int $nSeqEvento
597
     * @return string
598
     * @throws InvalidArgumentException
599
     */
600
    public function sefazManifesta($chave, $tpEvento, $xJust = '', $nSeqEvento = 1)
601
    {
602
        if (empty($chave) || empty($tpEvento)) {
603
            throw new InvalidArgumentException('Manifestacao: chave ou tipo de evento vazio!');
604
        }
605
        $tagAdic = '';
606
        if ($tpEvento == self::EVT_NAO_REALIZADA) {
607
            $xJust = Strings::replaceUnacceptableCharacters(substr(trim($xJust), 0, 255));
608
            $tagAdic = "<xJust>$xJust</xJust>";
609
        }
610
        return $this->sefazEvento('AN', $chave, $tpEvento, $nSeqEvento, $tagAdic);
611
    }
612
613
    /**
614
     * Request the registration of the manifestation of recipient in batch
615
     * @param \stdClass $std
616
     * @return string
617
     * @throws InvalidArgumentException
618
     * @throws RuntimeException
619
     */
620
    public function sefazManifestaLote(\stdClass $std)
621
    {
622
        $allowed = [
623
            self::EVT_CONFIRMACAO,
624
            self::EVT_CIENCIA,
625
            self::EVT_DESCONHECIMENTO,
626
            self::EVT_NAO_REALIZADA,
627
        ];
628
        if (empty($std) || empty($std->evento)) {
629
            throw new InvalidArgumentException('Manifestacao: parametro "std" ou evento estao vazios!');
630
        }
631
        if (count($std->evento) > 20) {
632
            throw new RuntimeException('Manifestacao: o lote de eventos esta limitado a 20!');
633
        }
634
        $evt = new \stdClass();
635
        $i = 0;
636
        foreach ($std->evento as $s) {
637
            if (!in_array($s->tpEvento, $allowed)) { // se o evento não estiver entre os permitidos ignore
638
                continue;
639
            }
640
            $tagAdic = '';
641
            if ($s->tpEvento == self::EVT_NAO_REALIZADA) {
642
                $xJust = Strings::replaceUnacceptableCharacters(substr(trim($s->xJust), 0, 255));
643
                $tagAdic = "<xJust>$xJust</xJust>";
644
            }
645
            $evt->evento[$i] = new \stdClass();
646
            $evt->evento[$i]->chave = $s->chNFe;
647
            $evt->evento[$i]->tpEvento = $s->tpEvento;
648
            $evt->evento[$i]->nSeqEvento = $s->nSeqEvento;
649
            $evt->evento[$i]->tagAdic = $tagAdic;
650
            $i++;
651
        }
652
        return $this->sefazEventoLote('AN', $evt);
653
    }
654
    
655
    /**
656
     * Send event for delivery receipt
657
     * @param \stdClass $std
658
     * @return string
659
     */
660
    public function sefazComprovanteEntrega(\stdClass $std)
661
    {
662
        if (empty($std->verAplic) && !empty($this->verAplic)) {
663
            $std->verAplic = $this->verAplic;
664
        }
665
        $hash = sha1($std->chNFe . $std->imagem);
666
        $datahash = date('Y-m-d\TH:i:sP');
667
        $cod = UFList::getCodeByUF($this->config->siglaUF);
668
        $tagAdic = "<cOrgaoAutor>{$cod}</cOrgaoAutor>"
669
            . "<tpAutor>1</tpAutor>"
670
            . "<verAplic>{$std->verAplic}</verAplic>"
671
            . "<dhEntrega>{$std->data_recebimento}</dhEntrega>"
672
            . "<nDoc>{$std->documento_recebedor}</nDoc>"
673
            . "<xNome>{$std->nome_recebedor}</xNome>";
674
        if (!empty($std->latitude) && !empty($std->longitude)) {
675
            $tagAdic .= "<latGPS>{$std->latitude}</latGPS>"
676
            . "<longGPS>{$std->longitude}</longGPS>";
677
        }
678
        $tagAdic .= "<hashComprovante>{$hash}</hashComprovante>"
679
            . "<dhHashComprovante>{$datahash}</dhHashComprovante>";
680
        $tpEvento = self::EVT_COMPROVANTE_ENTREGA;
681
        if ($std->cancelar == true) {
682
            $tpEvento = self::EVT_CANCELAMENTO_COMPROVANTE_ENTREGA;
683
        }
684
        return $this->sefazEvento(
685
            'AN',
686
            $std->chNFe,
687
            $tpEvento,
688
            $std->nSeqEvento,
689
            $tagAdic
690
        );
691
    }
692
    
693
    /**
694
     * Send event to SEFAZ in batch
695
     * @param string $uf
696
     * @param \stdClass $std
697
     * @return string
698
     * @throws RuntimeException
699
     * @throws InvalidArgumentException
700
     */
701
    public function sefazEventoLote($uf, \stdClass $std)
702
    {
703
        if (empty($uf) || empty($std)) {
704
            throw new InvalidArgumentException('Evento Lote: UF ou parametro "std" vazio!');
705
        }
706
        if (count($std->evento) > 20) {
707
            throw new RuntimeException('Evento Lote: o lote de eventos esta limitado a 20!');
708
        }
709
        $servico = 'RecepcaoEvento';
710
        $this->checkContingencyForWebServices($servico);
711
        $this->servico($servico, $uf, $this->tpAmb, false);
712
        $batchRequest = '';
713
        foreach ($std->evento as $evt) {
714
            if ($evt->tpEvento == self::EVT_EPEC) {
715
                continue; //não é possivel enviar EPEC com outros eventos
716
            }
717
            $ev = $this->tpEv($evt->tpEvento);
718
            $descEvento = $ev->desc;
719
            $cnpj = $this->config->cnpj;
720
            $dt = new \DateTime('now', new \DateTimeZone($this->timezone));
721
            $dhEvento = $dt->format('Y-m-d\TH:i:sP');
722
            $sSeqEvento = str_pad($evt->nSeqEvento, 2, "0", STR_PAD_LEFT);
723
            $eventId = "ID".$evt->tpEvento.$evt->chave.$sSeqEvento;
724
            $cOrgao = UFList::getCodeByUF($uf);
725
            $request = "<evento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
726
                . "<infEvento Id=\"$eventId\">"
727
                . "<cOrgao>$cOrgao</cOrgao>"
728
                . "<tpAmb>$this->tpAmb</tpAmb>";
729
            if ($this->typePerson === 'J') {
730
                $request .= "<CNPJ>$cnpj</CNPJ>";
731
            } else {
732
                $request .= "<CPF>$cnpj</CPF>";
733
            }
734
            $request .= "<chNFe>$evt->chave</chNFe>"
735
                . "<dhEvento>$dhEvento</dhEvento>"
736
                . "<tpEvento>$evt->tpEvento</tpEvento>"
737
                . "<nSeqEvento>$evt->nSeqEvento</nSeqEvento>"
738
                . "<verEvento>$this->urlVersion</verEvento>"
739
                . "<detEvento versao=\"$this->urlVersion\">"
740
                . "<descEvento>$descEvento</descEvento>"
741
                . "$evt->tagAdic"
742
                . "</detEvento>"
743
                . "</infEvento>"
744
                . "</evento>";
745
            //assinatura dos dados
746
            $request = Signer::sign(
747
                $this->certificate,
748
                $request,
749
                'infEvento',
750
                'Id',
751
                $this->algorithm,
752
                $this->canonical
753
            );
754
            $batchRequest .= Strings::clearXmlString($request, true);
755
        }
756
        $dt = new \DateTime('now', new \DateTimeZone($this->timezone));
757
        $lote = $dt->format('YmdHis') . rand(0, 9);
758
        $request = "<envEvento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
759
            . "<idLote>$lote</idLote>"
760
            . $batchRequest
761
            . "</envEvento>";
762
        $this->isValid($this->urlVersion, $request, 'envEvento');
763
        $this->lastRequest = $request;
764
        $parameters = ['nfeDadosMsg' => $request];
765
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
766
        $this->lastResponse = $this->sendRequest($body, $parameters);
767
        return $this->lastResponse;
768
    }
769
770
    /**
771
     * Request authorization for issuance in contingency EPEC
772
     * @param string $xml
773
     * @param string $verAplic
774
     * @return string
775
     * @throws InvalidArgumentException
776
     * @throws RuntimeException
777
     */
778
    public function sefazEPEC(&$xml, $verAplic = null)
779
    {
780
        if (empty($xml)) {
781
            throw new InvalidArgumentException('EPEC: parâmetro xml esta vazio!');
782
        }
783
        $nSeqEvento = 1;
784
        if ($this->contingency->type !== 'EPEC') {
785
            throw new RuntimeException('A contingencia EPEC deve estar ativada!');
786
        }
787
        //ajusta a NFe para a contingência definida e assina novamente
788
        $xml = $this->correctNFeForContingencyMode($xml);
789
        //extrai os dados da NFe
790
        $dom = new \DOMDocument('1.0', 'UTF-8');
791
        $dom->preserveWhiteSpace = false;
792
        $dom->formatOutput = false;
793
        $dom->loadXML($xml);
794
        $infNFe = $dom->getElementsByTagName('infNFe')->item(0);
795
        $emit = $dom->getElementsByTagName('emit')->item(0);
796
        $dest = $dom->getElementsByTagName('dest')->item(0);
797
        $cOrgaoAutor = UFList::getCodeByUF($this->config->siglaUF);
798
        $chNFe = substr($infNFe->getAttribute('Id'), 3, 44);
799
        
800
        // EPEC
801
        $dhEmi = $dom->getElementsByTagName('dhEmi')->item(0)->nodeValue;
802
        $tpNF = $dom->getElementsByTagName('tpNF')->item(0)->nodeValue;
803
        $emitIE = $emit->getElementsByTagName('IE')->item(0)->nodeValue;
804
        $destUF = $dest->getElementsByTagName('UF')->item(0)->nodeValue;
805
        $total = $dom->getElementsByTagName('total')->item(0);
806
        $vNF = $total->getElementsByTagName('vNF')->item(0)->nodeValue;
807
        $vICMS = $total->getElementsByTagName('vICMS')->item(0)->nodeValue;
808
        $vST = $total->getElementsByTagName('vST')->item(0)->nodeValue;
809
        $dID = !empty($dest->getElementsByTagName('CNPJ')->item(0))
810
            ? $dest->getElementsByTagName('CNPJ')->item(0)->nodeValue
811
            : null;
812
        if (!empty($dID)) {
813
            $destID = "<CNPJ>$dID</CNPJ>";
814
        } else {
815
            $dID = $dest->getElementsByTagName('CPF')->item(0)->nodeValue;
816
            if (!empty($dID)) {
817
                $destID = "<CPF>$dID</CPF>";
818
            } else {
819
                $dID = $dest->getElementsByTagName('idEstrangeiro')
820
                    ->item(0)
821
                    ->nodeValue;
822
                $destID = "<idEstrangeiro>$dID</idEstrangeiro>";
823
            }
824
        }
825
        $dIE = !empty($dest->getElementsByTagName('IE')->item(0)->nodeValue)
826
            ? $dest->getElementsByTagName('IE')->item(0)->nodeValue
827
            : '';
828
        $destIE = '';
829
        if (!empty($dIE)) {
830
            $destIE = "<IE>{$dIE}</IE>";
831
        }
832
        if (empty($verAplic) && !empty($this->verAplic)) {
833
            $verAplic = $this->verAplic;
834
        }
835
        $tagAdic = "<cOrgaoAutor>$cOrgaoAutor</cOrgaoAutor>"
836
            . "<tpAutor>1</tpAutor>"
837
            . "<verAplic>{$verAplic}</verAplic>"
838
            . "<dhEmi>{$dhEmi}</dhEmi>"
839
            . "<tpNF>{$tpNF}</tpNF>"
840
            . "<IE>{$emitIE}</IE>"
841
            . "<dest>"
842
            . "<UF>{$destUF}</UF>"
843
            . $destID
844
            . $destIE
845
            . "<vNF>{$vNF}</vNF>"
846
            . "<vICMS>{$vICMS}</vICMS>"
847
            . "<vST>{$vST}</vST>"
848
            . "</dest>";
849
        return $this->sefazEvento('AN', $chNFe, self::EVT_EPEC, $nSeqEvento, $tagAdic);
850
    }
851
852
    /**
853
     * Send event to SEFAZ
854
     * @param string $uf
855
     * @param string $chave
856
     * @param int $tpEvento
857
     * @param int $nSeqEvento
858
     * @param string $tagAdic
859
     * @return string
860
     */
861
    public function sefazEvento(
862
        $uf,
863
        $chave,
864
        $tpEvento,
865
        $nSeqEvento = 1,
866
        $tagAdic = ''
867
    ) {
868
        $eventos = [
869
            self::EVT_CCE => ['versao' => '1.00', 'nome' => 'envCCe'],
870
            self::EVT_CANCELA => ['versao' => '1.00', 'nome' => 'envEventoCancNFe'],
871
            self::EVT_CANCELASUBSTITUICAO => ['versao' => '1.00', 'nome' => 'envEventoCancSubst'],
872
            self::EVT_ATORINTERESSADO => ['versao' => '1.00', 'nome' => 'envEventoAtorInteressado'],
873
            self::EVT_COMPROVANTE_ENTREGA => ['versao' => '1.00', 'nome' => 'envEventoComprovanteEntrega'],
874
            self::EVT_CANCELAMENTO_COMPROVANTE_ENTREGA => ['versao' => '1.00', 'nome' => 'envEventoComprovanteEntrega'],
875
            self::EVT_CIENCIA => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
876
            self::EVT_CONFIRMACAO => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
877
            self::EVT_DESCONHECIMENTO => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
878
            self::EVT_NAO_REALIZADA => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
879
            self::EVT_PRORROGACAO_1 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
880
            self::EVT_PRORROGACAO_2 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
881
            self::EVT_CANCELA_PRORROGACAO_1 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
882
            self::EVT_CANCELA_PRORROGACAO_2 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
883
        ];
884
        $verEvento = $this->urlVersion;
885
        if (!empty($eventos[$tpEvento])) {
886
            $evt = $eventos[$tpEvento];
887
            $verEvento = $evt['versao'];
888
        }
889
        $ignore = $tpEvento == self::EVT_EPEC;
890
        $servico = 'RecepcaoEvento';
891
        $this->checkContingencyForWebServices($servico);
892
        $this->servico($servico, $uf, $this->tpAmb, $ignore);
893
        $ev = $this->tpEv($tpEvento);
894
        $descEvento = $ev->desc;
895
        $cnpj = isset($this->config->cnpj) ? $this->config->cnpj : '';
896
        $dt = new \DateTime(date("Y-m-d H:i:sP"), new \DateTimeZone($this->timezone));
897
        $dhEvento = $dt->format('Y-m-d\TH:i:sP');
898
        $sSeqEvento = str_pad($nSeqEvento, 2, "0", STR_PAD_LEFT);
899
        $eventId = "ID".$tpEvento.$chave.$sSeqEvento;
900
        $cOrgao = UFList::getCodeByUF($uf);
901
        $request = "<evento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
902
            . "<infEvento Id=\"$eventId\">"
903
            . "<cOrgao>$cOrgao</cOrgao>"
904
            . "<tpAmb>$this->tpAmb</tpAmb>";
905
        if ($this->typePerson === 'J') {
906
            $request .= "<CNPJ>$cnpj</CNPJ>";
907
        } else {
908
            $request .= "<CPF>$cnpj</CPF>";
909
        }
910
        $request .= "<chNFe>$chave</chNFe>"
911
            . "<dhEvento>$dhEvento</dhEvento>"
912
            . "<tpEvento>$tpEvento</tpEvento>"
913
            . "<nSeqEvento>$nSeqEvento</nSeqEvento>"
914
            . "<verEvento>$verEvento</verEvento>"
915
            . "<detEvento versao=\"$verEvento\">"
916
            . "<descEvento>$descEvento</descEvento>"
917
            . "$tagAdic"
918
            . "</detEvento>"
919
            . "</infEvento>"
920
            . "</evento>";
921
        //assinatura dos dados
922
        $request = Signer::sign(
923
            $this->certificate,
924
            $request,
925
            'infEvento',
926
            'Id',
927
            $this->algorithm,
928
            $this->canonical
929
        );
930
        $request = Strings::clearXmlString($request, true);
931
        $lote = $dt->format('YmdHis').rand(0, 9);
932
        $request = "<envEvento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
933
            . "<idLote>$lote</idLote>"
934
            . $request
935
            . "</envEvento>";
936
        if (!empty($eventos[$tpEvento])) {
937
            $evt = $eventos[$tpEvento];
938
            $this->isValid($evt['versao'], $request, $evt['nome']);
939
        } else {
940
            $this->isValid($this->urlVersion, $request, 'envEvento');
941
        }
942
        $this->lastRequest = $request;
943
        //return $request;
944
        $parameters = ['nfeDadosMsg' => $request];
945
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
946
        $this->lastResponse = $this->sendRequest($body, $parameters);
947
        return $this->lastResponse;
948
    }
949
950
    /**
951
     * Request the NFe download already manifested by its recipient, by the key
952
     * using new service in NfeDistribuicaoDFe
953
     * NOTA: NfeDownloadNF is deactivated
954
     * @param  string $chave
955
     * @return string
956
     * @throws InvalidArgumentException
957
     */
958
    public function sefazDownload($chave)
959
    {
960
        if (empty($chave)) {
961
            throw new InvalidArgumentException('Download: chave esta vazia!');
962
        }
963
        //carrega serviço
964
        $servico = 'NfeDistribuicaoDFe';
965
        $this->checkContingencyForWebServices($servico);
966
        $this->servico($servico, 'AN', $this->tpAmb, true);
967
        $cUF = UFList::getCodeByUF($this->config->siglaUF);
968
        $tagChave = "<consChNFe><chNFe>$chave</chNFe></consChNFe>";
969
        $cnpj = $this->config->cnpj;
970
        //monta a consulta
971
        $request = "<distDFeInt xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
972
            . "<tpAmb>".$this->tpAmb."</tpAmb>"
973
            . "<cUFAutor>$cUF</cUFAutor>";
974
        if ($this->typePerson === 'J') {
975
            $request .= "<CNPJ>$cnpj</CNPJ>";
976
        } else {
977
            $request .= "<CPF>$cnpj</CPF>";
978
        }
979
        $request .= "$tagChave"
980
            . "</distDFeInt>";
981
        //valida o xml da requisição
982
        $this->isValid($this->urlVersion, $request, 'distDFeInt');
983
        $this->lastRequest = $request;
984
        //montagem dos dados da mensagem SOAP
985
        $request = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
986
        $parameters = ['nfeDistDFeInteresse' => $request];
987
        $body = "<nfeDistDFeInteresse xmlns=\"$this->urlNamespace\">"
988
            . $request
989
            . "</nfeDistDFeInteresse>";
990
        //este webservice não requer cabeçalho
991
        $this->objHeader = null;
992
        $this->lastResponse = $this->sendRequest($body, $parameters);
993
        return $this->lastResponse;
994
    }
995
996
    /**
997
     * Maintenance of the Taxpayer Security Code - CSC (Old Token)
998
     * @param int $indOp Identificador do tipo de operação:
999
     *                   1 - Consulta CSC Ativos;
1000
     *                   2 - Solicita novo CSC;
1001
     *                   3 - Revoga CSC Ativo
1002
     * @return string
1003
     * @throws InvalidArgumentException
1004
     * @throws RuntimeException
1005
     */
1006
    public function sefazCsc($indOp)
1007
    {
1008
        if (empty($indOp) || $indOp < 1 || $indOp > 3) {
1009
            throw new InvalidArgumentException('CSC: identificador operacao invalido!');
1010
        }
1011
        if ($this->modelo != 65) {
1012
            throw new RuntimeException('CSC: modelo diferente de 65!');
1013
        }
1014
        $raizCNPJ = substr($this->config->cnpj, 0, -6);
1015
        //carrega serviço
1016
        $servico = 'CscNFCe';
1017
        $this->checkContingencyForWebServices($servico);
1018
        $this->servico($servico, $this->config->siglaUF, $this->tpAmb);
1019
        $request = "<admCscNFCe versao=\"$this->urlVersion\" xmlns=\"$this->urlPortal\">"
1020
            . "<tpAmb>$this->tpAmb</tpAmb>"
1021
            . "<indOp>$indOp</indOp>"
1022
            . "<raizCNPJ>$raizCNPJ</raizCNPJ>"
1023
            . "</admCscNFCe>";
1024
        if ($indOp == 3) {
1025
            $request = "<admCscNFCe versao=\"$this->urlVersion\" xmlns=\"$this->urlPortal\">"
1026
            . "<tpAmb>$this->tpAmb</tpAmb>"
1027
            . "<indOp>$indOp</indOp>"
1028
            . "<raizCNPJ>$raizCNPJ</raizCNPJ>"
1029
            . "<dadosCsc>"
1030
            . "<idCsc>".$this->config->CSCid."</idCsc>"
1031
            . "<codigoCsc>".$this->config->CSC."</codigoCsc>"
1032
            . "</dadosCsc>"
1033
            . "</admCscNFCe>";
1034
        }
1035
        //o xsd não está disponivel
1036
        $this->isValid($this->urlVersion, $request, 'admCscNFCe');
1037
        $this->lastRequest = $request;
1038
        $parameters = ['nfeDadosMsg' => $request];
1039
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
1040
        $this->lastResponse = $this->sendRequest($body, $parameters);
1041
        return $this->lastResponse;
1042
    }
1043
1044
    /**
1045
     * Checks the validity of an NFe, normally used for received NFe
1046
     * @param string $nfe
1047
     * @return bool
1048
     * @throws InvalidArgumentException
1049
     */
1050
    public function sefazValidate($nfe)
1051
    {
1052
        if (empty($nfe)) {
1053
            throw new InvalidArgumentException('Validacao NF-e: a string da NF-e esta vazia!');
1054
        }
1055
        //verifica a assinatura da NFe, exception caso de falha
1056
        Signer::isSigned($nfe);
1057
        $dom = new \DOMDocument('1.0', 'utf-8');
1058
        $dom->formatOutput = false;
1059
        $dom->preserveWhiteSpace = false;
1060
        $dom->loadXML($nfe);
1061
        //verifica a validade no webservice da SEFAZ
1062
        $tpAmb = $dom->getElementsByTagName('tpAmb')->item(0)->nodeValue;
1063
        $infNFe  = $dom->getElementsByTagName('infNFe')->item(0);
1064
        $chNFe = preg_replace('/[^0-9]/', '', $infNFe->getAttribute("Id"));
1065
        $protocol = $dom->getElementsByTagName('nProt')->item(0)->nodeValue;
1066
        $digval = $dom->getElementsByTagName('DigestValue')->item(0)->nodeValue;
1067
        //consulta a NFe
1068
        $response = $this->sefazConsultaChave($chNFe, $tpAmb);
1069
        $ret = new \DOMDocument('1.0', 'UTF-8');
1070
        $ret->preserveWhiteSpace = false;
1071
        $ret->formatOutput = false;
1072
        $ret->loadXML($response);
1073
        $retProt = $ret->getElementsByTagName('protNFe')->item(0);
1074
        if (!isset($retProt)) {
1075
            $xMotivo = $ret->getElementsByTagName('xMotivo')->item(0);
1076
            if (isset($xMotivo)) {
1077
                throw new InvalidArgumentException('Validacao NF-e: ' . $xMotivo->nodeValue);
1078
            } else {
1079
                throw new InvalidArgumentException('O documento de resposta nao contem o node "protNFe".');
1080
            }
1081
        }
1082
        $infProt = $ret->getElementsByTagName('infProt')->item(0);
1083
        $dig = $infProt->getElementsByTagName("digVal")->item(0);
1084
        $digProt = '000';
1085
        if (isset($dig)) {
1086
            $digProt = $dig->nodeValue;
1087
        }
1088
        $chProt = $infProt->getElementsByTagName("chNFe")->item(0)->nodeValue;
1089
        $nProt = $infProt->getElementsByTagName("nProt")->item(0)->nodeValue;
1090
        if ($protocol == $nProt && $digval == $digProt && $chNFe == $chProt) {
1091
            return true;
1092
        }
1093
        return false;
1094
    }
1095
1096
    /**
1097
     * Returns alias and description event from event code.
1098
     * @param  int $tpEvento
1099
     * @return \stdClass
1100
     * @throws \RuntimeException
1101
     */
1102
    private function tpEv($tpEvento)
1103
    {
1104
        $std = new \stdClass();
1105
        $std->alias = '';
1106
        $std->desc = '';
1107
        switch ($tpEvento) {
1108
            case self::EVT_CCE:
1109
                $std->alias = 'CCe';
1110
                $std->desc = 'Carta de Correcao';
1111
                break;
1112
            case self::EVT_CANCELA:
1113
                $std->alias = 'CancNFe';
1114
                $std->desc = 'Cancelamento';
1115
                break;
1116
            case self::EVT_CANCELASUBSTITUICAO:
1117
                $std->alias = 'CancNFe';
1118
                $std->desc = 'Cancelamento por substituicao';
1119
                break;
1120
            case self::EVT_EPEC: // Emissão em contingência EPEC
1121
                $std->alias = 'EPEC';
1122
                $std->desc = 'EPEC';
1123
                break;
1124
            case self::EVT_COMPROVANTE_ENTREGA:
1125
                $std->alias = 'CompEntrega';
1126
                $std->desc = 'Comprovante de Entrega';
1127
                break;
1128
            case self::EVT_CANCELAMENTO_COMPROVANTE_ENTREGA:
1129
                $std->alias = 'CancCompEntrega';
1130
                $std->desc = 'Cancelamento do Comprovante de Entrega';
1131
                break;
1132
            case 111500:
1133
            case 111501:
1134
                //EPP
1135
                //Pedido de prorrogação
1136
                $std->alias = 'EPP';
1137
                $std->desc = 'Pedido de Prorrogacao';
1138
                break;
1139
            case 111502:
1140
            case 111503:
1141
                //ECPP
1142
                //Cancelamento do Pedido de prorrogação
1143
                $std->alias = 'ECPP';
1144
                $std->desc = 'Cancelamento de Pedido de Prorrogacao';
1145
                break;
1146
            case self::EVT_CONFIRMACAO: // Manifestação Confirmacao da Operação
1147
                $std->alias = 'EvConfirma';
1148
                $std->desc = 'Confirmacao da Operacao';
1149
                break;
1150
            case self::EVT_CIENCIA: // Manifestação Ciencia da Operação
1151
                $std->alias = 'EvCiencia';
1152
                $std->desc = 'Ciencia da Operacao';
1153
                $std->tpAutor = 2;
1154
                break;
1155
            case self::EVT_DESCONHECIMENTO: // Manifestação Desconhecimento da Operação
1156
                $std->alias = 'EvDesconh';
1157
                $std->desc = 'Desconhecimento da Operacao';
1158
                break;
1159
            case self::EVT_NAO_REALIZADA: // Manifestação Operacao não Realizada
1160
                $std->alias = 'EvNaoRealizada';
1161
                $std->desc = 'Operacao nao Realizada';
1162
                break;
1163
            case self::EVT_ATORINTERESSADO: //ator interessado
1164
                $std->alias = 'EvAtorInteressado';
1165
                $std->desc = 'Ator interessado na NF-e';
1166
                break;
1167
            default:
1168
                $msg = "O código do tipo de evento informado não corresponde a "
1169
                . "nenhum evento estabelecido.";
1170
                throw new RuntimeException($msg);
1171
        }
1172
        return $std;
1173
    }
1174
}
1175