Skip to content

Instantly share code, notes, and snippets.

@luizvaz
Last active February 17, 2024 21:34
Show Gist options
  • Save luizvaz/6c3e2c1e6bc7d310898107c9f95476bb to your computer and use it in GitHub Desktop.
Save luizvaz/6c3e2c1e6bc7d310898107c9f95476bb to your computer and use it in GitHub Desktop.
Assinatura e Envio de XML SMARAPD padrão ABRASF 2.03 (Vila Velha)
<?php
/**
* User: LuizVAz
* Date: 28/02/2021
* Time: 19:46
*/
namespace Provedores\webService;
use DOMDocument;
use DOMXPath;
use Exception;
use RobRichards\XMLSecLibs\XMLSecEnc;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
use stdClass;
class smarapd_v001 extends \Provedores\webService\webService
{
private $cnpj;
private $dir;
private $nfs_dir;
private $rps_dir;
protected $rps_id;
protected $rps_serie;
private $user;
public function __construct(Request $request, Response $response, App $app){
parent::__construct($request, $response, $app);
$ym = date('Ym');
$this->dir = $dir = getenv('BASE_PATH');
$this->user = "your_code";
$this->cnpj = "your_code";
$this->inscricaoMunicipal = "your_code";
$this->rps_dir = "$dir/$cnpj/rps/$ym/";
$this->nfs_dir = "$dir/$cnpj/nfse/$ym/";
$this->rps_id = date('Ymd') . str_pad(time() - strtotime("today"), 5, '0');
$this->rps_serie = "ws1";
}
public function consultar(stdClass $rps){
$this->rps = $rps;
return $this->consultaNFsePorRps();
}
public function consultaNFsePorRps(): stdClass
{
// Retorno
$return = new stdClass();
$return->status = 500;
$return->data = array("success" => false);
// Parametros
$rps_numero = $this->rps_id;
$rps_serie = $this->rps_serie;
$rps_tipo = "1";
$rps_cnpj = $this->cnpj;
$rps_im = $this->inscricaoMunicipal;
$envelope = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:nfse=\"http://nfse.abrasf.org.br\">
<soapenv:Header/>
<soapenv:Body>
<nfse:consultarNfsePorRps>
<xml><![CDATA[<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<ConsultarNfseRpsEnvio xmlns=\"http://www.abrasf.org.br/nfse.xsd\">
<IdentificacaoRps>
<Numero>$rps_numero</Numero>
<Serie>$rps_serie</Serie>
<Tipo>$rps_tipo</Tipo>
</IdentificacaoRps>
<Prestador>
<CpfCnpj>
<Cnpj>$rps_cnpj</Cnpj>
</CpfCnpj>
<InscricaoMunicipal>$rps_im</InscricaoMunicipal>
</Prestador>
</ConsultarNfseRpsEnvio>]]></xml>
</nfse:consultarNfsePorRps>
</soapenv:Body>
</soapenv:Envelope>";
$url = $this->end_prod;
if ($this->rps->ambienteEmissao && (strtolower($this->rps->ambienteEmissao) === "homologacao")){
$url = $this->end_homo;
}
$ret = $this->sendRequest($envelope, "", $url);
$html = $ret['html'];
$httpcode = $ret['code'];
if ($httpcode!=200){
preg_match_all('/<faultstring>(.*?)<\/faultstring>/s', html_entity_decode($html), $matches);
$response = (count($matches)&&count($matches[1])?$matches[1][0]:$html);
$return->data['errors'][] = [
"codigo" => $httpcode,
"mensagem" => $response,
"correcao" => ""
];
return $return;
}
// Extrai Retorno SOAP
preg_match_all('/<return>(.*?)<\/return>/s', html_entity_decode($html), $matches);
$response = (count($matches)&&count($matches[1])?$matches[1][0]:$html);
// Parse
$return->status = $httpcode;
$this->parseRetorno($response, $return);
return $return;
}
public function emitir(stdClass $rps){
$this->rps = $rps;
return $this->gerarNfse();
}
public function gerarNfse(): stdClass
{
//Live Mode
$live = true;
//Retorno
$return = new stdClass();
$return->status = 500;
$return->data = array("success" => false);
$rps_dir = $this->rps_dir;
//rps_id ou idExterno
$rps_id = (property_exists($this->rps, 'idExterno'))?$this->rps->idExterno:$this->rps_id;
if (!$rps_id){
$rps_id = date('Ymd') . str_pad(time() - strtotime("today"), 5, '0');
$this->rps_id = $rps_id;
} else
$this->rps_id = $rps_id;
//Garante que o diretório exista
$fname = "${rps_dir}${rps_id}.xml";
if (!file_exists("${rps_dir}")) {
mkdir("${rps_dir}", 0777, true);
}
$envelope =
"<soapenv:Envelope ".
" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" ".
" xmlns:nfse=\"http://nfse.abrasf.org.br\">".
" <soapenv:Header/>".
" <soapenv:Body>".
" <nfse:recepcionarLoteRpsSincrono>".
" <xml><![CDATA[%DadosMsg%]]></xml>".
" </nfse:recepcionarLoteRpsSincrono>".
" </soapenv:Body>".
"</soapenv:Envelope>";
//Ambiente: 1 = Produção, 2 = Homologação
$amb = '1';
if ($this->rps->ambienteEmissao && (strtolower($this->rps->ambienteEmissao) === "homologacao")){
$amb = '2';
}
if ($live) {
//xml
$xml = $this->rpsAsXML();
/* $xml = str_ireplace('<?xml version="1.0" encoding="utf-8"?>', '', $xml); */
file_put_contents("$fname", $xml);
//$cab = htmlentities($cab, ENT_XML1 | ENT_NOQUOTES);
//$xml = htmlentities($xml, ENT_XML1 | ENT_NOQUOTES);
//$envelope = str_replace('%CabMsg%', $cab, $envelope);
$envelope = str_replace('%DadosMsg%', $xml, $envelope);
file_put_contents("${rps_dir}${rps_id}.soap", $envelope);
}
if ($live) {
//Live Mode
$url = $this->end_prod;
if ($this->rps->ambienteEmissao && (strtolower($this->rps->ambienteEmissao) === "homologacao")){
$url = $this->end_homo;
}
$ret = $this->sendRequest($envelope, "", $url);
$html = $ret["html"];
// Converte de UTF-8 para ISO-8859
//$html = utf8_decode(curl_exec($ch));
$httpcode = $ret["code"];
$return->status = $httpcode;
file_put_contents("$fname.ret", $html);
$html = utf8_decode($html); //Converter Depois de Salvo
} else {
//Test Mode
$httpcode = 200;
$return->status = $httpcode;
$html = file_get_contents("$fname.ret");
}
if ($httpcode!=200){
preg_match_all('/<faultstring>(.*?)<\/faultstring>/s', html_entity_decode($html), $matches);
$response = (count($matches)&&count($matches[1])?$matches[1][0]:$html);
$return->data['errors'][] = [
"codigo" => $httpcode,
"mensagem" => $response,
"correcao" => ""
];
return $return;
}
//Extrai
preg_match_all('/<return>(.*?)<\/return>/s', html_entity_decode($html), $matches);
$response = (count($matches)&&count($matches[1])?$matches[1][0]:$html);
$return = $this->parseRetorno($response, $return);
return $return;
}
public function parseRetorno($response, $return): stdClass
{
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
//Verifica falha ao processar XML de RETORNO
if (@$dom->loadXML($response) === false){
//Retorno
$return->data['errors'][] = [
"codigo" => "500",
"mensagem" => "$response",
"correcao" => "Contate o suporte!"
];
return $return;
}
$xp = new DOMXPath($dom);
$xp->registerNamespace('fake', "http://nfse.abrasf.org.br");
// /retornoNfseLote/errors/erro
$nodes = $xp->query("/*/*/*[name()='MensagemRetorno']");
if ($nodes->length != 0){
if (!array_key_exists('errors', $return->data))
$return->data['errors'] = [];
for ($i = 0; $i<$nodes->length; $i++){
$msg = $nodes->item($i);
//Tags
$cod = $msg->getElementsByTagName("Codigo");
if ($cod->length>0) {
$cod = $cod->item(0);
$cod = $cod->nodeValue;
if (strlen($cod)>5) $cod = "E00"; //Texto redundante
} else
$cod = "E00";
$txt = $msg->getElementsByTagName("Mensagem");
if ($txt->length>0) {
$txt = $txt->item(0);
$txt = $txt->nodeValue;
} else
$txt = "";
$cor = $msg->getElementsByTagName("Correcao");
if ($cor->length>0) {
$cor = $cor->item(0);
$cor = $cor->nodeValue;
} else
$cor = "";
//Retorno
$return->data['errors'][] = [
"codigo" => $cod,
"mensagem" => $txt,
"correcao" => $cor
];
}
return $return;
}
// Resposta EnviarLoteRpsSincrono
$nodes = $xp->query("/*[name()='EnviarLoteRpsSincronoResposta']");
if ($nodes->length != 0){
$nfseId = "";
$msg = $nodes->item(0);
$proto = $msg->getElementsByTagName("Protocolo");
$proto = ($proto->length>0)?$proto = $proto->item(0)->nodeValue:"0";
$return->data['protocolo'] = $proto;
$lote = $msg->getElementsByTagName("NumeroLote");
$lote = ($lote->length>0)?$lote = $lote->item(0)->nodeValue:"0";
$return->data['numeroLote'] = $lote;
// Consulta por Lote não funciona
// Precisa pegar pelo RPS
$return = $this->consultaNFsePorRps();
// Pussui Erros
if (array_key_exists('errors', $return->data)){
return $return;
}
// Salva Retorno
$num = $return->data["numero"];
$nfs_dir = $this->nfs_dir;
$fname = "${nfs_dir}${num}.xml";
if (!file_exists(dirname("$fname"))) {
mkdir(dirname("$fname"), 0777, true);
}
file_put_contents($fname, $msg->ownerDocument->saveXML());
$return->data['success'] = true;
}
// Possui Nota
$nodes = $xp->query("//CompNfse");
if ($nodes->length != 0) {
$nfseId = "";
$msg = $nodes->item(0);
$id = $msg->getElementsByTagName("InfNfse");
$id = ($id->length && ($id = $id->item(0)->getAttribute("Id")))?$id:$this->rps_id;
$num = $msg->getElementsByTagName("Numero");
$num = ($num->length)?$num->item(0)->nodeValue:$this->rps_id;
$cod = $msg->getElementsByTagName("CodigoVerificacao");
$cod = ($cod->length)?$cod->item(0)->nodeValue:"N/D";
$dat = $msg->getElementsByTagName("DataEmissao");
$dat = ($dat->length)?$dat->item(0)->nodeValue:"";
$dat = str_ireplace(' ', 'T', $dat);
$return->data['nfseId'] = $id;
$return->data['numero'] = $num;
$return->data['codigoVerificacao'] = $cod;
$return->data['dataEmissao'] = $dat;
$return->data['success'] = true;
}
return $return;
}
public function rpsAsXML()
{
//DOM
$dom = new DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
$dom->preserveWhiteSpace = true;
$dom->appendChild($enviarLoteRpsSincronoEnvio = $dom->createElement("EnviarLoteRpsSincronoEnvio"));
$enviarLoteRpsSincronoEnvio->setAttribute("xmlns", "http://www.abrasf.org.br/nfse.xsd");
// LoteRps
$enviarLoteRpsSincronoEnvio->appendChild($loteRps = $dom->createElement("LoteRps"));
$loteId = date('Ymd') . str_pad(time() - strtotime("today"), 5, '0');
$loteRps->setAttribute("versao", "2.03");
$loteRps->setAttribute("Id", "$loteId");
$loteRps->appendChild( $dom->createElement("NumeroLote", "$loteId"));
$loteRps->appendChild( $cpfCnpj = $dom->createElement("CpfCnpj"));
$cpfCnpj->appendChild( $dom->createElement("Cnpj", $this->conf->cnpj));
$loteRps->appendChild( $dom->createElement("InscricaoMunicipal", $this->conf->inscricaoMunicipal));
$loteRps->appendChild( $dom->createElement("QuantidadeRps", "1"));
// ListaRps
$loteRps->appendChild( $listaRps = $dom->createElement("ListaRps"));
//Rps
$listaRps->appendChild( $rps1 = $dom->createElement("Rps"));
//InfDeclaracaoPrestacaoServico
$rps1->appendChild( $infDec = $dom->createElement("InfDeclaracaoPrestacaoServico"));
$infDec->setAttribute("Id", "A".$this->rps_id);
//Rps
$infDec->appendChild( $rps2 = $dom->createElement("Rps"));
$rps2->setAttribute("Id", "B".$this->rps_id);
//IdentificacaoRps
$rps2->appendChild( $identificacaoRps = $dom->createElement("IdentificacaoRps"));
$identificacaoRps->appendChild($dom->createElement("Numero",$this->rps_id));
$identificacaoRps->appendChild($dom->createElement("Serie",$this->rps_serie));
$identificacaoRps->appendChild($dom->createElement("Tipo","1"));
//Formato yyyy-mm-dd
$dataEmissao = date("Y-m-d");
if ($this->rps->dataEmissao){
$dataEmissao = new \DateTime($this->rps->dataEmissao);
$dataEmissao = $dataEmissao->format('Y-m-d');
}
//DataEmissao
$rps2->appendChild($dom->createElement("DataEmissao",$dataEmissao));
//Status
$rps2->appendChild($dom->createElement("Status","1"));
//Competencia
$infDec->appendChild($dom->createElement("Competencia",$dataEmissao));
//Servico
$servico = $dom->createElement("Servico");
//Valores
$valores = $dom->createElement("Valores");
$valorTotal = 0.00;
$valor = (property_exists($this->rps->servico, 'valorServicos')?0.00+$this->rps->servico->valorServicos:0);
if (!$valor)
$valor = (property_exists($this->rps, 'valorTotal')?0.00+$this->rps->valorTotal:0);
if ($valor == 0)
throw new Exception("Informação servico->valorServicos não informada.");
$valores->appendChild(
$dom->createElement("ValorServicos", $this->formatValor($valor))
);
$valorTotal += $valor;
$valor = (property_exists($this->rps->servico, 'valorDeducoes')?0.00+$this->rps->servico->valorDeducoes:0);
if ($valor)
$valores->appendChild(
$dom->createElement("ValorDeducoes", $this->formatValor($valor))
);
$valorTotal -= $valor;
$aliquotaIss = 0;
if (property_exists($this->rps->servico, 'aliquotaIss'))
$aliquotaIss = 0.00+$this->rps->servico->aliquotaIss;
if ($aliquotaIss == 0)
if (property_exists($this->rps->servico, 'aliquota'))
$aliquotaIss = 0.00+$this->rps->servico->aliquota;
if ($aliquotaIss == 0)
throw new Exception("Informação aliquotaIss não encontrada.");
$valorIss = round($valorTotal * $aliquotaIss, 2, PHP_ROUND_HALF_EVEN);
$valores->appendChild($dom->createElement("ValorIss", $this->formatValor($valorIss*1)));
$valores->appendChild($dom->createElement("Aliquota", $this->formatValor($aliquotaIss*100)));
$servico->appendChild($valores);
//Servico
//issRetido
$issRetido = (property_exists($this->rps, 'issRetidoFonte')?$this->rps->issRetidoFonte:false);
$servico->appendChild($dom->createElement("IssRetido", $issRetido?'1':'2'));
//itemListaServicoLC116
$itemListaServico = (property_exists($this->rps, 'itemListaServicoLC116')?$this->rps->itemListaServicoLC116:false);
if (!$itemListaServico)
$itemListaServico = (property_exists($this->rps->servico, 'itemListaServico')?$this->rps->servico->itemListaServico:false);
if (!$itemListaServico)
$itemListaServico = $this->conf->itemListaServicoLC116;
$servico->appendChild($dom->createElement("ItemListaServico", $itemListaServico));
//cnae
/*
$cnae = (property_exists($this->rps, 'cnae')?$this->rps->cnae:false);
if (!$cnae)
$cnae = $this->conf->cnae;
$servico->appendChild($dom->createElement("CodigoCnae", $cnae));
*/
//codigoTributacaoMunicipio
$codigoTributacaoMunicipio = (property_exists($this->rps->servico, 'codigoTributacaoMunicipio')?$this->rps->servico->codigoTributacaoMunicipio:false);
if ($codigoTributacaoMunicipio)
$servico->appendChild($dom->createElement("CodigoTributacaoMunicipio", $codigoTributacaoMunicipio));
//descricao
$descricao = (property_exists($this->rps->servico, 'discriminacao')?$this->rps->servico->discriminacao:"");
if (!$descricao)
$descricao = (property_exists($this->rps->servico, 'descricao')?$this->rps->servico->descricao:"");
if (!$descricao)
$descricao = $this->conf->descricaoServico;
$observacao = (property_exists($this->rps->servico, 'observacao')?$this->rps->servico->observacao:"");
$descricao = ($descricao) ? "$descricao\n$observacao" : $observacao;
if ($descricao) {
// Essa limpeza será feita logo abaixo
//$descricao = str_replace(array("\r\n", "\r", "\n", "\t"), "", $descricao);
//$descricao = $this->remove_accents($descricao);
$servico->appendChild($dom->createElement("Discriminacao", $descricao));
}
//codigoMunicipio
$codigoMunicipio = (property_exists($this->rps->servico, 'codigoMunicipio')?$this->rps->servico->codigoMunicipio:0);
if (!$codigoMunicipio)
$codigoMunicipio = $this->conf->endereco->codigoIbgeCidade;
$servico->appendChild($dom->createElement("CodigoMunicipio", $codigoMunicipio));
//exigibilidadeISS
$servico->appendChild($dom->createElement("ExigibilidadeISS", "1"));
//municipioIncidencia
$municipioIncidencia = (property_exists($this->rps->servico, 'municipioPrestacaoServico')?$this->rps->servico->municipioPrestacaoServico:0);
if (!$municipioIncidencia)
$municipioIncidencia = $codigoMunicipio;
$servico->appendChild($dom->createElement("MunicipioIncidencia", $municipioIncidencia));
$infDec->appendChild($servico);
//prestador
$infDec->appendChild($prestador = $dom->createElement("Prestador"));
$cpfCnpj = $dom->createElement("CpfCnpj");
$cnpj = $dom->createElement("Cnpj", $this->conf->cnpj);
$cpfCnpj->appendChild($cnpj);
$prestador->appendChild($cpfCnpj);
$im = $dom->createElement("InscricaoMunicipal", $this->conf->inscricaoMunicipal);
$prestador->appendChild($im);
//Tomador
$infDec->appendChild($tomador = $dom->createElement("Tomador"));
$identificacaoTomador = $dom->createElement("IdentificacaoTomador");
$cpfCnpj = $dom->createElement("CpfCnpj");
$doc = null;
$temp = (property_exists($this->rps->cliente, 'cpfCnpj')?$this->rps->cliente->cpfCnpj:"");
if (strlen($temp)==14)
$doc = $dom->createElement("Cnpj", $this->rps->cliente->cpfCnpj);
elseif (strlen($temp)==11)
$doc = $dom->createElement("Cpf", $this->rps->cliente->cpfCnpj);
if ($doc){
$cpfCnpj->appendChild($doc);
$identificacaoTomador->appendChild($cpfCnpj);
}
if (property_exists($this->rps->cliente, 'inscricaoMunicipal')){
$doc = $dom->createElement('InscricaoMunicipal', $this->rps->cliente->inscricaoMunicipal);
$identificacaoTomador->appendChild($doc);
}
$tomador->appendChild($identificacaoTomador);
$temp = (property_exists($this->rps->cliente, 'nome')?$this->rps->cliente->nome:"");
if (!$temp)
$temp = (property_exists($this->rps->cliente, 'razaoSocial')?$this->rps->cliente->razaoSocial:"");
if ($temp)
$tomador->appendChild($dom->createElement("RazaoSocial", $temp));
//Endereço
$endereco = $dom->createElement("Endereco");
$temp = (property_exists($this->rps->cliente->endereco, 'logradouro')?$this->rps->cliente->endereco->logradouro:"");
if ($temp)
$endereco->appendChild($dom->createElement("Endereco", $temp));
$temp = (property_exists($this->rps->cliente->endereco, 'numero')?$this->rps->cliente->endereco->numero:"");
if ($temp)
$endereco->appendChild($dom->createElement("Numero", $temp));
$temp = (property_exists($this->rps->cliente->endereco, 'bairro')?$this->rps->cliente->endereco->bairro:"");
if ($temp)
$endereco->appendChild($dom->createElement("Bairro", $temp));
$temp = (property_exists($this->rps->cliente->endereco, 'cidade')?$this->rps->cliente->endereco->cidade:"");
if (!$temp)
$temp = (property_exists($this->rps->cliente->endereco, 'codigoMunicipio')?$this->rps->cliente->endereco->codigoMunicipio:"");
if ($temp)
$endereco->appendChild($dom->createElement("CodigoMunicipio", $temp));
$temp = (property_exists($this->rps->cliente->endereco, 'UF')?$this->rps->cliente->endereco->UF:"");
if ($temp)
$endereco->appendChild($dom->createElement("Uf", $temp));
$temp = (property_exists($this->rps->cliente->endereco, 'CEP')?$this->rps->cliente->endereco->CEP:"");
if ($temp)
$endereco->appendChild($dom->createElement("Cep", $temp));
if ($endereco->childNodes->length > 0)
$tomador->appendChild($endereco);
//Contato
$contato = $dom->createElement("Contato");
$temp = (property_exists($this->rps->cliente, 'telefone')?$this->rps->cliente->telefone:"");
if ($temp)
$contato->appendChild($dom->createElement("Telefone", $temp));
$temp = (property_exists($this->rps->cliente, 'email')?$this->rps->cliente->email:"");
if ($temp)
$contato->appendChild($dom->createElement("Email", $temp));
if ($tomador->childNodes->length > 0)
$tomador->appendChild($contato);
//RegimeEspecialTributacao
if ($this->conf->regimeEspecialTributacao)
$infDec->appendChild($dom->createElement("RegimeEspecialTributacao", $this->conf->regimeEspecialTributacao));
//OptanteSimplesNacional
$infDec->appendChild($dom->createElement("OptanteSimplesNacional", $this->conf->optanteSimplesNacional?'1':'2'));
$infDec->appendChild($dom->createElement("IncentivoFiscal", $this->conf->incentivadorCultural?'1':'2'));
//Retorna o RPS em formato XML
// limpeza do xml com a retirada dos TAB e Acentos
$decl = str_replace(array("\t"), '', $dom->saveXML());
$decl = $this->remove_accents($decl);
// Não remover a formatação depois de assinado
$decl = $this->assinaXML($decl, 'InfDeclaracaoPrestacaoServico');
$decl = str_ireplace('<?xml version="1.0" encoding="UTF-8"?>', '', $decl);
$lote = $this->assinaXML($decl, 'LoteRps');
return $lote;
}
function assinaXML($docxml, $tag){
try {
$tagid = $tag;
$crt_dir = dirname(dirname($this->rps_dir)) . '/cert';
$priKEY = "$crt_dir/pkey.pem";
$keypass = file_get_contents("$crt_dir/pass.txt");
$pubKEY = "$crt_dir/cert.pem";
// Habilita a manipulação de erros da libxml
libxml_use_internal_errors(true);
//limpar erros anteriores que possam estar em memória
libxml_clear_errors();
// carrega o documento no DOM
$xmldoc = new DOMDocument('1.0', 'UTF-8');
$xmldoc->preservWhiteSpace = true; //elimina espaços em branco
$xmldoc->formatOutput = false;
// muito importante deixar ativadas as opções para limpar os espacos em branco
// e as tags vazias
if (!$xmldoc->loadXML($docxml, LIBXML_NOBLANKS | LIBXML_NOXMLDECL | LIBXML_BIGLINES)) {
$msg = "Erro ao carregar XML, provavel erro no proprio xml!!";
$errors = libxml_get_errors();
if (!empty($errors)) {
$i = 1;
foreach ($errors as $error) {
$msg .= "\n [$i]-" . trim($error->message);
}
libxml_clear_errors();
}
throw new Exception($msg);
}
//extrair a tag com os dados a serem assinados
$xpath = new DOMXPath($xmldoc);
$xpath->registerNamespace('ns', "http://www.abrasf.org.br/nfse.xsd");
$node = $xpath->query("//ns:{$tagid}")->item(0);
if (!isset($node)) {
$msg = "A tag <$tagid> não existe no XML!!";
throw new Exception($msg);
}
// Cria novo Security object
$objDSig = new XMLSecurityDSig('');
// Usa a c14n exclusive canonicalization
$objDSig->setCanonicalMethod(XMLSecurityDSig::C14N);
// Assina usando SHA-1
$objDSig->addReference(
$node,
XMLSecurityDSig::SHA1,
[
"http://www.w3.org/2000/09/xmldsig#enveloped-signature",
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
],
[
'id_name' => 'Id',
'overwrite' => false
]
);
// Cria nova (private) Security key
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
$objKey->passphrase = "$keypass";
$objKey->loadKey("$priKEY", TRUE);
// Assina o XML
$objDSig->sign($objKey);
// Adiciona a public key para a signature
$objDSig->add509Cert(file_get_contents("$pubKEY"));
// Acrescenta a signature para o XML
$objDSig->appendSignature($node->parentNode);
$objDSig = null;
//grava na string o objeto DOM
$xml = $xmldoc->saveXML();
} catch (Exception $e) {
throw $e;
}
//retorna o documento assinado
return $xml;
} //fim signXML
function remove_accents($string) {
if ( !preg_match('/[\x80-\xff]/', $string) )
return $string;
$chars = array(
// Decompositions for Latin-1 Supplement
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
chr(195).chr(191) => 'y',
// Decompositions for Latin Extended-A
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's'
);
$string = strtr($string, $chars);
return $string;
}
public function sendRequest(string $envelope, string $action, string $url): array
{
$headers = array(
"Content-type: text/xml;charset=UTF-8",
"Accept-Encoding: gzip,deflate",
"SOAPAction: \"$action\"",
"Content-length: " . strlen($envelope),
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $envelope);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_ENCODING, "identity, deflate, gzip");
//A prefeitura bloqueia os IPs externos
//$proxy = '200.255.122.174:8080';
//curl_setopt($ch, CURLOPT_PROXY, $proxy);
//curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$html = curl_exec($ch);
// Converte de UTF-8 para ISO-8859
//$html = utf8_decode(curl_exec($ch));
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
"html" => $html,
"code" => $httpcode
];
}
}
@robmachado
Copy link

Luiz;
O envio sincrono foi obtido com sucesso, mas nenhuma consulta em modo de homologação retorna os documentos.
Essas consultas não localizam NADA !! e sempre retornam documento não localizado.
Está assim também em produção ??

Agradeço muito sua resposta meu amigo !

@luizvaz
Copy link
Author

luizvaz commented Mar 24, 2021

Em produção está OK.
Em homologação não adianda esperar muita coisa

@JoaoMarcos01
Copy link

Olá, Luiz tudo bem!
Estou integrando em minha aplicação Mogi das Cruzes o padão é o mesmo, como deve proceder com seu código.

Obrigado!

@luizvaz
Copy link
Author

luizvaz commented Sep 27, 2021

É só alterar rpsAsXML() de acordo com a sua necessidade, colocando as tags que não estão presentes que você precise.
Pode retirar o extends \Provedores\webService\webService.

Basta chamar o gerarNfse() para gerar uma nova nota.

Note que eu uso alguns valores de uma propriedade rps, basta preencher essas propriedades usando um objeto stdClass.

@JoaoMarcos01
Copy link

Obrigado Luiz.!!

Abraço

@WallisonBreno
Copy link

Cara, você pode me ajudar com um projeto em que estou trabalhando? Por favor

@luizvaz
Copy link
Author

luizvaz commented Sep 19, 2022

@giovanithome
Copy link

giovanithome commented Jul 13, 2023

Oi.
Preciso emitir notas pelo município de Viamão/RS, que usa o padrão ABRASF.
Baixei o teu código, fiz umas atualizações, porque o PHP não aceitou algumas coisas, removi o extends, mas parei em um erro:
Fatal error: Cannot use "parent" when current class scope has no parent in D:\Dropbox\www\nfse\smarapd_203.php on line 32
Tem algo que possa ser feito para resolver isso?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment