We’ve implemented a Socket Client in Kotlin to communicate with a Payment Terminal in the same local network. It works like the following:
- We open the Socket connection, the Payment Terminal responds with a message indicating its status
- We send a message with the Payment Request itself to the Payment Terminal
- When the Payment is Completed/Cancelled/Declined/…, the Payment Terminal responds with the Payment Result
Code samples below.
isRunning = true
try {
val serverAddress = InetAddress.getByName(host)
// Create a socket to make the connection with the server
//val socket = Socket(serverAddress, SERVER_PORT)
val socket = Socket()
socket.connect(InetSocketAddress(serverAddress, port), 5000) // Timeout to initially connect to the POS (connect())
socket.soTimeout = 10000 // Timeout when connection is dropped (read())
try {
writeBuffer =
PrintWriter(BufferedWriter(OutputStreamWriter(socket.outputStream)), true)
// In this while the client listens for the messages sent by the server
while (isRunning) {
val serverMessage = readString(socket.inputStream)
if(serverMessage != "") {
handlers.messageReceived(serverMessage)
}
}
handlers.sockedClosed()
} catch (e: Exception) {
handlers.exceptionOccurred(e)
} finally {
// The socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close()
}
} catch (e: Exception) {
handlers.exceptionOccurred(e)
}
private fun readString(inputStream: InputStream): String {
return buildString {
while (inputStream.available() > 0) {
val byte = inputStream.read()
// Filter out invalid characters (such as heartbeat i.e. 0)
if(byte > 0) {
val ch = byte.toChar()
append(ch)
}
}
}
}
fun sendMessage(message: String?) {
if (writeBuffer != null && !writeBuffer!!.checkError()) {
writeBuffer?.println(message)
writeBuffer?.flush()
}
}
This code works fine, except sometimes it does not. From time to time (avg. 1,5% of the cases), an exception ‘Socket Closed’ or ‘Stream closed.’ is thrown right at the moment we’re (about) to read the Payment Result. It’s an issue that’s very hard to reproduce but the implications are not to be overseen because the customer paid but we never get that result in our app.
Anyone any idea why the read() operation might interrupt unexpectedly?