Exemplo de integração com a API dos Correios via AdvPL

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.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

6 Responses

  1. Leonardo Barrozo dos Santos disse:

    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?

  2. Zé da Capela disse:

    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.

      • Zé da Capela disse:

        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.

Deixe uma resposta

Terminal de Informação