Criando uma integração com WhatsApp no Protheus usando a API da ConnectZap

No artigo de hoje, vou mostrar para vocês como integrar facilmente com WhatsApp no Protheus usando a API da ConnectZap.

Recentemente o grande Jader veio conversar comigo, explicando sobre uma ótima forma de integrar o Protheus com WhatsApp via AdvPL / TL++ usando a API da ConnectZap ( https://www.connectzap.com.br/ ).

Ele preparou dois códigos fontes de exemplo, que já estão prontos para qualquer um usar a API da ConnectZap.

Dos dois fontes, o ConnZap.prw é uma tela de leitura, que faz a integração entre o ConnectZap e o Protheus (também pode ser feito no portal). Já o MsgZap.prw é a função que vai ser acionada para o disparo da mensagem, que recebe 3 parâmetros:

  • cCelular – Número de celular que será recebido, por exemplo, 5521992584067
  • cMsg – Mensagem que será enviada
  • cAnexo – Caminho do arquivo que será enviado como anexo (esse é um parâmetro opcional)

Nesses dois fontes, também é necessário que você crie 2 parâmetros na sua base:

  • MV_ZAPURL – URL da ConnectZap
  • MV_ZSESSAO – Token / Sessão usado na integração da ConnectZap

Além de tudo isso, o pessoal da ConnectZap disponibilizou um excelente manual com as funcionalidades da API, veja em https://www.connectzap.com.br/manual.pdf.

Abaixo um vídeo de como funciona a integração com o Protheus via AdvPL.

Código fonte completo do ConnZap.prw:

//Bibliotecas
#Include 'TOTVS.ch'
#Include 'ParmType.ch'

/*/{Protheus.doc} User Function ConnZap
Tela de leitura do QRCODE - WhatsApp
@type  Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/

User Function ConnZap()
    Local aArea := GetArea()
    Local oNewPag
    Local oStepWiz := Nil
    Local oPanelBkg
    Private cMsg := Space(50)
    Private oDlg := Nil
    Private cNome
    Private cStatus := ""
    Private oSay1
    Private cGet1 := Space(150)
    Private cGet2 := Space(14)
    Private cGet3 := Space(250)

    //Se a pasta qrcode não existir na Protheus Data, cria
    If ! ExistDir("\qrcode")
        MakeDir("\qrcode")
    EndIf
    
    //Busca o Token / Sessão
    cNome := Alltrim(GetMv("MV_ZSESSAO"))

    //Criando a dialog da integração
    oDlg:= MSDialog():New(0, 0, 300, 300, 'Conecte o WhatsApp com seu SmartPhone', , , , nOr(WS_VISIBLE, WS_POPUP), CLR_BLACK, CLR_WHITE, , , .T., , , , .T.)
        oDlg:nWidth := 600
        oDlg:nHeight := 600

        //Cria um painel de fundo, e vincula um Wizard a ele
        oPanelBkg:= tPanel():New(0, 0, "", oDlg, , , , , , 300, 300)
        oStepWiz:= FWWizardControl():New(oPanelBkg)
        oStepWiz:ActiveUISteps()

        //Página 1 do Wizard
        oNewPag := oStepWiz:AddStep("1")
        oNewPag:SetStepDescription("Seleção de Token")
        oNewPag:SetConstruction({|oPanel| Cria_Pg1(oPanel, @cNome)})
        oNewPag:SetNextAction({|| Valida_Pg1(@cNome)})
        oNewPag:SetCancelAction({||, fCancel()})

        //Página 2 do Wizard
        oNewPag := oStepWiz:AddStep("2", {|oPanel| Cria_Pg2(oPanel)})
        oNewPag:SetStepDescription("Leitura de QrCode")
        oNewPag:SetNextAction({||Valida_Pg2(@cNome)})
        oNewPag:SetCancelAction({|| fCancel()})

        //Página 3 do Wizard
        oNewPag := oStepWiz:AddStep("3", {||oDlg:End()})
        oNewPag:SetStepDescription("Teste de envio")
        oNewPag:SetNextAction({||.T.})
        //Define o bloco ao clicar no botão Voltar
        oNewPag:SetCancelAction({|| fCancel()})
        oNewPag:SetPrevAction({||, .T.})

        oStepWiz:Activate()
    ACTIVATE DIALOG oDlg CENTER
    oStepWiz:Destroy()

    RestArea(aArea)
Return

//Construção da Página 1
Static Function Cria_Pg1(oPanel, cNome)
    Local aItems := {}

    //Monta o say e o combo para definir o Token
    oSay1:= TSay():New(10, 10, {||'Escolha o seu token de acesso'}, oPanel, , , , , , .T., , , 200, 20)
    cNome := PADR(cNome, 30)
    aAdd(aItems, Alltrim(GetMV("MV_ZSESSAO")))
    oCombo := tComboBox():new(20, 10, {|u|if(pcount()>0, cNome:=u, cNome)}, aItems, 100, 20, oPanel, , , , , , .t., , , , , , , , , )
Return

//Validação da página 1
Static Function Valida_Pg1(cNome)
    Local cStrJson := ""
    Local oObj
    Local lRet := .T.

    cNome := Alltrim(cNome)
    Processa({||cStrJson := U_fStatus(cNome)})

    //Busca o JSON e deserializa
    FWJsonDeserialize(cStrJson, @oObj)

    //Se o objeto estiver nulo, mostra um help, senão busca o status da integração
    If oObj == Nil
        Help(" ", 1, "Help", , "O serviço do Whatsapp está desconectado, favor reportar ao administrador!", 1, 0)
        lRet := .F.
    Else
        cStatus := oObj:Status:state
    EndIf

Return lRet

//Construção da página 2
Static Function Cria_Pg2(oPanel)
    Local oTimer
    Local oObj
    Local oFont

    //Define a fonte, e cria um Say
    oFont := TFont():New('Courier new', , -14, .T.)
    oSay1 := TSay():New( 10, 10, {|u| if( PCount() > 0, cMsg := u, cMsg ) } , oPanel, , oFont, , , , .T., CLR_BLUE, CLR_WHITE, 300, 009)

    //Verifica o status da conexão
    cStrJson := U_fStatus(cNome)
    FWJsonDeserialize(cStrJson, @oObj)
    cStatus := oObj:Status:state

    //Se for não encontrado, inicializa a conexão
    If cStatus == "NOTFOUND"
        cStrJson := U_fStart(cNome)
        Sleep(15000)

        cStrJson := U_fStatus(cNome)
        FWJsonDeserialize(cStrJson, @oObj)
        cStatus := oObj:Status:state
    EndIf

    //Se já estiver conectado
    If cStatus == "CONNECTED"
        oSay1:SetText('Seu WhatsApp já está conectado!')
        oTBitmap1 := TBitmap():New(40, 80, 260, 184, , '\qrcode\whatsapp.png', .T., oPanel, {||}, , .F., .F., , , .F., , .T., , .F.)

    //Senão cria um temporizador, e mostra a mensagem para apontar para o QRCode
    Else
        oTimer := TTimer():New(1, {|| BuscaQR(oPanel) }, oDlg )
        oTimer:Activate()
        oSay1:SetText('Abra o WhatsApp Web do seu SmartPhone e aponte a camera para o QRCODE:')
    EndIf

Return

//Validação da página 2
Static Function valida_pg2(cNome)
    Local lRet := .T.

    //Se o status não estiver conectado
    If cStatus != "CONNECTED"
        Help(" ", 1, "Help", , "O serviço do Whatsapp não está sincronizado, faça a leitura do Qrcode!", 1, 0)
        lRet := .F.
    EndIf
Return lRet

Static Function BuscaQR(oPanel)
    Local cStrJson := ""
    Local oObj
    Local cFile
    Local lRet := .T.

    //Define o nome do arquivo png do qrcode, e faz a conexão com a API
    cNome := alltrim(cNome)
    cFile := "qrcode_" + cNome + ".png"
    cStrJson := U_fStatus(cNome)
    FWJsonDeserialize(cStrJson, @oObj)
    cStatus := oObj:Status:state

    //Se o status estiver como conectado
    If cStatus == "CONNECTED"
        oSay1:SetText('Seu WhatsApp foi conectado!')
        oTBitmap1 := TBitmap():New(40, 80, 260, 184, , '\qrcode\whatsapp.png', .T., oPanel, {||}, , .F., .F., , , .F., , .T., , .F.)
        lRet := .T.
    
    //Senão, define a imagem do QRCode
    Else
        fQrCode(cNome)
        Sleep(1000)
        oTBitmap1 := TBitmap():New(40, 80, 260, 184, , '\qrcode\'+cFile, .T., oPanel, {||}, , .F., .F., , , .F., , .T., , .F.)
        cStrJson := U_fStatus(cNome)
        lRet := ("CONNECTED" $ cStrJson)
    EndIf
Return lRet

// Construção da página 3
Static Function cria_pg3(oPanel, cNome)
    Local lHasButton := .T.

    //Get da Mensagem
    oSay0:= TSay():New(10, 10, {||'Mensagem:'}, oPanel, , , , , , .T., , , 200, 20)
    TGet():New( 20, 10, { | u | If( PCount() == 0, cGet1, cGet1 := u ) }, oPanel, ;
        250, 010, "!@", , 0, 16777215, , .F., , .T., , .F., , .F., .F., , .F., .F. , , "cGet1", , , , lHasButton  )

    //Get do Número de Celular
    oSay2:= TSay():New(40, 10, {||'Celular:'}, oPanel, , , , , , .T., , , 70, 20)
    TGet():New( 50, 10, { | u | If( PCount() == 0, cGet2, cGet2 := u ) }, oPanel, ;
        60, 010, "@R (99) 99999-9999", , 0, 16777215, , .F., , .T., , .F., , .F., .F., , .F., .F. , , "cGet2", , , , lHasButton  )

    //Get do Arquivo (opcional)
    oSay3:= TSay():New(70, 10, {||'Escolha um arquivo (opcional):'}, oPanel, , , , , , .T., , , 200, 20)
    TGet():New( 80, 10, { | u | If( PCount() == 0, cGet3, cGet3 := u ) }, oPanel, ;
        250, 010, "!@", , 0, 16777215, , .F., , .T., , .F., , .F., .F., , .F., .F. , , "cGet3", , , , lHasButton  )
    
    //Botões
    oTButton1 := TButton():New( 81, 260, "Abrir",  oPanel, {||zChooseFile()}, 25, 10, , , .F., .T., .F., , .F., , , .F. ) 
    oTButton2 := TButton():New( 100, 10, "Enviar", oPanel, {||Iif(u_MsgZap('55' + cGet2 , cGet1, cGet3), MSGINFO( "Sucesso!", "Resultado do Envio" ), MSGINFO( "Mensagem não enviada.", "Resultado do Envio" ))}, 25, 10, , , .F., .T., .F., , .F., , , .F. ) 
Return

Static Function zChooseFile()
    Local aArea   := GetArea()
    Local cDirIni := GetTempPath()
    Local cTipArq := "Todas extensões (*.*)"
    Local cTitulo := "Seleção de Arquivos para Processamento"
    Local lSalvar := .F.
    Local cArqSel := ""
 
    //Se não estiver sendo executado via job
    If ! IsBlind()
 
        //Chama a função para buscar arquivos
        cArqSel := tFileDialog(;
            cTipArq, ;  // Filtragem de tipos de arquivos que serão selecionados
            cTitulo, ;  // Título da Janela para seleção dos arquivos
            , ;         // Compatibilidade
            cDirIni, ;  // Diretório inicial da busca de arquivos
            lSalvar, ;  // Se for .T., será uma Save Dialog, senão será Open Dialog
            ;          // Se não passar parâmetro, irá pegar apenas 1 arquivo; Se for informado GETF_MULTISELECT será possível pegar mais de 1 arquivo; Se for informado GETF_RETDIRECTORY será possível selecionar o diretório
        )
 
        If ! Empty(cArqSel)
            cGet3 := cArqSel
        EndIf
    EndIf

Return cArqSel

//Função que fecha a dialog
Static Function fCancel()
    oDlg:End()
Return .T.

Static Function fQrCode(cNome)
    Local cReturn
    Local nTimeOut := 120
    Local aHeadOut := {}
    Local cHeadRet := ""
    Local cPostParms:= ""
    Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
    Local cSession := Alltrim(cNome)
    Local cFile := "qrcode_"+alltrim(cNome)+".png"

    //Realiza a integração e busca o QRCode
    cUrl    += "/QRCode"
    aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
    aAdd(aHeadOut, 'Content-Type: application/json')
    cPostParms := '{"SessionName": "'+cSession+'", "View": true }'
    cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)

    //Grava a imagem
    MemoWrite('\qrcode\'+cFile, cReturn)
Return cReturn

/*/{Protheus.doc} User Function ConnZap
Fecha a conexão da Sessão / Token com ConnectZap
@type  Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/

User Function fClose(cNome)
    Local aArea := GetArea()
    Local cReturn
    Local nTimeOut := 120
    Local aHeadOut := {}
    Local cHeadRet := ""
    Local cPostParms:= ""
    Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
    Local cSession := Alltrim(cNome)

    //Define a operação para fechar e faz a integração
    cUrl    += "/Close"
    aAdd(aHeadOut, 'Content-Type: application/json')
    cPostParms := '{"SessionName":"'+cSession+'"}'
    cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)

    RestArea(aArea)
Return cReturn

/*/{Protheus.doc} User Function ConnZap
Inicializa a conexão da Sessão / Token com ConnectZap
@type  Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/

User Function fStart(cNome)
    Local aArea := GetArea()
    Local cReturn
    Local nTimeOut := 120
    Local aHeadOut := {}
    Local cHeadRet := ""
    Local cPostParms:= ""
    Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
    Local cSession := Alltrim(cNome)

    //Define a operação para inicializar e faz a integração
    cUrl    += "/Start"
    aAdd(aHeadOut, 'Content-Type: application/json')
    cPostParms := '{"SessionName":"'+cSession+'"}'
    cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
    Sleep(2000)

    RestArea(aArea)
Return cReturn

/*/{Protheus.doc} User Function ConnZap
Busca o status da Sessão / Token com ConnectZap
@type  Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/

User Function fStatus(cNome)
    Local aArea := GetArea()
    Local cReturn
    Local nTimeOut := 120
    Local aHeadOut := {}
    Local cHeadRet := ""
    Local cPostParms:= ""
    Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
    Local cSession := Alltrim(cNome)

    //Define a operação para buscar o status e faz a integração
    cUrl    += "/Status"
    aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
    aAdd(aHeadOut, 'Content-Type: application/json')
    cPostParms := '{"SessionName": "'+cSession+'"}'
    cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)

    RestArea(aArea)
Return cReturn

/*/{Protheus.doc} User Function ConnZap
Busca informações do dispositivo conectado da Sessão / Token com ConnectZap
@type  Function
@author Jader Berto
@since 13.08.2019
@version version
@example
@see https://www.connectzap.com.br/manual.pdf
/*/

User Function fHard(cNome)
    Local aArea := GetArea()
    Local cReturn
    Local nTimeOut := 120
    Local aHeadOut := {}
    Local cHeadRet := ""
    Local cPostParms:= ""
    Local cUrl := SuperGetMV("MV_ZAPURL", .T., "https://api.connectzap.com.br/sistema")
    Local cSession := Alltrim(cNome)

    //Define a operação para buscar informações do dispositivo conectado e faz a integração
    cUrl    += "/getHostDevice"
    aAdd(aHeadOut, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
    aAdd(aHeadOut, 'Content-Type: application/json')
    cPostParms := '{"SessionName": "'+cSession+'"}'
    cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)

    RestArea(aArea)
Return cReturn

Código fonte completo do MsgZap.prw:

//Bibliotecas
#Include 'TOTVS.ch'
#Include 'FWMVCDef.ch'
#Include 'RestFul.ch'
#Include 'FileIO.ch'

/*/{Protheus.doc} User Function MsgZap
Envio de WHATSAPP via Protheus
@type    Function
@author Jader Berto
@since 13.08.2019
@version version
@param cCelular, Caractere, Número de Celular que irá receber a mensagem
@param cMsg, Caractere, Mensagem que será enviada
@param cAnexo, Caractere, Caminho do anexo (parâmetro opcional)
@return lRet, Lógico, Retorna se houve sucesso ou não no envio
@example
    Exemplos abaixo:
    // Com Anexo
    U_MsgZap('5521992584067' ,'Teste Protheus', 'TSTPOLX01_210505_112556_Administrador.txt')

    // Sem Anexo
    U_MsgZap('5521975267383' ,'Teste Protheus')
@see https://www.connectzap.com.br/manual.pdf
/*/

User Function MsgZap(cCelular, cMsg, cAnexo)
    Local aArea      := GetArea()
    Local lRet       := .F.
    Default cCelular := ""
    Default cMsg     := ""
    Default cAnexo   := ""     
    Private lAuto    := IsBlind()

    //Retirando caracteres especiais do numero
    cCelular := Alltrim(cCelular)
    cCelular := Replace(cCelular, "(", "")
    cCelular := Replace(cCelular, ")", "")
    cCelular := Replace(cCelular, "-", "")
    cCelular := Replace(cCelular, " ", "")

    //Se o celular tiver vazio, ou o tamanho for menor que 8
    If Empty(cCelular) .Or. Len(cCelular) < 8
        If lAuto
            fConOut("CONNECTZAP = Favor enviar o número do celular WhatsApp.")
        Else
            MsgInfo("Favor enviar o número do celular WhatsApp.", "Problemas ao enviar" )
        EndIf

        lRet := .F.
    EndIf

    //Se não tiver mensagem para enviar
    If Empty(cMsg)
        If lAuto
            fConOut("CONNECTZAP = Favor enviar a mensagem a ser enviada.")
        Else
            MsgInfo("Favor enviar a mensagem a ser enviada.", "Problemas ao enviar" )
        EndIf

        lRet := .F.
    EndIf
	
	//Se houver anexo, verifica se a extensão jpg, bmp ou png
	If ! Empty(cAnexo)
		If ! (".jpg" $ Lower(cAnexo) .Or. ".bmp" $ Lower(cAnexo) .Or. ".png" $ Lower(cAnexo))
			lRet := .F.
		EndIf
	EndIf

    //Se passou pela validação do celular e da mensagem, Faz o envio da mensagem
    If lRet
        lRet := SetZap(cCelular, cMsg, cAnexo)
    EndIf

    RestArea(aArea)
Return lRet

Static function SetZap(cCelular ,cMsg, cAnexo)
    Local oOBJ 
    Local cStrJson 
    Local lRet			 := .F.
    Local nTimeOut   := 120
    Local aHeadOut   := {}
    Local cHeadRet   := ""
    Local cPostParms := ""
    Local cURL       := SuperGetMV("MV_ZAPURL", .T., "http://api.connectzap.com.br/sistema")
    Local cSession   := Alltrim(SuperGetMV("MV_ZSESSAO", .T., "SEUTOKEN"))
    Local xNome
    Local nZ         := 0

    cMsg := EncodeUTF8(Replace(cMsg, Chr(10), ""))
    cCelular := Alltrim(cCelular)

    //Se a pasta temporária não existir, cria
    If ! ExistDir("C:\temp")
        MakeDir("C:\temp")
    EndIf
    
    //Se a pasta de arquivos não existir, cria
    If ! ExistDir("C:\temp\files")
        MakeDir("C:\temp\files")
    EndIf

    //Faz a conexão conforme o token do parâmetro
    cStrJson := U_fStatus(cSession)
    Sleep(2000)
    FWJsonDeserialize(cStrJson, @oOBJ)
    cStatus := oOBJ:Status:state

    //Se não estiver conectado
    If cStatus $ "NOTFOUND|CLOSED|" .OR. cStatus == "DISCONNECTED"

        //Enquanto não estiver checado a informação de QRCode, reinicializa
        While !("QRCODE" $ U_fStatus(cSession))
            cStrJson := U_fStart(cSession)
            Sleep(3000)    

            //Se estiver conectado, encerra o laço
            If "CONNECTED" $ cStrJson
                Exit
            EndIf
        EndDo
    EndIf

    //Retirando o -enter- da mensagem
    cMsg := Alltrim(Replace(cMsg, CRLF, ""))

    //Se houver anexo
    If ! Empty(cAnexo)
        cUrl        += "/sendFileBase64"
        aAdd(aHeadOut, 'Accept: application/json')
        aAdd(aHeadOut, 'Content-Type: application/json')

        //Se tiver barra no anexo
        If "/" $ cAnexo
            xNome := SubStr(cAnexo, RAt("/", cAnexo) + 1, Len(cAnexo))
            __CopyFile(cAnexo, "C:\temp\files" + SubStr(cAnexo, RAt("/", cAnexo), Len(cAnexo)))
            cAnexo := Encode64(, "C:\\temp\\files\\" + SubStr(cAnexo, RAt("/", cAnexo) + 1, Len(cAnexo)), .F., .F.)

        Else
            xNome := SubStr(cAnexo, RAt("\", cAnexo) + 1, Len(cAnexo))
            __CopyFile(cAnexo, "C:\temp\files" + SubStr(cAnexo, RAt("\", cAnexo), Len(cAnexo)))
            
            //Percorre o nome do arquivo, e define \ na última \
            For nZ:= 1 to Len(cAnexo)
                If subs(cAnexo,nZ,1) == '\'
                    nPosIni := nZ + 1
                EndIf
            Next nZ
            
            cAnexo := Encode64(,"C:\\temp\\files\\" + SubStr(cAnexo, nPosIni, Len(cAnexo)), .F., .F.)
        EndIf

        //Define os parâmetros do Post
        cPostParms := '{'
        cPostParms += '"SessionName":"'    + cSession + '",'
        cPostParms += '"phonefull":"'        + cCelular + '",'
        cPostParms += '"base64":"'             + cAnexo     + '",'
        cPostParms += '"originalname":"' + xNome        + '",'
        cPostParms += '"caption":"'            + cMsg         + '"'
        cPostParms += '}'         

    //Senão, define como mensagem de envio de texto
    Else
        cUrl        += "/sendText"
        aAdd(aHeadOut, 'Accept: application/json')
        aAdd(aHeadOut, 'Content-Type: application/json')

        cPostParms := '{'
        cPostParms += '"SessionName":"'+ cSession + '",'
        cPostParms += '"phonefull":"'    + cCelular + '",'
        cPostParms += '"msg":"'                + Alltrim(cMsg) + '"'
        cPostParms += '}'
    EndIf

    //Usa o Post enviando as parametrizações acima
    cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
    sleep(3000)
    
    //Se o retorno não for nulo, verifica se o status é conectado ou fechado
    If cReturn != Nil     
        lRet := (oOBJ:Status:state == "CONNECTED") .OR. (oOBJ:Status:state == "CLOSED")
        
    Else
        lRet := .F.
    EndIf

    //Se o retorno não estiver correto
    If !lRet
        //Se não for forma automática, mostra a tela de conexão novamente, senão mostra mensagem no console.log
        If !lAuto
            u_ConnZap()
        Else
            fConOut("CONNECTZAP = Problema ao sincronizar com o Connectzap. Utilize o Painel de integração no protheus ou no site (painel.connectzap.com.br).")
        EndIf

        //Realiza um novo envio
        cReturn := HttpPost(cUrl, , cPostParms, nTimeOut, aHeadOut, @cHeadRet)
        Sleep(3000)

        //Verifica se teve sucesso no envio
        FWJsonDeserialize(cReturn, @oOBJ)
        lRet := "success" $ cReturn
    EndIf
 
Return    lRet

// https://centraldeatendimento.totvs.com/hc/pt-br/articles/360041301114-MP-ADVPL-Como-Ativar-a-fun%C3%A7%C3%A3o-FWLogMsg-
Static Function fConOut(cTexto)
    FWLogMsg("INFO", /*cTransactionId*/, "CONOUT", /*cCategory*/, /*cStep*/, /*cMsgId*/, cTexto, /*nMensure*/, /*nElapseTime*/, /*aMessage*/)
Return

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.

Deixe uma resposta

Terminal de Informação