No artigo de hoje, vou mostrar para vocês como fazer a integração com a API dos Correios.
Pessoal, os exemplos apresentados aqui foram disponibilizados com a ajuda do grande Fernando Sauer ( LinkedIn ).
Update Dezembro de 2023:
Pessoal, o Correios atualizou a API deles, agora é paga e é via REST. Em todo o caso, abaixo segue o artigo original e o novo revisado com REST.
Novo exemplo (REST)
Para usar dessa nova forma, a empresa deve contratar dos Correios a integração, e dessa forma usar as informações de usuário, senha, contrato, entre outras na integração.
A documentação das APIs dos Correios, estão disponíveis nesse link https://cws.correios.com.br .
Abaixo um exemplo, de uma função que calcula o preço, e uma que busca o token com os Correios:
User Function zTstPrc(cTipo, cCepOri, cCepDes, nPeso) Local cCodCon := SuperGetMv("MV_X_CONTC",.F.,'XXXXXXXXXX') //Código do Contrato Local cCodDR := SuperGetMv("MV_X_CODRC",.F.,'XX') //Código da DR do Contrato Local nPrcSimu := 0 //Valor simulado Local cUrl := "https://api.correios.com.br" Local cClasse := "/preco/v1/nacional" Local cMsg := "" Local cError := "" Local oRestClient Local cBody := "" Local aHeadOut := {} Local cToken := fGetToken() Local jJson Default cTipo := "" Default cCepOri := "" Default cCepDes := "" Default nPeso := 0 //Define o header da requisição aAdd(aHeadOut, "Content-Type: application/json; charset=UTF-8") aAdd(aHeadOut, "Accept: application/json") aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")") aAdd(aHeadOut, "Authorization: Bearer " + AllTrim(cToken)) //Monta o corpo da requisição cBody+= '{ ' + CRLF cBody+= '"idLote":"1", ' + CRLF cBody+= '"parametrosProduto":[ ' + CRLF cBody+= ' { ' + CRLF cBody+= ' "coProduto":"'+ AllTrim(cTipo) + '", ' + CRLF cBody+= ' "nuRequisicao":"1", ' + CRLF cBody+= ' "nuContrato":"'+AllTrim(cCodCon)+'", ' + CRLF cBody+= ' "nuDR":'+cCodDR+', ' + CRLF cBody+= ' "cepOrigem":"'+AllTrim(cCepOri)+'", ' + CRLF cBody+= ' "psObjeto":"'+cValToChar(nPeso)+'", ' + CRLF cBody+= ' "tpObjeto":"1", ' + CRLF cBody+= ' "cepDestino":"'+AllTrim(cCepDes)+'" ' + CRLF cBody+= ' } ' + CRLF cBody+= '] ' + CRLF cBody+= '} ' + CRLF //Cria a conexão com os Correios oRestClient:= FWRest():New(cUrl) oRestClient:setPath(cClasse) oRestClient:SetPostParams(cBody) // Efetua o Comando Post, no Host e Path informados anteriormente. If oRestClient:Post(aHeadOut) //Pega o resultado e transforma em um objeto JSON cMsg := oRestClient:GetResult() jJson := JsonObject():New() cError:= jJson:FromJson(cMsg) //Se não teve erro, pega o preço calculado If Empty(cError) nPrcSimu := jJson[1]:GetJsonText("pcFinal") nPrcSimu := Val(StrTran(nPrcSimu,",",".")) Alert("O preço será de " + cValToChar(nPrcSimu)) EndIf EndIf Return Static Function fGetToken() Local cToken := "" Local cUsrCor := AllTrim(SuperGetMv("MV_X_AUTCO",.F.,'teste')) // Usuário Local cPswCor := AllTrim(SuperGetMv("MV_X_PSWCO",.F.,'teste')) // Senha Local cCarPost := AllTrim(SuperGetMv("MV_X_POSTC",.F.,'YYYYYYYYYY')) // Cartão de Postagem Local cUrl := "https://api.correios.com.br" Local cClasse := "/token/v1/autentica/cartaopostagem" Local cMsg := "" Local cError := "" Local oRestClient Local cBody := "" Local aHeadOut := {} Local jJson //Define o Header da requisição aAdd(aHeadOut, "Content-Type: application/json; charset=UTF-8") aAdd(aHeadOut, "Accept: application/json") aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")") aAdd(aHeadOut,"Authorization: Basic " + Encode64(cUsrCor+":"+cPswCor)) //Body com Cartão de Postagem cBody += '{' + CRLF cBody += '"numero": "'+AllTrim(cCarPost)+'"' + CRLF cBody += '}' + CRLF //Cria a conexão com os Correios oRestClient := FWRest():New(cUrl) oRestClient:SetPath(cClasse) oRestClient:SetPostParams(cBody) //Efetua o Post If oRestClient:Post(aHeadOut) //Pega o resultado e transforma em um JSON cMsg := oRestClient:GetResult() jJson := JsonObject():New() cError := jJson:FromJson(cMsg) //Se não teve erro, pega o token If Empty(cError) cToken := jJson:GetJsonText("token") EndIf EndIf Return cToken
Artigo original (SOAP)
No caso, essa customização basicamente se comunica com o WSDL dos correios (documentação disponível em https://www.correios.com.br/atendimento/ferramentas/sistemas/arquivos/web-service-de-rastreamento ).
Depois de fazer essa comunicação, é buscado o resultado em XML, e então conforme as posições do texto é definido um retorno em array.
Para utilizar, é necessário ter um usuário e senha com acesso ao SRO XML dos Correios. No caso, para armazenar essas informações, crie os parâmetros MV_X_CORSE (Correios Senha) e MV_X_CORUS (Correios Usuário).
Abaixo o código fonte desenvolvido:
//Bibliotecas #Include "TOTVS.ch" /*/{Protheus.doc} User Function zCorreios Função que realiza a consulta de um rastreio direto nos correios e monta um array com o resultado @author Fernando Sauer @since 01/02/2021 @version 1.0 @type function @return aInfRast, Array, Retorna um array com as informações do rastreio @example aDados := u_zCorreios("XXAA01010101") @obs Documentação da API: https://www.correios.com.br/atendimento/ferramentas/sistemas/arquivos/web-service-de-rastreamento Exemplo documentado e adaptado por Daniel Atilio Abaixo as posições do Array de retorno: aInfRast[01] --> Data de atualização da informação nos Correios aInfRast[02] --> Hora de atualização da informação nos Correios aInfRast[03] --> Descrição do status do rastreamento aInfRast[04] --> Local do código de Rastreamento aInfRast[05] --> Código de Rastreamento aInfRast[06] --> Município do Rastreamento aInfRast[07] --> Estado do Objeto de Rastreamento aInfRast[08] --> Endereço de retirada /*/ User Function zCorreios(cRastreio) Local aArea := GetArea() Local oWsdl Local xRet Local cError := "" Local cWarning := "" Local cResp := "" Local lContinua := .T. //Retorno Último rastreio Local nPosIni := 0 Local nPosFim := 0 Local nLeitura := 0 Local dDtRast := "" Local cHrRast := "" Local cDesRast := "" Local cLcRast := "" Local cCodRast := "" Local cMunRast := "" Local cEstRast := "" Local cEndereco := "" Local aInfRast := Array(8) Local cLograd := "" //Logradouro Local cNumero := "" //Número Local cLocalid := "" //Localidade //Usuário e senha dos Correios - "Habilitar Acesso ao SRO XML [Solicitação]" Local cPWDCo := SuperGetMv("MV_X_CORSE", .F., 'XXXXXXXX') Local cUsuCo := SuperGetMv("MV_X_CORUS", .F., 'YYYYYYYY') Default cRastreio := "" //Código precisa estar preenchido If ! Empty(cRastreio) MsgInfo("Código de rastreio em branco") Else //Faz a comunicação com a API oWsdl := TWsdlManager():New() oWsdl:lSSLInsecure := .T. If xRet := ! oWsdl:ParseURL( "https://webservice.correios.com.br/service/rastro/Rastro.wsdl" ) MsgInfo( "Erro Parse: " + oWsdl:cError ) lContinua := .F. EndIf //Define a operação como buscaEventos If lContinua .And. xRet := !oWsdl:SetOperation( "buscaEventos" ) MsgInfo( "Erro SetOperation: " + oWsdl:cError ) lContinua := .F. EndIf //Se não houve erros If lContinua cMsgWs := '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:res="http://resource.webservice.correios.com.br/"> cMsgWs += '<soapenv:Header/>' cMsgWs += '<soapenv:Body>' cMsgWs += '<res:buscaEventos> cMsgWs += '<usuario>' + cUsuCo + '</usuario> cMsgWs += '<senha>' + cPWDCo + '</senha> cMsgWs += '<tipo>L</tipo> cMsgWs += '<resultado>U</resultado> cMsgWs += '<lingua>101</lingua> cMsgWs += '<objetos>'+ AllTrim(cRastreio) + '</objetos> cMsgWs += '</res:buscaEventos> cMsgWs += '</soapenv:Body> cMsgWs += '</soapenv:Envelope> //Busca o resultado da consulta xRet := oWsdl:SendSoapMsg(cMsgWs) xRet := oWsdl:SendSoapMsg(cMsgWs) cResp := oWsdl:GetParsedResponse() cRespost := oWsdl:GetSoapResponse() oXml := XmlParser( cRespost, "_", @cError, @cWarning ) //Data de atualização da informação nos Correios nPosIni := At("<data>", cRespost) + 6 nPosFim := At("</data>", cRespost) nLeitura := nPosFim - nPosIni dDtRast := SubStr(cRespost, nPosIni, nLeitura) //Hora de atualização da informação nos Correios nPosIni := At("<hora>", cRespost) + 6 nPosFim := At("</hora>", cRespost) nLeitura := nPosFim - nPosIni cHrRast := SubStr(cRespost, nPosIni, nLeitura) //Descrição do status do rastreamento nPosIni := At("<descricao>", cRespost) + 11 nPosFim := At("</descricao>", cRespost) nLeitura := nPosFim - nPosIni cDesRast := SubStr(cRespost, nPosIni, nLeitura) cDesRast := DecodeUTF8(cDesRast, "cp1252") //Local do código de Rastreamento nPosIni := At("<local>", cRespost) + 7 nPosFim := At("</local>", cRespost) nLeitura := nPosFim - nPosIni cLcRast := SubStr(cRespost, nPosIni, nLeitura) cLcRast := DecodeUTF8(cLcRast, "cp1252") //Código de Rastreamento nPosIni := At("<numero>", cRespost) + 8 nPosFim := At("</numero>", cRespost) nLeitura := nPosFim - nPosIni cCodRast := SubStr(cRespost, nPosIni, nLeitura) cCodRast := DecodeUTF8(cCodRast, "cp1252") //Município do Rastreamento nPosIni := At("<cidade>", cRespost) + 8 nPosFim := At("</cidade>", cRespost) nLeitura := nPosFim - nPosIni cMunRast := SubStr(cRespost, nPosIni, nLeitura) cMunRast := DecodeUTF8(cMunRast, "cp1252") //Estado do Objeto de Rastreamento nPosIni := At("<uf>", cRespost) + 4 nPosFim := At("</uf>", cRespost) nLeitura := nPosFim - nPosIni cEstRast := SubStr(cRespost, nPosIni, nLeitura) cEstRast := DecodeUTF8(cEstRast, "cp1252") //Endereço de retirada If "<endereco>" $ cRespost nPosIni := At("<endereco>", cRespost) + 10 nPosFim := At("</endereco>", cRespost) nLeitura := nPosFim - nPosIni cEndereco := SubStr(cRespost, nPosIni, nLeitura) cEndereco := DecodeUTF8(cEndereco, "cp1252") nPosIni := At("<logradouro>", cEndereco) + 12 nPosFim := At("</logradouro>", cEndereco) nLeitura := nPosFim - nPosIni cLograd := SubStr(cEndereco, nPosIni, nLeitura) cLograd := DecodeUTF8(cLograd, "cp1252") nPosIni := At("<numero>", cEndereco) + 8 nPosFim := At("</numero>", cEndereco) nLeitura := nPosFim - nPosIni cNumero := SubStr(cEndereco, nPosIni, nLeitura) cNumero := DecodeUTF8(cNumero, "cp1252") nPosIni := At("<localidade>", cEndereco) + 12 nPosFim := At("</localidade>", cEndereco) nLeitura := nPosFim - nPosIni cLocalid := SubStr(cEndereco, nPosIni, nLeitura) cLocalid := DecodeUTF8(cLocalid, "cp1252") cEndereco := cLograd + ", " + cNumero + " " + cLocalid Else cEndereco := "" EndIf //Define o retorno aInfRast[01] := dDtRast aInfRast[02] := cHrRast aInfRast[03] := cDesRast aInfRast[04] := cLcRast aInfRast[05] := cCodRast aInfRast[06] := Capital(cMunRast) aInfRast[07] := cEstRast aInfRast[08] := Capital(cEndereco) EndIf EndIf RestArea(aArea) Return aInfRast
Bom pessoal, por hoje é só.
Abraços e até a próxima.
Estou desenvolvendo um programa quase igual ao seu para os correios.
// Cria o objeto da classe TWsdlManager
oWsdl := TWsdlManager():New()
oWsdl:lSSLInsecure := .T.
xRet := oWsdl:ParseURL(“https://apphom.correios.com.br/SigepMasterJPA/AtendeClienteService/AtendeCliente?wsdl”)
If xRet == .F.
Alert( “Erro 0: ” + oWsdl:cError )
Return
EndIf
xRet := oWsdl:SetOperation( “verificaDisponibilidadeServico” )
If xRet == .F.
Alert( “Erro 1: ” + oWsdl:cError )
Return
EndIf
cMsgWs := ”
cMsgWs += ”
cMsgWs += ”
cMsgWs += ”
cMsgWs += ‘17000190’
cMsgWs += ‘04162’
cMsgWs += ‘05311900’
cMsgWs += ‘05311900’
cMsgWs += ‘sigep’
cMsgWs += ‘n5f9t8’
cMsgWs += ”
cMsgWs += ”
cMsgWs += ”
//Busca o resultado da consulta
xRet := oWsdl:SendSoapMsg(cMsgWs)
If xRet == .F.
Alert( “Erro 2: ” + oWsdl:cError ) //ERRO AQUI: SOAP Fault Code: soap:Client SOAP Fault String: Error reading XMLStreamReader.
Return
EndIf
cResp := oWsdl:GetParsedResponse()
cRespost := oWsdl:GetSoapResponse()
Esse exemplo acima é do próprio manual para teste.
Apresenta o erro (SOAP Fault Code: soap:Client SOAP Fault String: Error reading XMLStreamReader.)
Você poderia me ajudar? Já aconteceu com você esse tipo de erro?
Olá jovem.
No caso, não sei se o comentário cortou os caracteres de maior e menor, mas no seu cMsgWs, você esta enviando as tags abrindo e fechando certinho?
Bom dia! Fui dar uma olhada e o WS de rastreio do exemplo foi descontinuado pelos Correios. Agora é necessário usar a API privada deles.
Bom dia Zé, tudo joia?
Opa, obrigado por nos avisar, assim que possível atualizaremos o conteúdo do artigo.
Um grande abraço.
Bom dia, Daniel! Tudo certo?
Não só o WS de rastreio… todos os WS dos processos de solicitação de etiquetas, geração e envio de PLP etc… serão descontinuados em agosto de 2024. Ficando obrigatório o uso das novas APIs.
Com WS ficou apenas a Logistica Reversa, por enquanto.
Bom dia Zé, tudo joia graças a Deus e você?
Opa, obrigado por nos avisar. Nós atualizamos o artigo em Dezembro de 2023, já utilizando as APIs em REST.
Um grande abraço.