Código: Selecionar todos
/*
TITULO : SISTEMA DE GEST¦O DE COMERCIO
DATA : 06-06-2023
PROGRAMA : CLASS_PIX.PRG
COMENTARIO : ROTINA DE INTEGRACAO DE PIX GENERICO
*** como utilizar a class ***
oPIX := PIXClass( ):New( )
oPIX:Setup(sBanco, nEnviroment, sCertificado, sClient_Id, sClient_Secret, sPIX_Key, sPIX_Receive, sPIX_City, sPIX_Amount, sPIX_Description, sPIX_Identification, sPDF_Linha1, sPDF_linha2, stxid)
oPIX:Execute( )
if oPIX:erro_status = 200
...
...
...
else
hwg_MsgInfo("REMOVA A FORMA DE PAGAMENTO"+ HB_EOL() + ;
"E INCLUA NOVAMENTE PARA REPETIR"+ HB_EOL() + ;
"A OPERAÇÃO", "OPERAÇÃO NÃO EFETUADA!!!")
endif
obs: se passar com o stxid ele vai somente fazer uma consulta a um pix existente
***********************************************************************************
como criar um WEBHOOK
https://youtu.be/y-iCT7HexPY --> video explicando
https://insomnia.rest/download --> software utilizado na criacao da URL
https://groups.google.com/g/harbour-users/c/CBcrRAHnQSA WEB_SOQKET
https://github.com/FiveTechSoft/wsserver/blob/master/wsserver.prg WEB SOCKET EM HARBOUR
https://github.com/FiveTechSoft/wsserver
https://github.com/rafathefull/restful/blob/master/status_service.prg
https://dev.gerencianet.com.br/docs/api-pix-endpoints#exemplos-de-configura%C3%A7%C3%B5es-de-servidor
https://webhook.site/
*/
#include "hbclass.ch"
//#include "hbgtinfo.ch"
#define PDFCLASS_PORTRAIT 1
#define PDFCLASS_LANDSCAPE 2
#define PDFCLASS_TXT 3
#define PDFCLASS_ROLLS 4
#DEFINE WS_OK 200
#DEFINE WS_ERROR 400
CREATE CLASS PIXClass
*** Parametros de consultas ***
VAR txid INIT ""
VAR id INIT 0
VAR location INIT ""
VAR e2eId INIT ""
VAR data_Inicio INIT "" // NO FORMATO "2020-10-22T16:01:35Z"
VAR data_Fim INIT "" // NO FORMATO "2020-10-22T16:01:35Z"
*** variaveis de retorno ***
VAR qrcode INIT ""
VAR qrcodeImage INIT ""
VAR qrcodeImagePDF INIT ""
VAR linkVisualizacao INIT ""
VAR ListaDeCobranca
VAR Hash_Retorno
VAR pix_url INIT ""
VAR cFilename INIT ""
VAR status INIT ""
*** variaveis de retorno de ERRO ***
VAR erro_status INIT WS_OK
VAR erro_nome INIT ""
VAR erro_mensagem INIT ""
METHOD Setup(sBanco, nEnviroment, sCertificado, sClient_Id, sClient_Secret, sPIX_Key, sPIX_Receive, sPIX_City, sPIX_Amount, sPIX_Description, sPIX_Identification, sPDF_Linha1, sPDF_linha2) //ok
METHOD Execute( ) //ok
METHOD PDF_QRCode( ) //ok
METHOD Consulta( sBanco, nEnviroment, sCertificado, sClient_Id, sClient_Secret, stxid) //ok
METHOD Pix_String( ) //ok
PROTECTED:
**** Dados Dinamicos no momentos da Geracao
VAR nPIX_AMOUNT INIT ""
VAR cPIX_DESCRIPTION INIT ""
VAR cPIX_IDENTIFICATION INIT ""
**** Parametros Basicos cadastro da Empresa ****
VAR cPIX_KEY INIT "" // chave pix (CNPJ, email, celular)
VAR cPIX_RECEIVER INIT "" // Nome do Proprietario do PIX
VAR cPIX_CITY INIT "" // Nome da Cidade do proprietario do PIX
VAR cPIX_ZIPCODE INIT ""
**** Parametros Basicos do Ambiente ****
VAR BANCO INIT "000" // 000-API GENERICA, OUTRO VALOR PARA ROTA BASICA DO BANCO
VAR ENVIROMENT_TYPE INIT 1 // 0-Homologação 1-Produção
VAR MY_CERTIFICATE INIT "" //CERTIFICADO DIGITAL DE HOMOLOGACAO/PRODUCAO GERADO PELO BANCO EFI
VAR CLIENT_ID INIT "" //ID DE HOMOLOGACAO/PRODUCAO DO BANCO EFI
VAR CLIENT_SECRET INIT "" //SENHA DE HOMOLOGACAO/PRODUCAO DO BANCO EFI
VAR ROUTE_BASE INIT ""
VAR access_token INIT "" //token retornado pela autenticacao
VAR expires_in INIT 3600 // Tempo de expiração em segundos
VAR chave INIT ""
VAR devolutionCustomId INIT ""
VAR idEnvio INIT ""
VAR cPDF_Linha1 INIT ""
VAR cPDF_Linha2 INIT ""
METHOD Authorize( ) //ok
METHOD PrintQRCode ( ) //ok
METHOD pix_config_webhook( chave )
METHOD pix_get_webhook( chave )
METHOD pix_list_webhook( data_Inicio, data_Fim )
METHOD pix_delete_webhook( chave )
METHOD pix_create_charge( ) //ok
METHOD pix_create_immediate_charge( ) //ok
METHOD pix_detail_charge( ) //ok
METHOD pix_update_charge( ) //ok
METHOD pix_list_charges( ) //ok
METHOD pix_generate_QRCode( ) //ok
METHOD WS( cPostGet, cUrl, cBody ) //ok
ENDCLASS
************************************************************************************************************************************************************************************************************************
METHOD Setup(sBanco, nEnviroment, sCertificado, sClient_Id, sClient_Secret, sPIX_Key, sPIX_Receive, sPIX_City, sPIX_Amount, sPIX_Description, sPIX_Identification, sPDF_Linha1, sPDF_linha2) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL oBanco;
::txid := create_Txid( )
::BANCO := IF(sBanco == Nil, "000", sBanco)
::ENVIROMENT_TYPE := IF(nEnviroment == Nil, 0, nEnviroment)
::MY_CERTIFICATE := IF(sCertificado == Nil, "", sCertificado)
::CLIENT_ID := IF(sClient_Id == Nil,"", sClient_Id)
::CLIENT_SECRET := IF(sClient_Secret == Nil,"", sClient_Secret)
::cPIX_KEY := IF(sPIX_Key == Nil,"", sPIX_Key)
::cPIX_RECEIVER := IF(sPIX_Receive == Nil,"", sPIX_Receive)
::cPIX_CITY := IF(sPIX_City == Nil,"", sPIX_City)
::nPIX_AMOUNT := IF(sPIX_Amount == Nil,"0.00", sPIX_Amount)
::cPIX_DESCRIPTION := IF(sPIX_Description == Nil,"", sPIX_Description)
::cPIX_IDENTIFICATION := IF(sPIX_Identification == Nil,"", sPIX_Identification)
::cPDF_Linha1 := IF(sPDF_Linha1 == Nil,"", sPDF_Linha1)
::cPDF_Linha2 := IF(sPDF_Linha2 == Nil,"", sPDF_Linha2)
::cFileName:="C:\filesold\report\PIX_" + strzero(HB_RandomInt(1, 99999999),8,0) +'.PDF'
FOR EACH oBanco IN hb_jsonDecode( CARREGA_RESOURCE( "BANCOS" ) )
IF oBanco["value"] == ::BANCO
IF ::ENVIROMENT_TYPE = 1
::ROUTE_BASE := oBanco["production"]
ELSE
::ROUTE_BASE := oBanco["homologation"]
ENDIF
ENDIF
NEXT
return Nil
************************************************************************************************************************************************************************************************************************
METHOD Execute( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
IF ::BANCO = "000"
::Pix_String( )
::PrintQRCode( )
ELSE
::Authorize( )
::pix_create_charge( ) // ou ::pix_create_immediate_charge( )
::pix_generate_QRCode( )
::PrintQRCode( )
endif
return Nil
************************************************************************************************************************************************************************************************************************
METHOD Consulta( sBanco, nEnviroment, sCertificado, sClient_Id, sClient_Secret, stxid) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
::BANCO := IF(sBanco == Nil, "000", sBanco)
::ENVIROMENT_TYPE := IF(nEnviroment == Nil, 0, nEnviroment)
::MY_CERTIFICATE := IF(sCertificado == Nil, "", sCertificado)
::CLIENT_ID := IF(sClient_Id == Nil,"", sClient_Id)
::CLIENT_SECRET := IF(sClient_Secret == Nil,"", sClient_Secret)
::txid := IF(stxid == Nil,"", stxid)
IF ::BANCO != "000"
::Authorize( )
::pix_detail_charge( )
endif
return Nil
************************************************************************************************************************************************************************************************************************
METHOD Authorize( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
IF ::MY_CERTIFICATE != NIL
cURL := ::ROUTE_BASE + "/oauth/token?grantType=client_credentials"
cURL +="&client_id=" + ::CLIENT_ID
cURL +="&client_secret=" + ::CLIENT_SECRET
cRetorno := ::WS( "POST", cUrl, [{"grant_type": "client_credentials"}] )
if ::erro_status = WS_OK
::access_token := hb_jsonDecode( cRetorno )["access_token"]
if EMPTY(::access_token)
::erro_status := WS_ERROR
::erro_nome := 'access_token:'
::erro_mensagem := cRetorno
ENDIF
ENDIF
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD pix_config_webhook( chave ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
IF ::access_token != NIL
cURL := ::ROUTE_BASE + "/webhook/:" + chave
cRetorno := ::WS( "PUT", cUrl, '[{"webhookUrl": "' + 'https://exemplo-pix/webhook' + '""}]' )
ENDIF
return cRetorno
************************************************************************************************************************************************************************************************************************
METHOD pix_get_webhook( chave ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
IF ::access_token != NIL
cURL := ::ROUTE_BASE + "/webhook/:" + chave
cRetorno := ::WS( "GET", cUrl, "" )
ENDIF
return cRetorno
************************************************************************************************************************************************************************************************************************
METHOD pix_list_webhook( data_Inicio, data_Fim ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
IF ::access_token != NIL
cURL := ::ROUTE_BASE + "/webhook/"
cURL += "?inicio=" + data_Inicio
cURL += "?fim=" + data_Fim
cRetorno := ::WS( "GET", cUrl, "")
ENDIF
return cRetorno
************************************************************************************************************************************************************************************************************************
METHOD pix_delete_webhook( chave ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
IF ::access_token != NIL
cURL := ::ROUTE_BASE + "/webhook/:" + chave
cRetorno := ::WS( "DELETE", cUrl, "" )
ENDIF
return cRetorno
************************************************************************************************************************************************************************************************************************
METHOD pix_create_charge( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cBody;
, cURL;
, cRetorno;
, hRetorno;
if ::erro_status = WS_OK
cBody:= '{'
cBody+= '"calendario": {'
cBody+= '"expiracao": ' + ltrim(str(::expires_in))
cBody+= '},'
cBody+= '"valor": {'
cBody+= '"original": "' + ::nPIX_AMOUNT + '"'
cBody+= '},'
cBody+= '"chave": "' + ::cPIX_KEY + '",'
cBody+= '"solicitacaoPagador": "' + ::cPIX_IDENTIFICATION + '"'
cBody+= '}'
cURL := ::ROUTE_BASE + "/cob/" + ::txid
cRetorno := ::WS( "PUT", cUrl, cBody )
if ::erro_status = WS_OK
hRetorno:=hb_jsonDecode( cRetorno )
if Hb_HHasKey( hRetorno, "loc" )
::id := hRetorno["loc"]["id"]
::location := hRetorno["loc"]["location"]
endif
endif
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD pix_create_immediate_charge( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cBody;
, cURL;
, cRetorno;
, hRetorno;
if ::erro_status = WS_OK
cBody:= '{'
cBody+= '"calendario": {'
cBody+= '"expiracao": ' + ltrim(str(::expires_in))
cBody+= '},'
// cBody+= '"devedor": {'
// cBody+= '"cpf": "' + "13826018249" + '",'
// cBody+= '"cnpj": "' + "05597127000153" + '",'
// cBody+= '"nome": "' + 'NOME DO SEU CLIENTE' + '"'
// cBody+= '},'
cBody+= '"valor": {'
cBody+= '"original": "' + ::nPIX_AMOUNT + '"'
cBody+= '},'
cBody+= '"chave": "' + ::cPIX_KEY + '",'
cBody+= '"solicitacaoPagador": "' + ::cPIX_IDENTIFICATION + '"'
cBody+= '}'
cURL := ::ROUTE_BASE + "/cob"
cRetorno := ::WS( "POST", cUrl, cBody )
if ::erro_status = WS_OK
hRetorno:=hb_jsonDecode( cRetorno )
if Hb_HHasKey( hRetorno, "txid" )
::txid :=hRetorno["txid"]
::id := hRetorno["loc"]["id"]
::location := hRetorno["loc"]["location"]
ENDIF
endif
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD pix_detail_charge( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
, hRetorno;
if ::erro_status = WS_OK
cURL := ::ROUTE_BASE + "/cob/" + ::txid
cRetorno := ::WS( "GET", cUrl, "" )
if ::erro_status = WS_OK
hRetorno:=hb_jsonDecode( cRetorno )
if Hb_HHasKey( hRetorno, "status" )
::status:=hRetorno["status"]
::e2eid:=hRetorno["pix"]["endToEndId"]
endif
endif
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD pix_update_charge( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cBody;
, cURL;
, cRetorno;
, hRetorno;
if ::erro_status = WS_OK
cBody:= '{'
cBody+= '"valor": {'
cBody+= '"original": "' + ::nPIX_AMOUNT + '"'
cBody+= '}'
cBody+= '}'
cURL := ::ROUTE_BASE + "/cob/" + ::txid
cRetorno := ::WS( "PATCH", cUrl, cBody )
if ::erro_status = WS_OK
hRetorno:=hb_jsonDecode( cRetorno )
if Hb_HHasKey( hRetorno, "txid" )
::txid :=hRetorno["txid"]
::id := hRetorno["loc"]["id"]
::location := hRetorno["loc"]["location"]
ENDIF
endif
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD pix_list_charges( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno;
, hRetorno;
if ::erro_status = WS_OK
cURL := ::ROUTE_BASE + "/cob"
cURL += '?inicio=' + ::data_Inicio
cURL += '&fim=' + ::data_Fim
cRetorno := ::WS( "GET", cUrl, "" )
if ::erro_status = WS_OK
hRetorno:=hb_jsonDecode( cRetorno )
if Hb_HHasKey( hRetorno, "ListaDeCobranca" )
::ListaDeCobranca:=hRetorno["ListaDeCobranca"]
endif
endif
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD pix_generate_QRCode( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cURL;
, cRetorno, hRetorno;
, cBody:="";
if ::erro_status = WS_OK
cURL := ::ROUTE_BASE + "/loc/" + LTRIM(STR(::id,10,0)) + "/qrcode"
cRetorno := ::WS( "GET", cUrl, cBody )
if ::erro_status = WS_OK
hRetorno:=hb_jsonDecode( cRetorno )
if Hb_HHasKey( hRetorno, "qrcode" )
::qrcode :=hRetorno["qrcode"]
// ::qrcodeImage := hRetorno["qrcodeImage"]
::linkVisualizacao := hRetorno["linkVisualizacao"]
ENDIF
endif
ENDIF
return Nil
************************************************************************************************************************************************************************************************************************
METHOD WS( cPostGet, cUrl, cBody ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL oServer;
, cRetorno;
, hRetorno;
hb_Default( @cPostGet, "POST" )
hb_Default( @cBody, "" )
::erro_status := WS_OK
::erro_nome := ""
::erro_mensagem := ""
BEGIN SEQUENCE WITH {|o| break(o)}
oServer := Win_OleCreateObject( "MSXML2.ServerXMLHTTP.6.0" )
RECOVER
::erro_status := WS_ERROR
::erro_nome := 'Erro na Criação do Serviço:'
::erro_mensagem := 'MSXML2.ServerXMLHTTP.6.0'
RETURN ""
END SEQUENCE
BEGIN SEQUENCE WITH {|o| break(o)}
oServer:Open( cPostGet, cUrl, .F. )
RECOVER
::erro_status := WS_ERROR
::erro_nome := 'Erro na abertura do Serviço:'
::erro_mensagem := 'OPEN'
RETURN ""
END SEQUENCE
IF Empty(::access_token )
oServer:setOption( 3, "CURRENT_USER\MY\" + ::MY_CERTIFICATE )
oServer:SetRequestHeader( "Authorization", "Basic " + hb_base64Encode( ::CLIENT_ID + ":" + ::CLIENT_SECRET ) )
oServer:SetRequestHeader( "Content-Type", "application/json" )
ELSE
oServer:SetRequestHeader( "Content-Type", "application/json" )
oServer:SetRequestHeader( "Authorization", "Bearer " + ::access_token )
ENDIF
BEGIN SEQUENCE WITH {|o| break(o)}
oServer:Send( cBody )
RECOVER
::erro_status := WS_ERROR
::erro_nome := ':API PIX'
::erro_mensagem := 'Erro na Coneccão com o Serviço'
RETURN Nil
END SEQUENCE
oServer:WaitForResponse( ::expires_in )
cRetorno := oServer:ResponseBody()
hRetorno:=hb_jsonDecode( cRetorno )
IF EMPTY(cRetorno)
::erro_status := WS_ERROR
::erro_nome := 'API PIX'
::erro_mensagem := 'Retorno de Dados Vazio'
else
if Hb_HHasKey( hRetorno, "mensagem" )
::erro_status := WS_ERROR
::erro_nome := hRetorno["nome"]
::erro_mensagem := hRetorno["mensagem"]
endif
ENDIF
RETURN cRetorno
************************************************************************************************************************************************************************************************************************
METHOD Pix_String( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL cString
cString := ""
cString += get_p_value('00', '01')
// cString += get_p_value('01', '11')
cString += get_p_account_information(::cPIX_KEY, ::cPIX_RECEIVER, ::pix_url)
cString += get_p_value('52', '0000')
cString += get_p_value('53', '986')
cString += get_p_value('54', ::nPIX_AMOUNT)
cString += get_p_value('58', 'BR')
cString += get_p_value('59', SUBSTR(::cPIX_DESCRIPTION,1,25))
cString += get_p_value('60', ::cPIX_CITY)
** cString += get_p_value('61', '')
cString += get_p_additional_data_field(::cPIX_IDENTIFICATION)
cString += "6304"
cString += hb_numtohex(hb_crcct(cString, 0xFFFF, 0x11021))
::qrcode := cString
return Nil
************************************************************************************************************************************************************************************************************************
STATIC function get_p_value(identify, value)
************************************************************************************************************************************************************************************************************************
return trim(identify + strzero(len(alltrim(value)), 2) + value)
************************************************************************************************************************************************************************************************************************
STATIC function get_p_account_information(key, description, url)
************************************************************************************************************************************************************************************************************************
Local base_pix := get_p_value('00', 'br.gov.bcb.pix')
Local info_string := ''
if key != Nil
info_string += get_p_value('01', key)
elseif url != Nil
info_string += get_p_value('25', url)
endif
info_string += get_p_value('02', description)
return get_p_value('26', base_pix + info_string)
************************************************************************************************************************************************************************************************************************
STATIC function get_p_additional_data_field(identification)
************************************************************************************************************************************************************************************************************************
if identification != Nil
return get_p_value('62', get_p_value('05', identification))
endif
return get_p_value('62', get_p_value('05', '***'))
************************************************************************************************************************************************************************************************************************
FUNCTION create_Txid( )
************************************************************************************************************************************************************************************************************************
Local cRetorno:="";
, nCont;
, equivalente:= {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',;
'0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y',;
'Z','0','1','2','3','4','5','6','7','8','9'}
For nCont:=1 to 32
cRetorno += equivalente[HB_RandomInt(1, 62)]
next
return cRetorno
******************************************************************************************************************************
METHOD PDF_QRCode( ) CLASS PIXClass
******************************************************************************************************************************
LOCAL h1, iPDF, sPDF, hPDF, cFileName;
h1:=::qrcodeImage
iPDF:= Array( 1, 1 )
iPDF[1, 1] := h1
sPDF:= hb_base64Decode( iPDF[1, 1] )
cFileName:="C:\filesold\report\PIX_" + strzero(HB_RandomInt(1, 99999999),8,0) +'.PDF'
hPDF := FCreate(cFileName, 0)
FWrite(hPDF, sPDF)
FClose(hPDF)
WAPI_ShellExecute( NIL, "OPEN", cFileName ,"",NIL,5 )
RETURN Nil
************************************************************************************************************************************************************************************************************************
METHOD PrintQRCode( ) CLASS PIXClass
************************************************************************************************************************************************************************************************************************
LOCAL oPDF;
, nTamValor;
if ::erro_status = WS_OK
oPDF := PDFClass()
IF oPDF == NIL
hwg_msginfo("Falha da criação do objeto PDF","ATENÇÃO!!!")
RETURN .F.
ENDIF
oPDF:cFileName := ::cFileName
oPDF:SetType( PDFCLASS_ROLLS )
oPDF:nPageHeight := 200
oPDF:Begin()
oPDF:AddPage()
nTamValor := (38 - (len("Valor do PIX: " + LTRIM(TRANSFORM(::nPIX_AMOUNT,"@E 999,999,999.99" ))))) /2
oPDF:DrawText( 0 , 8, "Leia o QR Code Pix e pague" , Nil, 8, "Helvetica-Bold", Nil, Nil )
oPDF:DrawText( 0.7, 5, "utilizando o aplicativo do seu banco." , Nil, 8, "Helvetica-Bold", Nil, Nil )
oPDF:DrawBarcodeQRCode( 1, 5, 0.25, ::qrcode )
oPDF:DrawText( 13.3 , 2.5, ::cPDF_Linha1, Nil, 8, "Helvetica-Bold", 90, Nil )
oPDF:DrawText( 13.3 , 35 , ::cPDF_Linha2, Nil, 8, "Helvetica-Bold", 90, Nil )
oPDF:DrawText( 14.5, nTamValor , "Valor do PIX: " + LTRIM(TRANSFORM(VAL(::nPIX_AMOUNT),"@E 999,999,999.99" )), Nil, 10, "Helvetica-Bold", Nil, Nil )
oPDF:DrawLine( 15, 0, 15, 38, 3 )
oPDF:End( )
oPDF:PrintPreview( )
ENDIF
RETURN NIL