Como fazer a integração com WhatsApp no Protheus

Hoje vou mostrar como fazer a integração com WhatsApp usando a API do MktZap no Protheus via AdvPL.

Update – Setembro de 2021:

Pessoal, foi montado também exemplos usando a API da NETiZAP, inclusive criando ChatBot, segue também abaixo a listagem de artigos.

Update 30/07/2021:

Pessoal, o artigo original usa a API do MktZap, recebemos o exemplo também da ConnectZap, abaixo então terão os detalhes de ambas integrações.

NETiZAP

Abaixo a listagem de artigos, usando a API da NETiZAP:

  • Enviando mensagens – link
  • Enviando eMojis – link
  • Personalizando mensagens (quebra de linha) com exemplo de envio automático de boas vindas para clientes, fornecedores e vendedores – link
  • Enviando arquivos com exemplo de envio da DANFE via mensagem – link
  • Montando um menu interativo com usuário (chatbot) – link

ConnectZap (30/07/2021)

Para visualizar a documentação e os fontes disponibilizados, acesse Criando uma integração com WhatsApp no Protheus usando a API da ConnectZap

MktZap (25/01/2021)

Pessoal, o exemplo apresentado aqui foi disponibilizado com a ajuda do grande Fernando Sauer ( LinkedIn ). Basicamente essa função, utiliza uma integração com a API da MktZap (documentação disponível em https://apidoc.mktzap.com.br/#api-History-PostCompanyIdHistoryActive ), onde através do Company ID e da Key do Cliente é possível disparar mensagens.

Via AdvPL, foram utilizados dois recursos, o HttpGet para buscar o TOKEN do cliente (conforme os parâmetros customizados MV_X_ZAPID e MV_X_ZAPCL, Company ID e Key do Cliente respectivamente), e depois para o disparo da mensagem é usado FWRest com POST.

Então no fonte temos 3 funções, a principal que é a u_zMktZap() que recebe a mensagem, o código do cliente e a loja, e a partir disso ela prepara o envio conforme o campo A1_X_WHATS no cadastro de clientes. As outras duas funções são estáticas, a fGetToken() que busca o TOKEN do cliente conforme os parâmetros e a fEnviaMsg() que monta a mensagem e realiza o disparo dela usando a API do MktZap.

Pessoal, ressalto que a integração abaixo é para envio de mensagens, se você quiser uma ferramenta completa no Protheus com integração com BOTS, recomendo:

Sem mais delongas, abaixo o código desenvolvido:

//Bibliotecas
#Include 'TOTVS.ch'
#Include 'ParmType.ch'
#Include 'TopConn.ch'
#Include "RestFul.ch"
#Include "RPTDef.ch"
  
/*/{Protheus.doc} User Function zMktZap
Envia mensagem para WhatsApp usando a API do MktZap para algum cliente cadastrado na SA1
@author Fernando Sauer
@since 16/10/2020
@version 1.0
@type function
@return lRet, Lógico, Retorna se deu certo (.T.) ou não (.F.) o disparo no WhatsApp
@obs Documentação da API: https://apidoc.mktzap.com.br/#api-History-PostCompanyIdHistoryActive
    Exemplo documentado e adaptado por Daniel Atilio
@example u_zMktZap("Esse é um teste da Atilio Sistemas", "000001", "01")
/*/
  
User Function zMktZap(cMensagem, cCliente, cLoja)
    Local aArea := GetArea()
    Local cToken := ""
    Local cNumero := ""
    Local cPais := ""
    Local lRet := .F.
    Default cMensagem := ""
    Default cCliente := ""
    Default cLoja := ""
      
    DbSelectArea("SA1")
    SA1->(DbSetOrder(1)) // Filial + Código + Loja
      
    //Se conseguir posicionar no cadastro de clientes
    If SA1->(DbSeek(FWxFilial("SA1") + cCliente + cLoja)) .And. ! Empty(cMensagem)
          
        //Obtem o TOKEN para disparar a mensagem
        cToken := fGetToken()
          
        //Se deu certo o TOKEN (texto for diferente de ERRO
        If Upper(cToken) != "ERRO"
          
            //Montando o número de telefone para ficar no padrão País + DDD + Número (5514999999999) - buscando de campo customizado chamado A1_X_WHATS
            cNumero := Alltrim(SA1->A1_X_WHATS)
            cPais := Alltrim(SA1->A1_PAIS)
              
            //Remove caracteres especiais do número, como hífen e parenteses
            cNumero := StrTran(cNumero, "-", "")
            cNumero := StrTran(cNumero, " ", "")
            cNumero := StrTran(cNumero, "(", "")
            cNumero := StrTran(cNumero, ")", "")
              
            //Se o começo do número for diferente de 55, e o país for Brasil, adiciona 55 na esquerda
            If SubStr(cNumero, 1, 2) != "55" .And. cPais == "105"
                cNumero := "55" + cNumero
            EndIf
              
            //Chama a função de envio de mensagem, e se não deu certo, mostra mensagem
            lRet := fEnviaMsg(cToken, cMensagem, cNumero, Alltrim(SA1->A1_NOME))
            If ! lRet
                MsgStop("Falha ao enviar a mensagem!", "Atenção")
            EndIf
              
        Else
            MsgStop("Falha ao buscar o TOKEN!", "Atenção")
        EndIf
          
    Else
        MsgStop("Cliente não encontrado e/ou Mensagem vazia!", "Atenção")
    EndIf
      
    RestArea(aArea)
Return lRet
  
/*/{Protheus.doc} fGetToken
Função para buscar o TOKEN do MktZap via HttpGet
@author Fernando Sauer
@since 16/10/2020
@version 1.0
@type function
@return cToken, Caractere, Código do TOKEN disponibilizadopelo MktZap
/*/
  
Static function fGetToken()
    Local cUrl := ""
    Local cCompId := Alltrim(GetMV("MV_X_ZAPID")) //ID da Compania - deve ser cadastrado e informado conforme MktZap disponibiliza
    Local cCliKey := Alltrim(GetMV("MV_X_ZAPCL")) //Chave - deve ser cadastrado e informado conforme MktZap disponibiliza
    Local aGetTok := {}
    Local cToken := ""
    Local cTpRet := ""
    Local cHtmlPage
      
    //Montando a string que será enviada para o MktZap
    cUrl := "https://api.mktzap.com.br/company/"
    cUrl += cCompId
    cUrl += "/token?clientKey="
    cUrl += cCliKey
      
    //Executa uma requisição através do HttpGet
    cHtmlPage := HttpGet(cUrl)
      
    //Trata a string que foi retornada
    cHtmlPage := StrTran(cHtmlPage, ':', ";")
    cHtmlPage := StrTran(cHtmlPage, '{', "")
    cHtmlPage := StrTran(cHtmlPage, '}', "")
    cHtmlPage := StrTran(cHtmlPage, '"', "")
      
    //Quebrando a string em um array e pegando a primeira posição
    aGetTok := StrTokArr( cHtmlPage, ";" )
    cTpRet := aGetTok[1]
      
    //Se veio a informação, pega a segunda posição sendo o TOKEN, senão retornará erro
    If Upper(cTpRet) == "ACCESSTOKEN"
        cToken := aGetTok[2]
    Else
        cToken := "Erro"
    EndIf
      
Return cToken
  
/*/{Protheus.doc} fEnviaMsg
Função que faz o envio da mensagem
@author Fernando Sauer
@since 16/10/2020
@version 1.0
@type function
@return lRet, Lógico, Retorna se deu certo (.T.) ou não (.F.) o disparo no WhatsApp
/*/
  
Static Function fEnviaMsg(cToken, cMensagem, cNumero, cNome)
    Local cUrlRegisto := 'https://api.mktzap.com.br'
    Local cCompId := Alltrim(GetMV("MV_X_ZAPID")) //ID da Compania - deve ser cadastrado e informado conforme MktZap disponibiliza
    Local oRestClient := FWRest():New(cUrlRegisto)
    Local cClasse := "/company/" + cCompId +"/history/active"
    Local aHeadSend    := {}
    Local cJson       := ''
    Local cBodyEnv       := ''
    Local oObjResul   := Nil
    Local cMsgExib := ''
    Local cAmbiente := "whatsapp"
    Local lRet := .F.
    Default cMensagem := ""
    Default cNumero := ""
      
      
    /* Modelo de Mensagem para envio
    cMensagem := "Olá, *"+ AllTrim(cNome) +  "* "  + SPACE(80)//+ CRLF
    cMensagem += "Lembramos que você possui um boleto que vencerá nos próximos dias. " + SPACE(80)//+ CRLF
    cMensagem += "Beneficiário: " + FWFilialName(SUBSTR(SE1->E1_FILIAL,1,2)) + " "  +  SPACE(80)//+ CRLF
    cMensagem += "Vencimento: *"+ DtoC(SE1->E1_VENCREA) + "* " +  SPACE(80)//+  CRLF
    cMensagem += "Valor R$: *" + AllTrim(TRANSFORM(SE1->E1_VALOR, "@E 999,999.99"))  + "* " +  SPACE(100) //+ CRLF
    cMensagem += "Cógido de Barras para Pagamento: " + cCodBar + " " //+ CRLF*/
  
      
    //Monta o Cabeçalho
    aAdd(aHeadSend, "Content-Type: application/json; charset=UTF-8")
    aAdd(aHeadSend, "Accept: application/json")
    aAdd(aHeadSend, "User-Agent: Chrome/65.0 (compatible; Protheus " + GetBuild() + ")")
    aAdd(aHeadSend, "Authorization: Basic " + cToken)
      
    //Monta a mensagem que será enviada
    cBodyEnv := '{'                                            + CRLF
    cBodyEnv += '    "type": "' + cAmbiente + '",'             + CRLF        // Texto: whatsapp
    cBodyEnv += '    "phone_number": "' + cNumero + '",'        + CRLF         // Número para envio da mensagem
    cBodyEnv += '    "name": "' + cNome + '",'                 + CRLF        // Nome da Pessoa que a mensagem será enviada
    cBodyEnv += '    "messages": ['                                + CRLF
    cBodyEnv += '        {'                                        + CRLF
    cBodyEnv += '            "type": "text" ,'                    + CRLF
    cBodyEnv += '            "content": "' + cMensagem + '" '    + CRLF
    cBodyEnv += '        }'                                        + CRLF
    cBodyEnv += '    ]'                                            + CRLF
    cBodyEnv += '}'                                            + CRLF
      
    //Tira caracteres especiais e transforma a codificação
    cBodyEnv := A140IRemASC(cBodyEnv)
    cBodyEnv := EncodeUtf8(cBodyEnv)
      
    //Define o path onde será feito a requisição Informa o path aonde será feito a requisição
    oRestClient:setPath(cClasse)
       
    //Define o conteúdo do body
    oRestClient:SetPostParams(cBodyEnv)
      
    //Efetua o Post para enviar a mensagem
    If oRestClient:Post(aHeadSend)
        //Busca o resultado
        cMsgExib := oRestClient:GetResult()
        cJson := cMsgExib
          
        // Converte string no Formato JSon para Objeto
        If FWJsonDeserialize(cJson, @oObjResul)
              
            //Se houve erro, ficará na variável
            If Type('oObjResul:Error') == 'C'
                cMsgExib := oObjResul:Error
                lRet := .F.
            Else
                lRet := .T.
            EndIf
              
        //Se houve algum erro dentro do JSON será exibido
        Else
            cMsgExib := "Erro JSON: " + cJson
            lRet := .F.
        EndIf
          
    Else
        cMsgExib := "Erro no Post: " + oRestClient:GetLastError() + CRLF
        cMsgExib += oRestClient:GetResult()
        lRet := .F.
    EndIf
      
    //Mostra mensagem ao encerrar a função
    If lRet
        MsgInfo("Mensagem enviada com sucesso!", "Atenção")
    Else
        MsgStop(cMsgExib, "Erro")
    EndIf
      
Return lRet

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan Atilio (Daniel Atilio)
Especialista em Engenharia de Software pela FIB. Entusiasta de soluções Open Source. E blogueiro nas horas vagas.

8 Responses

  1. EDMILSON SANTOS disse:

    Fiquei na dúvida com estes campos: Como saber sobre o ID e Key do Cliente, onde encontras estes.
    MV_X_ZAPID e MV_X_ZAPCL

  2. Raphael Silva disse:

    Boa tarde! Em contato com MktZap, só oferecem a plataforma inteira deles de troca de mensagens Não oferencem ID e o Key. Será que voce consegue com eles alguns pra gente testar?

  3. JADER BERTO disse:

    Boa tarde amigos.

    Uma outra solução é o Connectzap.

    Ele possibilita:

    Envio de boleto bancário para clientes com notificações sobre vencimento.
    Envio de solicitação de aprovação para determinado processo como: Solicitação de Compras, Pedido de Compras, Cotação, Contratos, etc.
    Envio de Nota Fiscal de Saída para clientes.
    Envio de contra-cheque para funcionários.
    Comunicação do RH com Funcionários.
    Notificações diversas, como saldo em estoque, não conformidades, procedimentos de segurança, servidor com baixa memória, etc.

    http://www.connectzap.com.br

  4. Tiago Fonseca Lima de Moraes disse:

    Boa tarde amigos, estou com uma situação na minha empresa em que preciso enviar um relatório diariamente para os vendedores, irei colocar esse relatório no schedule, gostaria de saber se alguém já fez a integração pra mandar algum relatório de forma automatica pelo WhatsApp dessa forma?

Deixe uma resposta