I tried to write code in C# and I connected with RS232 to send HEX I needed EDC Verifone X990 to respond but now my EDC Verifone X990 has not responded to anything.
in the project now I have 3 files.
ConnectSerialPort.cs
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.IO.Ports;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography;
using System.Text;
public class ConnectSerialPort
{
public string PortName;
public SerialPort serialPort;
public List<string> availablePorts = new List<string>();
public ConnectSerialPort()
{
GetAvailablePorts();
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine("Can connect or response IN");
string invoice = string.Empty;
string invoiceHex = "";
Calculator _calculator = new Calculator();
if (serialPort.BytesToRead > 0)
{
Console.WriteLine("Can connect or response IN1");
}
}
private PackData _packData = new PackData();
public void WriteToSerialPort(string port)
{
try
{
StringBuilder xmlData = new StringBuilder();
xmlData.AppendFormat("<xml>" +
"<trade_type>" + "CARD" + "</trade_type>" +
"<amount>" + 100.25 + "</amount>" +
"<pos_ref_no>" + "IV20181001000011" + "</pos_ref_no>" +
"<transaction_type>" + "SALE" + "</transaction_type>" +
//"<service_type>" + "SHOWQR" + "</service_type>" +
"</xml>");
byte[] buffer = Encoding.UTF8.GetBytes("02004136303030303030303030313032303030301C343000123030303030303030303033321C37340001321C034C");
Console.WriteLine("send data byte array : " + buffer.Length);
Console.WriteLine(port);
StartPayment(3, 100, "000000");
}
catch (System.Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
public void StartPayment(int mode, decimal price, string reference)
{
try
{
PrintData dataPack = new PrintData();
if ((int)mode == 3)
{
var printData = new PrintData()
{
TranCode = "70",
Price = price,
Optional = "0",
Mode = "03",
Invoice = "0",
Reference = reference
};
dataPack = printData;
}
else if ((int)mode == 4)
{
var printData = new PrintData()
{
TranCode = "20",
Price = price,
Optional = "0",
Invoice = "0",
Reference = reference
};
dataPack = printData;
}
else
{
Console.WriteLine("Wrong Payment choice");
}
var fomat = _packData.PackDataMessageStructure(dataPack);
foreach (var f in fomat) {
Console.Write(f);
}
serialPort.Write(fomat, 0, fomat.Length);
bgReader.RunWorkerAsync();
}
catch (Exception ex)
{
Console.WriteLine("Payment Error : " + ex.Message);
}
}
void GetAvailablePorts()
{
availablePorts.Clear();
string[] ports = SerialPort.GetPortNames();
availablePorts.AddRange(ports);
}
public void SetSelectedPort(int index)
{
Console.WriteLine("Set Port Index : " + availablePorts[index]);
if (availablePorts.Count > 0)
{
PortName = availablePorts[index];
Console.WriteLine("Set Port : " + PortName);
OnPortDropdownValueChanged();
}
}
BackgroundWorker bgReader;
void OnPortDropdownValueChanged()
{
serialPort = new SerialPort(PortName);
serialPort.BaudRate = 9600;
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
//serialPort.Handshake = Handshake.None;
serialPort.StopBits = StopBits.One;
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
if (serialPort.IsOpen)
{
serialPort.Close();
}
try
{
serialPort.Open();
bgReader = new BackgroundWorker
{
WorkerReportsProgress = false,
WorkerSupportsCancellation = true
};
bgReader.DoWork += new DoWorkEventHandler(BgReader_OnDoWork);
Console.WriteLine($"Serial port {serialPort.PortName} opened successfully. + {serialPort.IsOpen}");
}
catch (System.Exception ex)
{
Console.WriteLine($"Error opening serial port: {ex.Message}");
}
}
void BgReader_OnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
Console.WriteLine("Other1");
while (!worker.CancellationPending)
{
try
{
if (serialPort.BytesToRead > 0)
{
Console.WriteLine("OtherTest");
}
}
catch (Exception ex)
{
//Logger.Error("Error in processing: " + ex.Message);
}
}
}
public void DisConnectPort()
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
Console.WriteLine("Serial port closed.");
}
}
public static string ConvertToHex(string cardRangeSelector, decimal amount, string merchantNumber, string ref1, string ref2)
{
StringBuilder hexBuilder = new StringBuilder();
// Convert REQD Field (Card Range Selector) to Hex
hexBuilder.Append(Convert.ToByte(cardRangeSelector[0]).ToString("X2"));
// Convert OPTL Field (Amount) to Hex
int amountInCents = (int)(amount * 100);
hexBuilder.Append(amountInCents.ToString("X12"));
// Convert OPTL Field (Merchant Number) to Hex
hexBuilder.Append(Convert.ToByte(merchantNumber[0]).ToString("X2"));
// Convert OPTL Field (Generic Data/Ref#1) to Hex
hexBuilder.Append(Encoding.ASCII.GetBytes(ref1).Length.ToString("X2"));
hexBuilder.Append(Encoding.ASCII.GetBytes(ref1).ToHex());
// Convert OPTL Field (Generic Data/Ref#2) to Hex
hexBuilder.Append(Encoding.ASCII.GetBytes(ref2).Length.ToString("X2"));
hexBuilder.Append(Encoding.ASCII.GetBytes(ref2).ToHex());
return hexBuilder.ToString();
}
public string calculateSignature(IEnumerable<KeyValuePair<string, string>> requestPayloadValues, string secretKey, string currentDate)
{
int size = 0;
string hash = string.Empty;
UTF8Encoding encoding = new UTF8Encoding();
System.Text.StringBuilder PlainDataToHash = new System.Text.StringBuilder();
var res = requestPayloadValues.GetEnumerator();
while (res.MoveNext())
{
switch (res.Current.Key)
{
case "IPN_PID[]":
size = res.Current.Value.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(res.Current.Value);
break;
case "IPN_PNAME[]":
size = res.Current.Value.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(res.Current.Value);
break;
case "IPN_DATE":
size = res.Current.Value.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(res.Current.Value);
break;
}
}
size = currentDate.Length;
PlainDataToHash.Append(size);
PlainDataToHash.Append(currentDate);
Console.WriteLine(PlainDataToHash);
string pass = secretKey;
byte[] passBytes = encoding.GetBytes(pass);
// sha256; for sha3-256, replace HMACSHA256 with HMACSHA3_256
HMACSHA256 hmacSha = new HMACSHA256(passBytes);
string HashData = PlainDataToHash.ToString();
byte[] baseStringForHashBytes = encoding.GetBytes(HashData);
byte[] hashBytes = hmacSha.ComputeHash(baseStringForHashBytes);
hash = ByteToString(hashBytes);
return hash;
}
public static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("x2"); // hex format
}
return (sbinary);
}
public string generateTag(IEnumerable<KeyValuePair<string, string>> requestPayloadValues, string secretKey)
{
var now = DateTime.Now.ToString("yyyyMMddHHmmss");
var responseHash = calculateSignature(requestPayloadValues, secretKey, now);
// sha256; for sha3-256, replace algo="sha256" with algo="sha3-256"
return $"<sig algo="sha256" date="{now}">{responseHash}</sig>";
}
}
public static class ExtensionMethods
{
// Extension method to convert string to Hex
public static string ToHex(this byte[] bytes)
{
StringBuilder hexBuilder = new StringBuilder();
foreach (byte b in bytes)
{
hexBuilder.Append(b.ToString("X2"));
}
return hexBuilder.ToString();
}
}
PackDataBase.cs
using System.Text;
public class Calculator
{
public byte CalculateLRC(List<byte> bytes)
{
byte lrc = 0;
for (int i = 0; i < bytes.Count; i++)
{
lrc ^= bytes[i];
}
lrc.ToString("X2");
return lrc;
}
public string[] ASCIItoHex(string value)
{
byte[] inputByte = Encoding.UTF8.GetBytes(value);
var result = inputByte.Select(item => string.Format("{0:x2}", item)).ToArray();
return result;
}
public string[] GetLLLLValue(int value)
{
//LLLL field data calculator
int llll = value;
string[] result = new string[2];
string llll1 = "00";
string llll2 = "41";
if (llll > 0 && llll <= 255)
{
llll2 = llll.ToString();
result[0] = llll1;
result[1] = llll2;
}
else
{
//input more than 256 or input = 0 or input -1
throw new Exception("input more than 256 byte");
}
//------------------------------------------------------------------------------------
return result;
}
public int ParseBCD(byte[] input)
{
var val = 0;
string str = "";
foreach (var b in input)
{
str += b.ToString("X2");
}
int.TryParse(str, out val);
return val;
}
public byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public string HexadecimalToASCII(string hex)
{
string ascii = "";
for (int i = 0; i < hex.Length; i += 2)
{
ascii += (char)HexadecimalToDecimal(hex.Substring(i, 2));
}
return ascii;
}
public int HexadecimalToDecimal(string hex)
{
hex = hex.ToUpper();
int hexLength = hex.Length;
double dec = 0;
for (int i = 0; i < hexLength; ++i)
{
byte b = (byte)hex[i];
if (b >= 48 && b <= 57)
b -= 48;
else if (b >= 65 && b <= 70)
b -= 55;
dec += b * Math.Pow(16, ((hexLength - i) - 1));
}
return (int)dec;
}
}
public class FieldData
{
public Calculator _calculator = new Calculator();
public List<byte> FieldDataMessageForSaleQR(decimal price, string mode, string optional, string referenceTagNo)
{
int llll = 0;
string fs = "1c";
//Main input
//------------------------------------------------------------------------------------
string[] FTAT = _calculator.ASCIItoHex("40");
int priceInt = decimal.ToInt32(price * 100);
llll = priceInt.ToString().Length;
var llllp = _calculator.GetLLLLValue(12);
var priceC = _calculator.ASCIItoHex(priceInt.ToString());
var pc = 12 - llll;
//------------------------------------------------------------------------------------
var FTPM = _calculator.ASCIItoHex("A1");
if (mode == "01")
{
Console.WriteLine("Alipay Payment");
}
else if (mode == "02")
{
Console.WriteLine("Wechat Payment");
}
else if (mode == "03")
{
Console.WriteLine("QR Code Payment");
}
else if (mode == "04")
{
Console.WriteLine("QR Credit Payment");
}
else if (mode == "30")
{
Console.WriteLine("Dolfin Payment");
}
else if (mode == "99")
{
Console.WriteLine("Other Payment");
}
llll = mode.Length;
var llllc = _calculator.GetLLLLValue(llll);
var modeHex = _calculator.ASCIItoHex(mode);
//Optional input
//------------------------------------------------------------------------------------
string[] FTR1;
string r1;
string[] llllr1 = new string[2];
FTR1 = _calculator.ASCIItoHex("R1");
llll = referenceTagNo.Length;
llllr1 = _calculator.GetLLLLValue(20);
var r11 = _calculator.ASCIItoHex(referenceTagNo);
var FTR1c = 20 - llll;
if (optional == "1")
{
}
//------------------------------------------------------------------------------------
//Pack Message Data
List<string> input = new List<string>();
if (optional == "0")
{
input.AddRange(FTAT);
input.Add(llllp[0]);
input.Add(llllp[1]);
for (int i = 0; i < pc; i++)
{
if (i < pc)
{
input.Add("30");
}
}
input.AddRange(priceC);
input.Add(fs);
input.AddRange(FTPM);
input.Add(llllc[0]);
input.Add(llllc[1]);
input.AddRange(modeHex);
input.Add(fs);
input.AddRange(FTR1);
input.Add(llllr1[0]);
input.Add(llllr1[1]);
for (int i = 0; i < FTR1c; i++)
{
if (i < pc)
{
input.Add("30");
}
}
input.AddRange(r11);
input.Add(fs);
}
else if (optional == "1")
{
}
//------------------------------------------------------------------------------------
List<byte> fef = input.Select(s => Convert.ToByte(s, 16)).ToList();
return fef;
}
public List<byte> FieldDataMessageForSaleStandard(decimal price, string optional)
{
int llll = 0;
string fs = "1c";
//Main input
//------------------------------------------------------------------------------------
string[] ftat = _calculator.ASCIItoHex("40");
int priceInt = decimal.ToInt32(price * 100);
llll = priceInt.ToString().Length;
var llllp = _calculator.GetLLLLValue(12);
var priceC = _calculator.ASCIItoHex(priceInt.ToString());
var pc = 12 - llll;
//Optional input
//------------------------------------------------------------------------------------
string[] FTR1;
string r1;
string[] FTR2;
string r2;
string[] llllr1 = new string[2];
string[] llllr2 = new string[2];
if (optional == "1")
{
}
//------------------------------------------------------------------------------------
//Pack Message Data
List<string> input = new List<string>();
if (optional == "0")
{
input.AddRange(ftat);
input.Add(llllp[0]);
input.Add(llllp[1]);
for (int i = 0; i < pc; i++)
{
if (i < pc)
{
input.Add("30");
}
}
input.AddRange(priceC);
input.Add(fs);
}
/*else if (optional == "1")
{
//input = $"{FTAT} {LLLLP[0]} {LLLLP[1]} {priceC} {FS} {FTPM} {LLLLC[0]} {LLLLC[1]} {mode} {FS} {FTR1} {LLLLR1[0]} {LLLLR1[1]} {r1} {FS} {FTR1} {LLLLR2[0]} {LLLLR2[1]} {r2} {FS}";
}*/
//------------------------------------------------------------------------------------
List<byte> fef = input.Select(s => Convert.ToByte(s, 16)).ToList();
return fef;
}
public List<byte> FieldDataMessageForVoid(string invoice)
{
int llll = 0;
string fs = "1c";
//Main input
//------------------------------------------------------------------------------------
string[] ftat = _calculator.ASCIItoHex("65");
llll = invoice.Length;
var llllp = _calculator.GetLLLLValue(6);
var invoiceNoC = _calculator.ASCIItoHex(invoice);
var pc = 6 - llll;
//Optional input
//------------------------------------------------------------------------------------
string[] llllr1 = new string[2];
string[] llllr2 = new string[2];
//------------------------------------------------------------------------------------
//Pack Message Data
List<string> input = new List<string>();
input.AddRange(ftat);
input.Add(llllp[0]);
input.Add(llllp[1]);
input.AddRange(invoiceNoC);
input.Add(fs);
//------------------------------------------------------------------------------------
List<byte> fef = input.Select(s => Convert.ToByte(s, 16)).ToList();
return fef;
}
}
public class GetMessage
{
public MessageInfo GetMessages(byte[] input)
{
Calculator _calculator = new Calculator();
if (input[0] != 0x02) return null;
var len = _calculator.ParseBCD(new byte[] { input[1], input[2] });
// Check tail.
if (input.Length == len + 5 && input[input.Length - 2] == 0x03)
{
var transportBytes = new byte[10];
Array.Copy(input, 3, transportBytes, 0, 10);
var tranDat = System.Text.Encoding.ASCII.GetString(transportBytes);
TransportHeader tran = new TransportHeader
{
Type = tranDat.Substring(0, 2),
Destination = tranDat.Substring(2, 4),
Source = tranDat.Substring(6, 4),
};
var presentBytes = new byte[8];
Array.Copy(input, 13, presentBytes, 0, 8);
var presDat = System.Text.Encoding.ASCII.GetString(presentBytes);
PresentationHeader pres = new PresentationHeader
{
FormatVer = presDat.Substring(0, 1),
Type = presDat.Substring(1, 1),
TransactionCode = presDat.Substring(2, 2),
ResponseCode = presDat.Substring(4, 2),
MoreMessage = presDat.Substring(6, 1) == "1",
LastChar = presentBytes[7].ToString("X2")
};
// read all datas.
List<Element> element = new List<Element>();
int index = 21;
while (index < 3 + len)
{
var typeCode = System.Text.Encoding.ASCII.GetString(new byte[] { input[index], input[index + 1] });
var elementLength = _calculator.ParseBCD(new byte[] { input[index + 2], input[index + 3] });
var elementBytes = new byte[elementLength];
Array.Copy(input, index + 4, elementBytes, 0, elementLength);
var elementData = System.Text.Encoding.ASCII.GetString(elementBytes);
element.Add(new Element { Type = typeCode, Length = elementLength, Data = elementData });
index += elementLength + 5;
}
return new MessageInfo
{
Transport = tran,
Presentation = pres,
Size = len,
Datas = element
};
}
return null;
}
}
public class PackData
{
public byte[] PackDataMessageStructure(PrintData printData)
{
FieldData fieldData = new FieldData();
Calculator calculator = new Calculator();
MessageStruture message = new MessageStruture();
byte lrc;
List<string> rri = new List<string>() { "30", "31", "32" };
string tc = printData.TranCode;
var tcc = calculator.ASCIItoHex(tc.ToString());
List<string> mi = new List<string>() { "30", "31" };
List<byte> fdm;
if (tc == "70")
{
fdm = fieldData.FieldDataMessageForSaleQR(printData.Price, printData.Mode, printData.Optional, printData.Reference);
}
else if (tc == "26")
{
fdm = fieldData.FieldDataMessageForVoid(printData.Invoice);
}
else if (tc == "20")
{
fdm = fieldData.FieldDataMessageForSaleStandard(printData.Price, printData.Optional);
}
else if (tc == "4")
{
fdm = null;
}
else
{
throw new Exception("Not Match Payment");
}
List<byte> fomat;
string input;
if (tc == "70" || tc == "26" || tc == "20")
{
input = $"{message.THT1} {message.THT2} {message.TD1} {message.TD2} {message.TD3} {message.TD4} {message.TS1} {message.TS2} {message.TS3} {message.TS4} {message.FV} {rri[0]} {tcc[0]} {tcc[1]} {message.RC1} {message.RC2} {mi[0]}";
fomat = input.Split().Select(s => Convert.ToByte(s, 16)).ToList();
fomat.Add(message.FS);
fomat.AddRange(fdm);
string llllc = fomat.Count.ToString();
int decValue = Convert.ToInt32(llllc, 16);
var llll = calculator.GetLLLLValue(decValue);
fomat.Insert(0, Convert.ToByte(llll[1]));
fomat.Insert(0, Convert.ToByte(llll[0]));
fomat.Add(Convert.ToByte(message.ETX));
lrc = calculator.CalculateLRC(fomat);
fomat.Add(lrc);
fomat.Insert(0, Convert.ToByte(message.STX));
return fomat.ToArray();
}
else if (tc == "4")
{
var example = new byte[] { 0x02, 0x00, 0x18, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x44, 0x30, 0x30, 0x30, 0x30, 0x1c, 0x03, 0x44 };
return example;
}
else
{
return null;
}
}
}
public class MessageInfo
{
public int Size { get; set; }
public TransportHeader Transport { get; set; }
public PresentationHeader Presentation { get; set; }
public List<Element> Datas { get; set; }
}
public class TransportHeader
{
public string Type { get; set; }
public string Destination { get; set; }
public string Source { get; set; }
}
public class PresentationHeader
{
public string FormatVer { get; set; }
public string Type { get; set; }
public string TransactionCode { get; set; }
public string ResponseCode { get; set; }
public bool MoreMessage { get; set; }
public string LastChar { get; set; }
}
public class Element
{
public string Type { get; set; }
public int Length { get; set; }
public string Data { get; set; }
}
public class MessageStruture
{
// front
public string STX = "02";
// back
public string ETX = "03";
// Transport Header(TH)
public string THT1 = "36";
public string THT2 = "30";
public string TD1 = "30";
public string TD2 = "30";
public string TD3 = "30";
public string TD4 = "30";
public string TS1 = "30";
public string TS2 = "30";
public string TS3 = "30";
public string TS4 = "30"; //total 10 byte
// Presentation Header(PH)
public string FV = "31";
public byte FS = 0x1c;
public string RC1 = "30";
public string RC2 = "30";
}
public class PrintData
{
public string TranCode { get; set; }
public decimal Price { get; set; }
public string Optional { get; set; }
public string Mode { get; set; }
public string Invoice { get; set; }
public string Reference { get; set; }
}
public class TransactionResult
{
public bool Success { get; set; }
public string Message { get; set; }
public string Invoice { get; set; }
}
Program.cs
using System.Text;
string cardRangeSelector = "V";
decimal amount = 100.50m;
string merchantNumber = "1";
string ref1 = "Ref1";
string ref2 = "Ref2";
string hexData = ConnectSerialPort.ConvertToHex(cardRangeSelector, amount, merchantNumber, ref1, ref2);
Console.WriteLine(hexData);
var connect = new ConnectSerialPort();
Console.WriteLine($"Read Port");
for (int i = 0; i < connect.availablePorts.Count; i++)
Console.WriteLine($"{i} : {connect.availablePorts[i]}");
Console.Write("Select a port (enter the corresponding number): ");
string userInput = Console.ReadLine();
if (int.TryParse(userInput, out int selectedPortIndex) && selectedPortIndex >= 0 && selectedPortIndex < connect.availablePorts.Count)
{
string selectedPort = connect.availablePorts[selectedPortIndex];
Console.WriteLine($"You selected port: {selectedPort}");
connect.SetSelectedPort(selectedPortIndex);
while (true)
{
string inputconfirm = Console.ReadLine();
Console.Write("Select a Mode (enter the corresponding number): " + inputconfirm);
connect.WriteToSerialPort(hexData);
}
}
else
{
Console.WriteLine("Invalid input. Please enter a valid number corresponding to the port.");
}
I tried to Open the Serial Port and send the HEX request messages and I expected EDC Verifone X990 to respond to the response messages but EDC didn’t respond to anything.