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.