Payment terminal TIMAPI.DLL
Moderator: Rathinagiri
Re: Payment terminal TIMAPI.DLL
Hello Serge
I don't know how to incorporate it into HMG.
I am open to any solution.
I was thinking about whether to solve it by switching to MiniGUI, I assume that it uses libraries of the "lib" type.
The source code in C language is available and I thought it would not be a problem to create a library of type "a".
The post has over 900 views, but I don't see any interest except for you, Edward, Daniel, Jimmy (AUGE_OHR). I've gotten used to n-questions within HMG going unanswered. I know there are many smart people here who have devoted a lot of their time to HMG, I'm just afraid they have left this forum.
Question for Grigory (Filatov), is it possible to use the supplied libraries in MiniGUI
Thanks everyone for your help.
Best regards, Georg
I don't know how to incorporate it into HMG.
I am open to any solution.
I was thinking about whether to solve it by switching to MiniGUI, I assume that it uses libraries of the "lib" type.
The source code in C language is available and I thought it would not be a problem to create a library of type "a".
The post has over 900 views, but I don't see any interest except for you, Edward, Daniel, Jimmy (AUGE_OHR). I've gotten used to n-questions within HMG going unanswered. I know there are many smart people here who have devoted a lot of their time to HMG, I'm just afraid they have left this forum.
Question for Grigory (Filatov), is it possible to use the supplied libraries in MiniGUI
Thanks everyone for your help.
Best regards, Georg
Re: Payment terminal TIMAPI.DLL
Unfortunately, I have not received any documentation from WorldLine so far. But:
- I was able to reverse engineer the algorithm to calculate the correct SIXml protocol magic number.
- listening to TCP packets I was able to extract some syntaxes of SIXml.
- I can establish a connection with a payment terminal (simulator)
- Get status feature
- login to the terminal
- read information about the application
- read information about the card inserted into the payment terminal.
I have prepared sources for communication regarding the above features.
I saved the data sent to the terminal in raw form, I did not have time to analyze the meaning of individual variables, except for those whose interpretation of their meaning did not raise any doubts. I focused on getting the correct communication between HMG and the payment terminal simulator.
The R.E. process is quite a tedious job, if you are interested I can send you the methodology I use, you can try to take the project further.
- I was able to reverse engineer the algorithm to calculate the correct SIXml protocol magic number.
- listening to TCP packets I was able to extract some syntaxes of SIXml.
- I can establish a connection with a payment terminal (simulator)
- Get status feature
- login to the terminal
- read information about the application
- read information about the card inserted into the payment terminal.
I have prepared sources for communication regarding the above features.
Code: Select all
#include "hmg.ch"
Function Main
nTimeOut := 300
ipadres := '127.0.0.1'
ipport := 7784
nSequenceNumber := 0
DEFINE WINDOW main AT 0 , 0 WIDTH 800 HEIGHT 500 TITLE "ECR/POS - EFT" MAIN
DEFINE MAIN MENU
DEFINE POPUP "Test"
MENUITEM "Run test" ACTION test()
END POPUP
END MENU
DEFINE RICHEDITBOX log_
ROW 10
COL 10
WIDTH 500
HEIGHT 420
VALUE ""
TOOLTIP ""
FONTBOLD .F.
FONTITALIC .F.
FONTUNDERLINE .F.
FONTSTRIKEOUT .F.
HELPID Nil
TABSTOP .T.
VISIBLE .T.
READONLY .T.
END RICHEDITBOX
DEFINE RICHEDITBOX EFTDisplay_
ROW 10
COL 520
WIDTH 250
HEIGHT 200
VALUE ""
TOOLTIP ""
FONTNAME "Courier"
FONTSIZE 14
FONTBOLD .F.
FONTITALIC .F.
FONTUNDERLINE .F.
FONTSTRIKEOUT .F.
HELPID Nil
TABSTOP .T.
VISIBLE .T.
READONLY .T.
END RICHEDITBOX
DEFINE RICHEDITBOX EFTPrinter_
ROW 230
COL 520
WIDTH 250
HEIGHT 200
VALUE ""
TOOLTIP ""
FONTNAME "Courier"
FONTSIZE 14
FONTBOLD .F.
FONTITALIC .F.
FONTUNDERLINE .F.
FONTSTRIKEOUT .F.
HELPID Nil
TABSTOP .T.
VISIBLE .T.
READONLY .T.
END RICHEDITBOX
END WINDOW
CENTER WINDOW main
ACTIVATE WINDOW main
RETURN
Procedure test
addlog ( ">> Initialization" )
socket := TIM_init ( ipadres, ipport, nTimeOut )
IF !hb_InetIsSocket ( socket )
addlog ( "Connection failed" )
Return
ENDIF
addlog ( ">> Get Feature Request" )
IF !TIM_Send ( socket, TIM_GetFeatureRequest ( nSequenceNumber ) )
addlog ("Failed to send Feature Request request")
return
ENDIF
cResp := TIM_Recv ( socket, 10 /* nTimeOut */ )
strfile ( cResp, "resp1.xml" )
oXMLFile := objXML ( cResp )
DisplayEFTMessage ( oXMLFile )
hStatus := GetStatusInfo ( oXMLFile )
LogStatusInfo ( hStatus )
IF hStatus [ "CardReaderStatus" ] == "CardInserted"
//show info about card
LogCardInfo ( oXMLFile )
ENDIF
cTerminalID := hStatus [ "TerminalID" ]
msginfo ( "Next step" )
addlog ( ">> Login" )
cPosId := "POS1234"
nSequenceNumber++
IF !TIM_Send ( socket, TIM_Login ( nSequenceNumber, cPosId ) )
addlog ("Failed to send login request")
return
ENDIF
cResp := TIM_Recv ( socket, 3 /* nTimeOut */ )
strfile ( cResp, "resp2.xml" )
oXMLFile := objXMLLoad ( oXMLFile, cResp )
DisplayEFTMessage ( oXMLFile )
addlog ( "Result Code: " + oXMLFile:documentElement:selectNodes( "//sixml:Response" ):item(0):getAttribute( "ResultCode" ) )
msginfo ( "Next step" )
addlog ( ">> Get Application Information" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_GetApplicationInformation ( nSequenceNumber ) )
addlog ("Failed to send Application Information request")
return
ENDIF
cResp := TIM_Recv ( socket, 10 /* nTimeOut */ )
strfile ( cResp, "resp3.xml" )
oXMLFile := objXMLLoad ( oXMLFile, cResp )
DisplayEFTMessage ( oXMLFile )
hStatus := GetStatusInfo ( oXMLFile )
LogStatusInfo ( hStatus )
IF hStatus [ "CardReaderStatus" ] == "CardInserted"
//show info about card
LogCardInfo ( oXMLFile )
ENDIF
msginfo ( "Next step" )
addlog ( ">> Close" )
TIM_Close ( socket )
RETURN
**********************************************
Function TIM_init ( cIPadres, nIPport, nTimeOut )
Local socket, nTCPIPErr
Local nTry := 1, nMaxTry := 10
IF ! hb_inetInit()
addlog ( "Inet could not be initialized!" )
RETURN Nil
ENDIF
socket := hb_inetCreate()
hb_inetTimeout( socket, nTimeOut )
DO WHILE nTry <= nMaxTry
nTry ++
hb_inetConnect( cIPadres, nIPport, socket )
nTCPIPErr := hb_inetErrorCode( socket )
IF nTCPIPErr = 0
EXIT
ENDIF
Inkey(.05)
ENDDO
IF nTCPIPErr # 0
addlog ( "Connection error. Error number:" + hb_valToStr ( nTCPIPErr ) + " " + hb_valToStr( hb_inetErrorDesc( socket ) ) )
hb_inetClose( socket )
RETURN Nil
ENDIF
RETURN socket
********************************************************
FUNCTION TIM_GetFeatureRequest ( nSequenceNumber )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Status" ' + ;
'Function="FeatureRequest" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '"/>'
RETURN cSIXml
********************************************************
FUNCTION TIM_GetApplicationInformation ( nSequenceNumber )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Status" ' + ;
'Function="ApplicationInformation" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '"/>'
RETURN cSIXml
********************************************************
FUNCTION TIM_Login ( nSequenceNumber, cPosID )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Admin" ' + ;
'Function="Login" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '">' + ;
'<sixml:PosId>' + cPosID + '</sixml:PosId>' + ;
'<sixml:IntegratorId>0</sixml:IntegratorId>' +;
'<sixml:ProtocolOptionList>' +;
'<sixml:ProtocolOption OptionType="sixml:ProtocolLevel">3</sixml:ProtocolOption>' +;
'<sixml:ProtocolOption OptionType="sixml:Guides">1</sixml:ProtocolOption>' +;
'<sixml:ProtocolOption OptionType="sixml:AutoCommit">0</sixml:ProtocolOption>' +;
'</sixml:ProtocolOptionList>' + ;
'<sixml:PrintOptionList>'+ ;
'<sixml:PrintOptions Recipient="Merchant" PrintFormat="Normal" PrintWidth="40" PrintFlags="0"/>' +;
'<sixml:PrintOptions Recipient="Cardholder" PrintFormat="Normal" PrintWidth="40" PrintFlags="0"/>' +;
'</sixml:PrintOptionList><sixml:ManufacturerFlags>0</sixml:ManufacturerFlags></sixml:Request>'
RETURN cSIXml
********************************************************
FUNCTION TIM_Send ( socket, cPakiet )
Local nTry := 1, nMaxTry := 3, xSIXml
IF !hb_InetIsSocket ( socket )
addlog ( "Invalid socket" )
Return .F.
ENDIF
cPakiet := TIM_Magic_numer ( cPakiet ) + cPakiet
strfile ( cPakiet, "pakiet.txt" )
DO WHILE nTry <= nMaxTry
hb_inetFD( socket )
IF hb_inetSendAll( socket, cPakiet ) == Len ( cPakiet )
RETURN .T.
ENDIF
//podejmij kolejna próbę
nTry ++
ENDDO
RETURN .F.
**************************************************************
FUNCTION TIM_Recv ( socket, nTimeOut )
Local cBuf, nBuf, nStartClock := Seconds(), cResponse := ""
Default nTimeOut := 10
IF !hb_InetIsSocket ( socket )
addlog ( "Invalid socket" )
Return ""
ENDIF
DO While .T.
nLength := TIM_isRespSIXml ( socket )
cBuf := SPACE ( nLength )
nBuf := hb_inetRecvAll( socket, @cBuf, Len( cBuf ) )
cResponse += Left ( cBuf, nBuf )
IF !Empty( cResponse )
RETURN cResponse
ENDIF
IF Seconds() - nStartClock >= nTimeOut .And. nBuf == -1 //osiągnięto TimeOut
addlog ( "Timeout has been reached waiting for a response." )
RETURN cResponse
ENDIF
ENDDO
RETURN
******************************************************************
FUNCTION TIM_Close ( socket )
hb_inetClose( socket )
hb_inetCleanup()
RETURN
**************************************************************
FUNCTION TIM_isRespSIXml ( socket, nTimeOut )
Local cBuf, nBuf, nStartClock := Seconds(), cResponse := ""
Local nSIXmlMagicLen := 9
Local cPrefix := "SIXml"
Local nTransportLayerVersion := 0x02
Local nReceiveChannel := 0x01
Local nRemainder, nInteger, nDivider := 256
Local nLength := 1024
Default nTimeOut := 3
IF !hb_InetIsSocket ( socket )
addlog ( "Invalid socket" )
Return Nil
ENDIF
DO While .T.
cBuf := " " //SPACE ( Len ( nSIXmlMagicLen ) )
nBuf := hb_inetRecvAll( socket, @cBuf, Len( cBuf ) )
cResponse += Left ( cBuf, nBuf )
IF Len ( cResponse ) == nSIXmlMagicLen
IF Left ( cResponse, Len ( cPrefix ) ) == cPrefix .And. ;
SubStr ( cResponse, Len ( cPrefix ) + 1, 1 ) == Chr ( nTransportLayerVersion ) .And. ;
Right ( cResponse, 1 ) == Chr ( nReceiveChannel ) //jest odpowiedz SIXmlMagicHeader
nInteger := Asc ( SubStr ( cResponse, Len ( cPrefix ) + 2, 1 ) )
nRemainder := Asc ( SubStr ( cResponse, Len ( cPrefix ) + 3, 1 ) )
nLength := (( nInteger * nDivider ) + nRemainder ) - 1
Return nLength
ENDIF
ENDIF
IF Seconds() - nStartClock >= nTimeOut //osiągnięto TimeOut
addlog ( "Timeout has been reached waiting for a response." )
RETURN nLength
ENDIF
ENDDO
RETURN nLength
******************************************************************
FUNCTION TIM_Magic_numer ( cPakiet )
Local cPrefix := "SIXml"
Local nTransportLayerVersion := 0x02
Local nReceiveChannel := 0x01
Local nRemainder, nInteger, nDivider := 256
Local nLength := Len ( cPakiet ) + 1
nInteger := INT ( nLength / nDivider )
nRemainder := ( nLength % nDivider )
RETURN cPrefix + Chr ( nTransportLayerVersion ) + Chr ( nInteger ) + Chr ( nRemainder ) + Chr ( nReceiveChannel )
******************************************************************************
Static Function objXML ( cXMLString )
Local oMyErr, oXMLFile := Win_OleCreateObject( "Msxml2.DOMDocument.3.0" )
oXMLFile:async := .F.
oXMLFile:setProperty("SelectionLanguage", "XSLPattern") // https://www.codemag.com/article/0102051/XSL-Patterns
Return objXMLLoad ( oXMLFile, cXMLString )
**************************************************************
Static Function objXMLLoad ( oXMLFile, cXMLString )
Local oMyErr
oXMLFile:loadXML ( cXMLString )
IF oXMLFile:parseError:errorCode != 0
oMyErr := oXMLFile:parseError
addlog ("You have error " + oMyErr:reason)
RETURN Nil
ENDIF
RETURN oXMLFile
*****************************************************************
Function DisplayEFTMessage ( oXMLFile )
Local oNodeList := oXMLFile:documentElement:selectNodes( "//sixml:DisplayLine" )
Local oOneElement
IF oNodeList:length > 0 //Show the received message
Main.EFTDisplay_.Value := ""
FOR EACH oOneElement IN oNodeList
main.EFTDisplay_.AddText ( LEN( main.EFTDisplay_.Value) ) := oOneElement:text + CRLF
NEXT
ENDIF
RETURN
***********************************
Procedure LogStatusInfo ( hStatus )
addlog ( "Card Reader Status: " + hStatus [ "CardReaderStatus" ] )
addlog ( "Transaction Status: " + hStatus [ "TransactionStatus" ] )
addlog ( "Connection Status: " + hStatus [ "ConnectionStatus" ] )
addlog ( "Management Status: " + hStatus [ "ManagementStatus" ] )
addlog ( "Receipt Information: " + hStatus [ "ReceiptInformation" ] )
addlog ( "Terminal ID: " + hStatus [ "TerminalID" ] )
RETURN Nil
***********************************
Function GetStatusInfo ( oXMLFile )
Local hStatus := { "CardReaderStatus" => oXMLFile:documentElement:selectNodes( "//sixml:CardReaderStatus" ):item(0):text, ;
"TransactionStatus" => oXMLFile:documentElement:selectNodes( "//sixml:TransactionStatus" ):item(0):text, ;
"ConnectionStatus" => oXMLFile:documentElement:selectNodes( "//sixml:ConnectionStatus" ):item(0):text, ;
"ManagementStatus" => oXMLFile:documentElement:selectNodes( "//sixml:ManagementStatus" ):item(0):text, ;
"ReceiptInformation" => oXMLFile:documentElement:selectNodes( "//sixml:ReceiptInformation" ):item(0):text, ;
"TerminalID" => oXMLFile:documentElement:selectNodes( "//sixml:TerminalId" ):item(0):text }
RETURN hStatus
*************************************
Procedure LogCardInfo ( oXMLFile )
Local oCardData := oXMLFile:documentElement:selectNodes( "//sixml:CardData" ):item(0):attributes
Local oOneAttribute := nil
IF oCardData:length > 0
addlog ( Space ( 5 ) + "Information about of inserted card:" )
FOR EACH oOneAttribute IN oCardData
addlog ( Space ( 10 ) + oOneAttribute:name + ": " + oOneAttribute:text )
NEXT
addlog ( Space ( 10 ) + "Language: " + oXMLFile:documentElement:selectNodes( "//sixml:CardData/sixml:Language" ):item(0):text )
ENDIF
RETURN
*******************
FUNCTION addlog( cTxt )
main.log_.AddText ( LEN( main.log_.Value) ) := cTxt + CRLF
DO events
RETURN nil
- serge_girard
- Posts: 3309
- Joined: Sun Nov 25, 2012 2:44 pm
- DBs Used: 1 MySQL - MariaDB
2 DBF - Location: Belgium
- Contact:
Re: Payment terminal TIMAPI.DLL
Some improvements, some new events:
Code: Select all
#include "hmg.ch"
Function Main
nTimeOut := 300
ipadres := '127.0.0.1'
ipport := 7784
nSequenceNumber := 0
DEFINE WINDOW main AT 0 , 0 WIDTH 800 HEIGHT 500 TITLE "ECR/POS - EFT" MAIN
DEFINE MAIN MENU
DEFINE POPUP "Test"
MENUITEM "Run test" ACTION test()
END POPUP
END MENU
DEFINE RICHEDITBOX log_
ROW 10
COL 10
WIDTH 500
HEIGHT 420
VALUE ""
TOOLTIP ""
FONTBOLD .F.
FONTITALIC .F.
FONTUNDERLINE .F.
FONTSTRIKEOUT .F.
HELPID Nil
TABSTOP .T.
VISIBLE .T.
READONLY .T.
END RICHEDITBOX
DEFINE Label Display
ROW 10
COL 520
WIDTH 250
VALUE "DISPLAY"
END Label
DEFINE RICHEDITBOX EFTDisplay_
ROW 50
COL 520
WIDTH 250
HEIGHT 170
VALUE ""
TOOLTIP ""
FONTNAME "Courier"
FONTSIZE 14
FONTBOLD .F.
FONTITALIC .F.
FONTUNDERLINE .F.
FONTSTRIKEOUT .F.
HELPID Nil
TABSTOP .T.
VISIBLE .T.
READONLY .T.
END RICHEDITBOX
DEFINE Label Printer
ROW 230
COL 520
WIDTH 250
VALUE "PRINTER"
END Label
DEFINE RICHEDITBOX EFTPrinter_
ROW 260
COL 520
WIDTH 250
HEIGHT 170
VALUE ""
TOOLTIP ""
FONTNAME "Courier"
FONTSIZE 14
FONTBOLD .F.
FONTITALIC .F.
FONTUNDERLINE .F.
FONTSTRIKEOUT .F.
HELPID Nil
TABSTOP .T.
VISIBLE .T.
READONLY .T.
END RICHEDITBOX
END WINDOW
CENTER WINDOW main
ACTIVATE WINDOW main
RETURN
Procedure test
cPosId := "POS1234"
cUserID := "0"
oXMLFile := objXML ( '' )
hStatus := { "CardReaderStatus" => "", "TransactionStatus" => "", "ConnectionStatus" => "", "ManagementStatus" => "", "ReceiptInformation" => "", "TerminalID" => "" }
addlog ( ">> Initialization, Connect" )
socket := TIM_init ( ipadres, ipport, nTimeOut )
IF !hb_InetIsSocket ( socket )
addlog ( "Connection failed" )
Return
ENDIF
TIM_RecvAll ( socket )
addlog ( ">> Get Feature Request" )
IF !TIM_Send ( socket, TIM_GetFeatureRequest ( nSequenceNumber ) )
addlog ("Failed to send Feature Request request")
return
ENDIF
TIM_RecvAll ( socket )
cTerminalID := hStatus [ "TerminalID" ]
msginfo ( "Next step" )
addlog ( ">> Login" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_Login ( nSequenceNumber, cPosId ) )
addlog ("Failed to send login request")
return
ENDIF
TIM_RecvAll ( socket )
msginfo ( "Next step" )
addlog ( ">> Get Application Information" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_GetApplicationInformation ( nSequenceNumber ) )
addlog ("Failed to send Application Information request")
return
ENDIF
TIM_RecvAll ( socket )
msginfo ( "Next step" )
addlog ( ">> Put System Information" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_PutSystemInformation ( nSequenceNumber ) )
addlog ("Failed to send System Information")
return
ENDIF
TIM_RecvAll ( socket )
msginfo ( "Next step" )
addlog ( ">> Activate" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_Activate ( nSequenceNumber, cUserID ) )
addlog ("Failed to send Activate request")
return
ENDIF
TIM_RecvAll ( socket )
msginfo ( "Next step" )
addlog ( ">> DeActivate" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_DeActivate ( nSequenceNumber ) )
addlog ("Failed to send Deactivate request")
return
ENDIF
TIM_RecvAll ( socket )
msginfo ( "Next step" )
addlog ( ">> Logout" )
nSequenceNumber++
IF !TIM_Send ( socket, TIM_Logout ( nSequenceNumber ) )
addlog ("Failed to send logout request")
return
ENDIF
TIM_RecvAll ( socket )
msginfo ( "Next step" )
addlog ( ">> Close" )
Main.EFTDisplay_.Value := ""
TIM_Close ( socket )
RETURN
**********************************************
Function TIM_init ( cIPadres, nIPport, nTimeOut )
Local socket, nTCPIPErr
Local nTry := 1, nMaxTry := 10
IF ! hb_inetInit()
addlog ( "Inet could not be initialized!" )
RETURN Nil
ENDIF
socket := hb_inetCreate()
hb_inetTimeout( socket, nTimeOut )
DO WHILE nTry <= nMaxTry
nTry ++
hb_inetConnect( cIPadres, nIPport, socket )
nTCPIPErr := hb_inetErrorCode( socket )
IF nTCPIPErr = 0
EXIT
ENDIF
Inkey(.05)
ENDDO
IF nTCPIPErr # 0
addlog ( "Connection error. Error number:" + hb_valToStr ( nTCPIPErr ) + " " + hb_valToStr( hb_inetErrorDesc( socket ) ) )
hb_inetClose( socket )
RETURN Nil
ENDIF
RETURN socket
********************************************************
FUNCTION TIM_GetFeatureRequest ( nSequenceNumber )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Status" ' + ;
'Function="FeatureRequest" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '"/>'
RETURN cSIXml
********************************************************
FUNCTION TIM_GetApplicationInformation ( nSequenceNumber )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Status" ' + ;
'Function="ApplicationInformation" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '"/>'
RETURN cSIXml
********************************************************
FUNCTION TIM_PutSystemInformation ( nSequenceNumber )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Status" ' + ;
'Function="SystemInformation" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '">' + ;
'<sixml:EcrData>' + ;
'<sixml:EcrInfo EcrInfoType="EftAPI" ' + ;
'EcrInfoName="HMG TIM API" ' + ;
'EcrInfoManufacturerName="HMG community" ' + ;
'EcrInfoVers="3.20.0-2513" ' + ;
'EcrInfoArchitecture="harbour"/>' + ;
'<sixml:EcrInfo EcrInfoType="Os" ' + ;
'EcrInfoName="Microsoft Windows NT 6.2.9200.0" ' + ;
'EcrInfoVers="6.2.9200.0" ' + ;
'EcrInfoArchitecture="Win32NT"/>' + ;
'</sixml:EcrData></sixml:Request>'
RETURN cSIXml
********************************************************
FUNCTION TIM_Login ( nSequenceNumber, cPosID )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Admin" ' + ;
'Function="Login" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '">' + ;
'<sixml:PosId>' + cPosID + '</sixml:PosId>' + ;
'<sixml:IntegratorId>0</sixml:IntegratorId>' +;
'<sixml:ProtocolOptionList>' +;
'<sixml:ProtocolOption OptionType="sixml:ProtocolLevel">3</sixml:ProtocolOption>' +;
'<sixml:ProtocolOption OptionType="sixml:Guides">1</sixml:ProtocolOption>' +;
'<sixml:ProtocolOption OptionType="sixml:AutoCommit">0</sixml:ProtocolOption>' +;
'</sixml:ProtocolOptionList>' + ;
'<sixml:PrintOptionList>'+ ;
'<sixml:PrintOptions Recipient="Merchant" PrintFormat="Normal" PrintWidth="40" PrintFlags="0"/>' +;
'<sixml:PrintOptions Recipient="Cardholder" PrintFormat="Normal" PrintWidth="40" PrintFlags="0"/>' +;
'</sixml:PrintOptionList><sixml:ManufacturerFlags>0</sixml:ManufacturerFlags></sixml:Request>'
RETURN cSIXml
********************************************************
FUNCTION TIM_Logout ( nSequenceNumber, cPosID )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Admin" ' + ;
'Function="Logout" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '"/>'
RETURN cSIXml
********************************************************
FUNCTION TIM_Activate ( nSequenceNumber, cUserID )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Admin" ' + ;
'Function="Activate" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '">' + ;
'<sixml:PosId>' + cPosID + '</sixml:PosId>' + ;
'<sixml:UsrId>' + cUserID + '</sixml:UsrId></sixml:Request>'
RETURN cSIXml
********************************************************
FUNCTION TIM_DeActivate ( nSequenceNumber )
Local cSIXml := '<?xml version="1.0" encoding="UTF-8"?>' + ;
'<sixml:Request xmlns:sixml="http://www.worldline.com/" ' + ;
'FunctionGroup="Admin" ' + ;
'Function="Deactivate" ' + ;
'SequenceNumber="' + AllTrim( Str( nSequenceNumber ) ) + '"/>'
RETURN cSIXml
********************************************************
FUNCTION TIM_Send ( socket, cPakiet )
Local nTry := 1, nMaxTry := 3, xSIXml
IF !hb_InetIsSocket ( socket )
addlog ( "Invalid socket" )
Return .F.
ENDIF
cPakiet := TIM_Magic_numer ( cPakiet ) + cPakiet
strfile ( cPakiet, "pakiet.txt" )
DO WHILE nTry <= nMaxTry
hb_inetFD( socket )
IF hb_inetSendAll( socket, cPakiet ) == Len ( cPakiet )
RETURN .T.
ENDIF
//podejmij kolejna próbę
nTry ++
ENDDO
RETURN .F.
**************************************************************
FUNCTION TIM_Recv ( socket, nTimeOut )
Local cBuf, nBuf, nStartClock := Seconds(), cResponse := "", hResponse, nLength
Default nTimeOut := 10
IF !hb_InetIsSocket ( socket )
addlog ( "Invalid socket" )
Return ""
ENDIF
DO While .T.
hResponse := TIM_isRespSIXml ( socket )
nLength := hResponse [ "ResposeLength" ]
IF hResponse [ "IsSIXmlHeader" ] := .F.
cResponse += hResponse [ "ReceivedString" ]
ENDIF
cBuf := SPACE ( nLength )
nBuf := hb_inetRecvAll( socket, @cBuf, Len( cBuf ) )
cResponse += Left ( cBuf, nBuf )
IF !Empty( cResponse ) .OR. nBuf == -1
RETURN cResponse
ENDIF
IF Seconds() - nStartClock >= nTimeOut //osiągnięto TimeOut
addlog ( "Recv Timeout has been reached waiting for a response." )
RETURN cResponse
ENDIF
ENDDO
RETURN
******************************************************************
FUNCTION TIM_Close ( socket )
hb_inetClose( socket )
hb_inetCleanup()
RETURN
**************************************************************
FUNCTION TIM_isRespSIXml ( socket, nTimeOut )
Local cBuf, nBuf, nStartClock := Seconds(), cResponse := ""
Local nSIXmlMagicLen := 9
Local cPrefix := "SIXml"
Local nTransportLayerVersion := 0x02
Local nReceiveChannel := 0x01
Local nRemainder, nInteger, nDivider := 256
Local nLength := 1024
Default nTimeOut := 3
IF !hb_InetIsSocket ( socket )
addlog ( "Invalid socket" )
Return Nil
ENDIF
DO While .T.
cBuf := " " //SPACE ( Len ( nSIXmlMagicLen ) )
nBuf := hb_inetRecvAll( socket, @cBuf, Len( cBuf ) )
cResponse += Left ( cBuf, nBuf )
IF Len ( cResponse ) == nSIXmlMagicLen
IF Left ( cResponse, Len ( cPrefix ) ) == cPrefix .And. ;
SubStr ( cResponse, Len ( cPrefix ) + 1, 1 ) == Chr ( nTransportLayerVersion ) .And. ;
Right ( cResponse, 1 ) == Chr ( nReceiveChannel ) //jest odpowiedz SIXmlMagicHeader
nInteger := Asc ( SubStr ( cResponse, Len ( cPrefix ) + 2, 1 ) )
nRemainder := Asc ( SubStr ( cResponse, Len ( cPrefix ) + 3, 1 ) )
nLength := (( nInteger * nDivider ) + nRemainder ) - 1
Return { "ResposeLength" => nLength, "ReceivedString" => cResponse, "IsSIXmlHeader" => .T. }
ENDIF
Return { "ResposeLength" => nLength, "ReceivedString" => cResponse, "IsSIXmlHeader" => .F. }
ENDIF
IF nBuf = -1
Return { "ResposeLength" => nLength, "ReceivedString" => cResponse, "IsSIXmlHeader" => .F. }
ENDIF
IF Seconds() - nStartClock >= nTimeOut //osiągnięto TimeOut
addlog ( "Magic number Timeout has been reached waiting for a response." )
Return { "ResposeLength" => nLength, "ReceivedString" => cResponse, "IsSIXmlHeader" => .F. }
ENDIF
ENDDO
RETURN
******************************************************************
FUNCTION TIM_Magic_numer ( cPakiet )
Local cPrefix := "SIXml"
Local nTransportLayerVersion := 0x02
Local nReceiveChannel := 0x01
Local nRemainder, nInteger, nDivider := 256
Local nLength := Len ( cPakiet ) + 1
nInteger := INT ( nLength / nDivider )
nRemainder := ( nLength % nDivider )
RETURN cPrefix + Chr ( nTransportLayerVersion ) + Chr ( nInteger ) + Chr ( nRemainder ) + Chr ( nReceiveChannel )
******************************************************************************
Static Function objXML ( cXMLString )
Local oMyErr, oXMLFile := Win_OleCreateObject( "Msxml2.DOMDocument.3.0" )
oXMLFile:async := .F.
oXMLFile:setProperty("SelectionLanguage", "XSLPattern") // https://www.codemag.com/article/0102051/XSL-Patterns
IF Empty ( cXMLString )
Return oXMLFile
ENDIF
Return objXMLLoad ( oXMLFile, cXMLString )
**************************************************************
Static Function objXMLLoad ( oXMLFile, cXMLString )
Local oMyErr
oXMLFile:loadXML ( cXMLString )
IF oXMLFile:parseError:errorCode != 0
oMyErr := oXMLFile:parseError
addlog ("You have error " + oMyErr:reason)
RETURN Nil
ENDIF
RETURN oXMLFile
*****************************************************************
Function DisplayEFTMessage ( oXMLFile )
Local oNodeList := oXMLFile:documentElement:selectNodes( "//sixml:DisplayLine" )
Local oOneElement
IF oNodeList:length > 0 //Show the received message
Main.EFTDisplay_.Value := ""
FOR EACH oOneElement IN oNodeList
main.EFTDisplay_.AddText ( LEN( main.EFTDisplay_.Value) ) := oOneElement:text + CRLF
NEXT
ENDIF
RETURN
***********************************
Procedure LogStatusInfo ( hStatus )
addlog ( "Card Reader Status: " + hStatus [ "CardReaderStatus" ] )
addlog ( "Transaction Status: " + hStatus [ "TransactionStatus" ] )
addlog ( "Connection Status: " + hStatus [ "ConnectionStatus" ] )
addlog ( "Management Status: " + hStatus [ "ManagementStatus" ] )
addlog ( "Receipt Information: " + hStatus [ "ReceiptInformation" ] )
addlog ( "Terminal ID: " + hStatus [ "TerminalID" ] )
RETURN Nil
***********************************
Function GetStatusInfo ( oXMLFile )
IF oXMLFile:documentElement:selectNodes( "//sixml:CardReaderStatus" ):length > 0
hStatus [ "CardReaderStatus" ] := oXMLFile:documentElement:selectNodes( "//sixml:CardReaderStatus" ):item(0):text
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:TransactionStatus" ):length > 0
hStatus [ "TransactionStatus" ] := oXMLFile:documentElement:selectNodes( "//sixml:TransactionStatus" ):item(0):text
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:ConnectionStatus" ):length > 0
hStatus [ "ConnectionStatus" ] := oXMLFile:documentElement:selectNodes( "//sixml:ConnectionStatus" ):item(0):text
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:ManagementStatus" ):length > 0
hStatus [ "ManagementStatus" ] := oXMLFile:documentElement:selectNodes( "//sixml:ManagementStatus" ):item(0):text
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:ReceiptInformation" ):length > 0
hStatus [ "ReceiptInformation" ] := oXMLFile:documentElement:selectNodes( "//sixml:ReceiptInformation" ):item(0):text
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:TerminalId" ):length > 0
hStatus [ "TerminalID" ] := oXMLFile:documentElement:selectNodes( "//sixml:TerminalId" ):item(0):text
ENDIF
RETURN hStatus
*************************************
Procedure LogCardInfo ( oXMLFile )
Local oCardData := oXMLFile:documentElement:selectNodes( "//sixml:CardData" ):item(0):attributes
Local oOneAttribute := nil
Local oLanguage := oXMLFile:documentElement:selectNodes( "//sixml:CardData/sixml:Language" )
IF oCardData:length > 0
addlog ( Space ( 5 ) + "Information about of inserted card:" )
FOR EACH oOneAttribute IN oCardData
addlog ( Space ( 10 ) + oOneAttribute:name + ": " + oOneAttribute:text )
NEXT
IF oLanguage:length > 0
addlog ( Space ( 10 ) + "Language: " + oLanguage:item(0):text )
ENDIF
ENDIF
RETURN
*******************
Procedure LogResponseInfo ( oXMLFile )
Local oResponse := oXMLFile:documentElement:selectNodes( "//sixml:Response" ):item(0):attributes
Local oOneAttribute := nil
IF oResponse:length > 0
addlog ( Space ( 5 ) + "Response:" )
FOR EACH oOneAttribute IN oResponse
addlog ( Space ( 10 ) + oOneAttribute:name + ": " + oOneAttribute:text )
NEXT
ENDIF
RETURN
*******************
FUNCTION addlog( cTxt )
main.log_.AddText ( LEN( main.log_.Value) ) := cTxt + CRLF
DO events
RETURN nil
***********************************************
FUNCTION TIM_RecvAll ( socket )
Local cResp
STATIC nCount := 1
DO WHILE .T.
cResp := TIM_Recv ( socket, 3 /* nTimeOut */ )
IF Empty ( cResp )
EXIT
ENDIF
strfile ( cResp, "resp" + strzero ( nCount, 3) + ".xml" )
oXMLFile := objXMLLoad ( oXMLFile, cResp )
IF oXMLFile:documentElement:selectNodes( "//sixml:Notification" ):length > 0
hStatus := GetStatusInfo ( oXMLFile )
LogStatusInfo ( hStatus )
IF hStatus [ "CardReaderStatus" ] == "CardInserted"
//show info about card
LogCardInfo ( oXMLFile )
ENDIF
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:DisplayContent" ):length > 0
DisplayEFTMessage ( oXMLFile )
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:Response" ):length > 0
LogResponseInfo ( oXMLFile )
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:ReceiptHeader" ):length > 0
PrintEFTReceipt ( oXMLFile:documentElement:selectNodes( "//sixml:ReceiptHeader" ) )
ENDIF
IF oXMLFile:documentElement:selectNodes( "//sixml:PrintData/sixml:Receipt" ):length > 0
PrintEFTReceipt ( oXMLFile:documentElement:selectNodes( "//sixml:PrintData/sixml:Receipt" ) )
ENDIF
nCount ++
ENDDO
RETURN
*****************************************************************
Function PrintEFTReceipt ( oNodeList /* oXMLFile */ )
//Local oNodeList := oXMLFile:documentElement:selectNodes( "//sixml:ReceiptHeader" )
Local oOneElement
IF oNodeList:length > 0 //Print the received receipt
main.EFTPrinter_.AddText ( LEN( main.EFTPrinter_.Value) ) := CRLF
FOR EACH oOneElement IN oNodeList
main.EFTPrinter_.AddText ( LEN( main.EFTPrinter_.Value) ) := oOneElement:text + CRLF
NEXT
ENDIF
RETURN
Re: Payment terminal TIMAPI.DLL
Hi Edward.
Thank you very much for your help.
Great job.
I'm still on the road today. I will test tomorrow and let you know the result.
Best regards, Georg
Thank you very much for your help.
Great job.
I'm still on the road today. I will test tomorrow and let you know the result.
Best regards, Georg
- danielmaximiliano
- Posts: 2625
- Joined: Fri Apr 09, 2010 4:53 pm
- Location: Argentina
- Contact:
Re: Payment terminal TIMAPI.DLL
Hola Georg y Eduard
Recuerdo que para crear la libreria hbssl que esta en la carpeta contrib de debe tener los archivos .c fuente y .h (include)
ademas de los .dll y de esa manera crear la libreria libhbssl.
y colocar dicho archivo a la carpeta Include de HMG
Recuerdo que para crear la libreria hbssl que esta en la carpeta contrib de debe tener los archivos .c fuente y .h (include)
ademas de los .dll y de esa manera crear la libreria libhbssl.
se debe a que falta timapi.h que no la encuentro en ningun sitioundefined reference to `HB_FUN_TA_TERMINAL_GET_TIM_API_VERSION'

*´¨)
¸.·´¸.·*´¨) ¸.·*¨)
(¸.·´. (¸.·` *
.·`. Harbour/HMG : It's magic !
(¸.·``··*
Saludos / Regards
DaNiElMaXiMiLiAnO
Whatsapp. := +54901169026142
Telegram Name := DaNiElMaXiMiLiAnO
¸.·´¸.·*´¨) ¸.·*¨)
(¸.·´. (¸.·` *
.·`. Harbour/HMG : It's magic !
(¸.·``··*
Saludos / Regards
DaNiElMaXiMiLiAnO
Whatsapp. := +54901169026142
Telegram Name := DaNiElMaXiMiLiAnO
Re: Payment terminal TIMAPI.DLL
Hello Daniel
path to the file timapi.h
\C\TimApi\Include\timapi\
Georg
path to the file timapi.h
\C\TimApi\Include\timapi\
Georg
Re: Payment terminal TIMAPI.DLL
Hello everyone
Thanks again Edward.
My knowledge.
I tried to communicate with a real payment terminal. By default, the terminal has DHCP set for assigning an IP address. The assigned address can be displayed, but it can change after a power failure or disconnection. Communication should be done according to the terminal ID.
It does not detect a real connected payment card.
I don't know if it is necessary to submit a request for payment soon, then he should wait for the card to be inserted. I forced the example in ..TimAPI\DotNet\ExampleEcrSync\ExampleEcrSync.exe, it logs in after the payment request and waits for attachment (RC) or card insertion.
Best regards, Georg
Thanks again Edward.
My knowledge.
I tried to communicate with a real payment terminal. By default, the terminal has DHCP set for assigning an IP address. The assigned address can be displayed, but it can change after a power failure or disconnection. Communication should be done according to the terminal ID.
It does not detect a real connected payment card.
I don't know if it is necessary to submit a request for payment soon, then he should wait for the card to be inserted. I forced the example in ..TimAPI\DotNet\ExampleEcrSync\ExampleEcrSync.exe, it logs in after the payment request and waits for attachment (RC) or card insertion.
Best regards, Georg
Re: Payment terminal TIMAPI.DLL
Hi.
I don't have access to a physical terminal, so I'm testing on a simulator.
I just use the client application "\TIMAPI_DotNet\DotNet\Examples\AdvancedEcr\AdvancedEcr.exe"
Here is my configuration file:
When it comes to establishing a connection via Terminal ID, communication via the UDP protocol is required. Here's a screenshot of the log. I do not know if such communication can be carried out from the harbor level, I will look for it on the web.
Can't force static IP on terminal?
Regarding downloading data from the card:
Here's my flow chart. first do Connect, then Login, then Activate. Insert a virtual card in the simulator. The terminal should send the card details and the status should change. When it comes to reading data from the card, it reads them from the simulator, the virtual ones. Perhaps the physical cards do not return this data. Check what logs are with the physical terminal and physical card.
I don't have access to a physical terminal, so I'm testing on a simulator.
I just use the client application "\TIMAPI_DotNet\DotNet\Examples\AdvancedEcr\AdvancedEcr.exe"
Here is my configuration file:
When it comes to establishing a connection via Terminal ID, communication via the UDP protocol is required. Here's a screenshot of the log. I do not know if such communication can be carried out from the harbor level, I will look for it on the web.
Can't force static IP on terminal?
Regarding downloading data from the card:
Here's my flow chart. first do Connect, then Login, then Activate. Insert a virtual card in the simulator. The terminal should send the card details and the status should change. When it comes to reading data from the card, it reads them from the simulator, the virtual ones. Perhaps the physical cards do not return this data. Check what logs are with the physical terminal and physical card.
Re: Payment terminal TIMAPI.DLL
I think I managed to establish communication via UDP, at least it works on the terminal simulator.
Check this code and see if the physical terminal returns an ip address and port number. In the code, write the correct ID of the terminal.
Does it return that SSL is off or on?
Check this code and see if the physical terminal returns an ip address and port number. In the code, write the correct ID of the terminal.
Code: Select all
#include "hmg.ch"
Function Main
cTerminal_ID := '12345678'
nTimeOut := 300
cBroadcastAdress := '255.255.255.255'
nTerminal_UDP_port := 33334
nListening_UDP_port := 33335
cUDP_CRLF := hb_inetCRLF()
cUPD_RAW_Data := 'SIXml' + cUDP_CRLF + 'TerminalRequest' + cUDP_CRLF + 'TID: ' + cTerminal_ID + cUDP_CRLF + 'Connect: false' + cUDP_CRLF
cUPD_RCV_Data := Space ( 1024 )
IF ! hb_inetInit()
MsgStop ( "Inet could not be initialized!" )
RETURN Nil
ENDIF
//socket := hb_inetDGram( .T. )
socket := hb_inetDGramBind( nListening_UDP_port, , .T. /* lBroadcast */ )
hb_inetTimeout( socket, nTimeOut )
msgdebug ( 'hb_inetErrorCode', hb_inetErrorCode( socket ) )
//hb_inetFD( socket )
msgdebug ( 'UDP bytes sent"', hb_inetDGramSend( socket, cBroadcastAdress, nTerminal_UDP_port, cUPD_RAW_Data ) )
msgdebug ( 'hb_inetErrorCode', hb_inetErrorCode( socket ) )
msgdebug ( 'received UDP bytes:', nRecv_bytes := hb_inetDGramRecv( socket, @cUPD_RCV_Data ), 'received UDP data:', hb_Atokens ( Left ( cUPD_RCV_Data, nRecv_bytes ), cUDP_CRLF ))
Return