EXPLORE INFORMATION : AI, BITCOIN, TECHNOLOGY. Enjoy!

Cara membuat program EDC ECR

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Linq;

namespace EDCSenderApp
{
public class EDCSender : IDisposable
{
private SerialPort serialPort;
private TcpClient tcpClient;
private NetworkStream networkStream;
private bool isConnected;
private ConnectionType connectionType;

enum ConnectionType

{
None,
Serial,
TCP

}

// Constants for
private const byte STX = 0x02;
private const byte ETX = 0x03;
private const byte ACK = 0x06;
private const byte NAK = 0x15;
public EDCSender()

{

serialPort = null;
tcpClient = null;
networkStream = null;
isConnected = false;
connectionType = ConnectionType.None;

}

// Fungsi untuk connect via Serial (COM Port)

public bool ConnectSerial(string portName, int baudRate = 9600)

{

try

{

serialPort = new SerialPort(portName, baudRate)

{

DataBits = 8,
StopBits = StopBits.One,
Parity = Parity.None,
Handshake = Handshake.None,

ReadTimeout = 5000,

WriteTimeout = 5000

};
serialPort.Open();
connectionType = ConnectionType.Serial;
isConnected = true;

Console.WriteLine($"✅ Berhasil connect ke {portName} dengan baudrate {baudRate}");

return true;
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error: Gagal membuka port {portName} - {ex.Message}");
return false;
}
}

// Fungsi untuk connect via TCP/IP

public bool ConnectTCP(string ipAddress, int port)

{
try
{

tcpClient = new TcpClient();
tcpClient.Connect(ipAddress, port);
networkStream = tcpClient.GetStream();
connectionType = ConnectionType.TCP;
isConnected = true;

Console.WriteLine($"✅ Berhasil connect ke {ipAddress}:{port}");

return true;

}

catch (Exception ex)

{

Console.WriteLine($"❌ Error: Gagal connect ke {ipAddress}:{port} - {ex.Message}");

return false;

}
}


// Fungsi untuk menghitung LRC sesuai spesifikasi

public byte CalculateLRC(byte[] data)

{

byte lrc = 0;
foreach (byte b in data)
{
lrc ^= b;
}
return lrc;
}


// Fungsi untuk membuat message EDC lengkap sesuai spesifikasi AAA

public byte[] CreateEDCMessage(AAARequest request)

{

List<byte> message = new List<byte>();


// Add STX (Start of Text)

message.Add(STX);


// Convert request to byte array (150 bytes)

byte[] messageData = request.ToByteArray();


// PERBAIKAN: Gunakan single byte untuk length - 150 = 0x96

byte lengthByte = 0x96; // 150 dalam hex

message.Add(lengthByte);

// Add message data (150 bytes)

message.AddRange(messageData);

// Add ETX (End of Text)

message.Add(ETX);

// Calculate and add LRC (XOR dari Length sampai ETX, tidak termasuk STX)
byte[] dataForLRC = message.Skip(1).ToArray(); // Skip STX
byte lrc = CalculateLRC(dataForLRC);
message.Add(lrc);

Console.WriteLine($"šŸ“¦ Message frame structure:");
Console.WriteLine($" STX: 0x{STX:X2}");
Console.WriteLine($" Length: 0x{lengthByte:X2} ({lengthByte} bytes)");
Console.WriteLine($" Data: {messageData.Length} bytes");
Console.WriteLine($" ETX: 0x{ETX:X2}");
Console.WriteLine($" LRC: 0x{lrc:X2}");
Console.WriteLine($" Total frame: {message.Count} bytes");

// VERIFIKASI KONVERSI

Console.WriteLine($"šŸ” Length Verification:");

Console.WriteLine($" Decimal: {lengthByte}");

Console.WriteLine($" Hex: 0x{lengthByte:X2}");

return message.ToArray();
}
// Fungsi untuk send data dengan protocol
public bool SendData(byte[] data)
{
if (!isConnected)
{
Console.WriteLine("❌ Error: Tidak terkoneksi ke device");

return false;
}
try
{
Console.WriteLine($"šŸ“¤ Mengirim {data.Length} bytes ke EDC...");

DisplayHexData(data, "DATA YANG DIKIRIM");

switch (connectionType)

{

case ConnectionType.Serial:

serialPort.DiscardInBuffer(); // Clear buffer sebelum kirim

serialPort.Write(data, 0, data.Length);

break;

case ConnectionType.TCP:

networkStream.Write(data, 0, data.Length);

networkStream.Flush();

break;

default:
return false;

}

Console.WriteLine($"✅ Berhasil mengirim {data.Length} bytes");

return true;

}

catch (Exception ex)
{

Console.WriteLine($"❌ Error: Gagal mengirim data - {ex.Message}");

return false;

}

}

// Fungsi untuk receive response dengan protocol - FIXED LENGTH PARSING

public AAAResponse ReceiveResponse(int timeoutMs = 30000)

{

if (!isConnected)
{

Console.WriteLine("❌ Error: Tidak terkoneksi ke device");

return null;

}

try
{
Console.WriteLine("⏳ Menunggu response dari EDC...");
byte[] responseData = new byte[0];
DateTime startTime = DateTime.Now;
switch (connectionType)
{
case ConnectionType.Serial:
serialPort.ReadTimeout = timeoutMs;
List<byte> serialBuffer = new List<byte>();
byte[] serialReadBuffer = new byte[1024];
while ((DateTime.Now - startTime).TotalMilliseconds < timeoutMs)
{
if (serialPort.BytesToRead > 0)
{
int bytesRead = serialPort.Read(serialReadBuffer, 0, serialReadBuffer.Length);
serialBuffer.AddRange(serialReadBuffer.Take(bytesRead));
Console.WriteLine($"šŸ“„ Received {bytesRead} bytes (Total: {serialBuffer.Count})");
// Cari STX dalam buffer
int stxIndex = -1;
for (int i = 0; i < serialBuffer.Count; i++)
{
if (serialBuffer[i] == STX)
{
stxIndex = i;
break;
}
}
if (stxIndex >= 0)
{
// Buang data sebelum STX
if (stxIndex > 0)
{
serialBuffer.RemoveRange(0, stxIndex);
Console.WriteLine($"šŸ”„ Dibuang {stxIndex} bytes sebelum STX");
}
// Sekarang buffer dimulai dengan STX
if (serialBuffer.Count >= 5) // Minimal STX + LEN(1) + min data + ETX + LRC
{
// PERBAIKAN: Parsing length dengan single byte
int dataLength = serialBuffer[1]; // Single byte length
int totalExpectedLength = 1 + 1 + dataLength + 1 + 1; // STX + LEN + DATA + ETX + LRC
Console.WriteLine($"šŸ“ Data Length: {dataLength} bytes, Total Expected: {totalExpectedLength} bytes");
// Untuk response, expect 200 bytes data
if (dataLength == 200 || dataLength == 150) // Bisa 150 untuk request, 200 untuk response
{
if (serialBuffer.Count >= totalExpectedLength)
{
responseData = serialBuffer.Take(totalExpectedLength).ToArray();
Console.WriteLine($"✅ Data lengkap diterima: {responseData.Length} bytes");
break;
}
else
{
Console.WriteLine($"⏳ Menunggu data lengkap: {serialBuffer.Count}/{totalExpectedLength} bytes");
}
}
else
{
Console.WriteLine($"⚠️ Warning: Unexpected data length {dataLength}. Expected 150 or 200.");
// Tetap coba proses jika data cukup
if (serialBuffer.Count >= totalExpectedLength)
{
responseData = serialBuffer.Take(totalExpectedLength).ToArray();

Console.WriteLine($"✅ Data lengkap diterima: {responseData.Length} bytes");

break;
}
}
}
}
else
{
if (serialBuffer.Count > 100)
{
serialBuffer.Clear();
Console.WriteLine("šŸ”„ Clear buffer: STX tidak ditemukan dalam 100 bytes pertama");
}
}
}
Thread.Sleep(50);
}
break;
case ConnectionType.TCP:
networkStream.ReadTimeout = timeoutMs;
List<byte> tcpBuffer = new List<byte>();
byte[] tcpReadBuffer = new byte[1024];
while ((DateTime.Now - startTime).TotalMilliseconds < timeoutMs)
{
if (networkStream.DataAvailable)
{
int bytesRead = networkStream.Read(tcpReadBuffer, 0, tcpReadBuffer.Length);
tcpBuffer.AddRange(tcpReadBuffer.Take(bytesRead));
Console.WriteLine($"šŸ“„ Received {bytesRead} bytes (Total: {tcpBuffer.Count})");
// Cari STX dalam buffer
int stxIndex = -1;
for (int i = 0; i < tcpBuffer.Count; i++)
{
if (tcpBuffer[i] == STX)
{
stxIndex = i;
break;
}
}
if (stxIndex >= 0)
{
if (stxIndex > 0)
{
tcpBuffer.RemoveRange(0, stxIndex);
Console.WriteLine($"šŸ”„ Dibuang {stxIndex} bytes sebelum STX");
}
if (tcpBuffer.Count >= 5)
{
// Single byte length parsing
int dataLength = tcpBuffer[1];
int totalExpectedLength = 1 + 1 + dataLength + 1 + 1;
Console.WriteLine($"šŸ“ Data Length: {dataLength} bytes, Total Expected: {totalExpectedLength} bytes");
if (dataLength == 200 || dataLength == 150)
{
if (tcpBuffer.Count >= totalExpectedLength)
{
responseData = tcpBuffer.Take(totalExpectedLength).ToArray();
Console.WriteLine($"✅ Data lengkap diterima: {responseData.Length} bytes");
break;
}
else
{
Console.WriteLine($"⏳ Menunggu data lengkap: {tcpBuffer.Count}/{totalExpectedLength} bytes");

}
}
else
{
Console.WriteLine($"⚠️ Warning: Unexpected data length {dataLength}");
if (tcpBuffer.Count >= totalExpectedLength)
{
responseData = tcpBuffer.Take(totalExpectedLength).ToArray();
Console.WriteLine($"✅ Data lengkap diterima: {responseData.Length} bytes");
break;
}
}
}
}
else
{
if (tcpBuffer.Count > 100)
{
tcpBuffer.Clear();
Console.WriteLine("šŸ”„ Clear buffer: STX tidak ditemukan dalam 100 bytes pertama");
}
}
}
Thread.Sleep(50);
}
break;
}
if (responseData.Length == 0)
{
Console.WriteLine("⚠️ Timeout: Tidak ada response dari EDC");
return null;
}
Console.WriteLine($"✅ Received total {responseData.Length} bytes dari EDC");
DisplayHexData(responseData, "DATA YANG DITERIMA");
return ParseResponseMessage(responseData);
}
catch (TimeoutException)
{
Console.WriteLine("⚠️ Timeout: Tidak ada response dari EDC dalam waktu yang ditentukan");
return null;
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error saat receive response: {ex.Message}");
return null;
}
}
// Parse response message sesuai spesifikasi- FIXED LENGTH HANDLING
private AAAResponse ParseResponseMessage(byte[] responseData)
{
try
{
Console.WriteLine("\nšŸ” Parsing response message...");

if (responseData[0] != STX)
{
Console.WriteLine($"❌ ERROR: STX tidak valid. Expected: 0x{STX:X2}, Actual: 0x{responseData[0]:X2}");
return null;
}
Console.WriteLine($"✅ STX Valid: 0x{responseData[0]:X2}");
// PERBAIKAN: Parsing length dengan single byte
int dataLength = responseData[1]; // Single byte length
Console.WriteLine($"šŸ“ Declared Length: {dataLength} bytes (0x{responseData[1]:X2})");
// Jangan force ke 200, gunakan length yang dideklarasikan
int totalExpectedLength = 1 + 1 + dataLength + 1 + 1; // STX + LEN + DATA + ETX + LRC
if (responseData.Length < totalExpectedLength)
{
Console.WriteLine($"❌ ERROR: Data tidak lengkap. Expected: {totalExpectedLength}, Actual: {responseData.Length}");
return null;
}
if (responseData.Length > totalExpectedLength)
{
Console.WriteLine($"⚠️ Warning: Data lebih panjang dari expected. Mengambil {totalExpectedLength} bytes dari {responseData.Length}");
byte[] trimmedData = new byte[totalExpectedLength];
Array.Copy(responseData, trimmedData, totalExpectedLength);
responseData = trimmedData;
}
int etxPosition = 1 + 1 + dataLength; // STX(1) + LEN(1) + DATA
if (responseData[etxPosition] != ETX)
{
Console.WriteLine($"❌ ERROR: ETX tidak valid. Expected: 0x{ETX:X2}, Actual: 0x{responseData[etxPosition]:X2} pada posisi {etxPosition}");
return null;
}
Console.WriteLine($"✅ ETX Valid: 0x{responseData[etxPosition]:X2} pada posisi {etxPosition}");
byte receivedLRC = responseData[responseData.Length - 1];
byte calculatedLRC = CalculateLRC(responseData.Skip(1).Take(responseData.Length - 2).ToArray());
Console.WriteLine($"šŸ”¢ LRC Check: Received=0x{receivedLRC:X2}, Calculated=0x{calculatedLRC:X2}");
if (receivedLRC != calculatedLRC)
{
Console.WriteLine($"❌ ERROR: LRC tidak valid");
Console.WriteLine("⚠️ Tetapi melanjutkan parsing untuk debugging...");
}
else
{
Console.WriteLine("✅ LRC Response Valid");
}
byte[] messageData = new byte[dataLength];
Array.Copy(responseData, 2, messageData, 0, dataLength); // Start dari posisi 2 (setelah STX dan LEN)
Console.WriteLine($"šŸ“Š Message Data: {messageData.Length} bytes");
return new AAAResponse(messageData);
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error parsing response: {ex.Message}");
return null;
}
}
// Fungsi untuk disconnect
public void Disconnect()
{
if (isConnected)
{
switch (connectionType)
{
case ConnectionType.Serial:
serialPort?.Close();
serialPort?.Dispose();
serialPort = null;
break;
case ConnectionType.TCP:
networkStream?.Close();
tcpClient?.Close();
networkStream = null;
tcpClient = null;
break;
}
isConnected = false;
connectionType = ConnectionType.None;
Console.WriteLine("šŸ”Œ Disconnected dari device");
}
}

// Fungsi untuk display data dalam format HEX
public void DisplayHexData(byte[] data, string title)
{
Console.WriteLine($"\n{title} ({data.Length} bytes):");
Console.WriteLine("Pos 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII");
Console.WriteLine(" ---------------------------------------------------------");
for (int i = 0; i < data.Length; i += 16)
{
Console.Write($"{i:X4} ");
for (int j = 0; j < 16; j++)
{
if (i + j < data.Length)
Console.Write($"{data[i + j]:X2} ");
else
Console.Write(" ");
}
Console.Write(" ");
for (int j = 0; j < 16; j++)
{
if (i + j < data.Length)
{
byte b = data[i + j];
if (b >= 32 && b <= 126)
Console.Write(Encoding.ASCII.GetString(new byte[] { b }));
else
Console.Write(".");
}
else
{
Console.Write(" ");
}
}
Console.WriteLine();
}
Console.WriteLine();
}
public bool IsConnected => isConnected;
public void Dispose()
{
Disconnect();
}
}

// ... (AAARequest, AAAResponse, AAAUtility classes remain the same as your original code)
// Class untuk AAA Request Message (150 bytes)

public class AAARequest
{
public byte Version { get; set; } = 0x33;
public string TransType { get; set; } = "01";
public decimal TransAmount { get; set; }
public decimal OtherAmount { get; set; }
public string PAN { get; set; } = "";
public string ExpiryDate { get; set; } = "";
public string CancelReason { get; set; } = "00";
public string InvoiceNumber { get; set; } = "";
public string AuthCode { get; set; } = "";
public char InstallmentFlag { get; set; } = 'N';
public char RedeemFlag { get; set; } = 'N';
public char DCCFlag { get; set; } = 'N';
public string InstallmentPlan { get; set; } = "";
public string InstallmentTenor { get; set; } = "";
public string GenericData { get; set; } = "";
public string RefNumber { get; set; } = "";
public string OriginalDate { get; set; } = "";
public byte[] ToByteArray()
{

List<byte> data = new List<byte>();

data.Add(Version);
data.AddRange(Encoding.ASCII.GetBytes(TransType));

long transAmountInSen = (long)(TransAmount * 100);
string transAmountStr = transAmountInSen.ToString().PadLeft(12, '0');
data.AddRange(Encoding.ASCII.GetBytes(transAmountStr));
long otherAmountInSen = (long)(OtherAmount * 100);
string otherAmountStr = otherAmountInSen.ToString().PadLeft(12, '0');
data.AddRange(Encoding.ASCII.GetBytes(otherAmountStr));
data.AddRange(Encoding.ASCII.GetBytes(PAN.PadRight(19, ' ')));
data.AddRange(Encoding.ASCII.GetBytes(ExpiryDate.PadLeft(4, '0')));
data.AddRange(Encoding.ASCII.GetBytes(CancelReason.PadLeft(2, '0')));
data.AddRange(Encoding.ASCII.GetBytes(InvoiceNumber.PadLeft(6, '0')));
data.AddRange(Encoding.ASCII.GetBytes(AuthCode.PadRight(6, ' ')));
data.Add((byte)InstallmentFlag);
data.Add((byte)RedeemFlag);
data.Add((byte)DCCFlag);
data.AddRange(Encoding.ASCII.GetBytes(InstallmentPlan.PadRight(3, ' ')));
data.AddRange(Encoding.ASCII.GetBytes(InstallmentTenor.PadRight(2, ' ')));
data.AddRange(Encoding.ASCII.GetBytes(GenericData.PadRight(12, ' ')));
data.AddRange(Encoding.ASCII.GetBytes(RefNumber.PadRight(12, ' ')));
data.AddRange(Encoding.ASCII.GetBytes(OriginalDate.PadLeft(4, '0')));
data.AddRange(Encoding.ASCII.GetBytes(new string(' ', 50)));

if (data.Count != 150)
{
throw new InvalidOperationException($"Request message harus 150 bytes, tapi {data.Count} bytes");
}
return data.ToArray();
}
}
// Class untuk AAA Response Message (200 bytes)
public class AAAResponse
{
public byte Version { get; set; }
public string TransType { get; set; }
public decimal TransAmount { get; set; }
public decimal OtherAmount { get; set; }
public string PAN { get; set; }
public string ExpiryDate { get; set; }
public string RespCode { get; set; }
public string RRN { get; set; }
public string ApprovalCode { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public string MerchantId { get; set; }
public string TerminalId { get; set; }
public char OfflineFlag { get; set; }
public string CardholderName { get; set; }
public string PANCashierCard { get; set; }
public string InvoiceNumber { get; set; }
public string BatchNumber { get; set; }
public string IssuerId { get; set; }
public char InstallmentFlag { get; set; }
public char DCCFlag { get; set; }
public char RedeemFlag { get; set; }
public string InfoAmount { get; set; }
public string DCCDecimalPlace { get; set; }
public string DCCCurrencyName { get; set; }
public string DCCExchangeRate { get; set; }
public char CouponFlag { get; set; }
public string Filler { get; set; }
public AAAResponse(byte[] data)
{
if (data.Length != 200)
{
Console.WriteLine($"⚠️ Warning: Response data {data.Length} bytes (seharusnya 200)");
}
string dataStr = Encoding.ASCII.GetString(data);
try
{
Version = data[0];
TransType = dataStr.Substring(1, 2);
string transAmountStr = dataStr.Substring(3, 12);
if (long.TryParse(transAmountStr, out long transAmountLong))
TransAmount = transAmountLong / 100m;

string otherAmountStr = dataStr.Substring(15, 12);
if (long.TryParse(otherAmountStr, out long otherAmountLong))
OtherAmount = otherAmountLong / 100m;
PAN = dataStr.Substring(27, 19).Trim();
ExpiryDate = dataStr.Substring(46, 4).Trim();
RespCode = dataStr.Substring(50, 2);
RRN = dataStr.Substring(52, 12).Trim();
ApprovalCode = dataStr.Substring(64, 6).Trim();
Date = dataStr.Substring(70, 8);
Time = dataStr.Substring(78, 6);
MerchantId = dataStr.Substring(84, 15).Trim();
TerminalId = dataStr.Substring(99, 8).Trim();
OfflineFlag = dataStr[107];
CardholderName = dataStr.Substring(108, 26).Trim();
PANCashierCard = dataStr.Substring(134, 16).Trim();
InvoiceNumber = dataStr.Substring(150, 6).Trim();
BatchNumber = dataStr.Substring(156, 6).Trim();
IssuerId = dataStr.Substring(162, 2).Trim();
InstallmentFlag = dataStr[164];
DCCFlag = dataStr[165];
RedeemFlag = dataStr[166];
InfoAmount = dataStr.Substring(167, 12).Trim();
DCCDecimalPlace = dataStr.Substring(179, 1).Trim();
DCCCurrencyName = dataStr.Substring(180, 3).Trim();
DCCExchangeRate = dataStr.Substring(183, 8).Trim();
CouponFlag = dataStr[191];
Filler = dataStr.Substring(192, 8).Trim();
}
catch (Exception ex)
{
throw new ArgumentException($"Error parsing response data: {ex.Message}");
}
}
public void Display()
{
Console.WriteLine("\nšŸ“‹ RESPONSE DETAILS:");
Console.WriteLine("====================");
Console.WriteLine($"Version: 0x{Version:X2} (ASCII: '{(char)Version}')");
Console.WriteLine($"Transaction Type: {TransType}");
Console.WriteLine($"Amount: Rp {TransAmount:N0}");
Console.WriteLine($"Other Amount: Rp {OtherAmount:N0}");
Console.WriteLine($"PAN: {PAN}");
Console.WriteLine($"Expiry Date: {ExpiryDate}");
Console.WriteLine($"Response Code: {RespCode} - {GetRespCodeDescription(RespCode)}");
Console.WriteLine($"RRN: {RRN}");
Console.WriteLine($"Approval Code: {ApprovalCode}");
Console.WriteLine($"Date/Time: {Date} {Time}");
Console.WriteLine($"Merchant ID: {MerchantId}");
Console.WriteLine($"Terminal ID: {TerminalId}");
Console.WriteLine($"Offline Flag: {OfflineFlag}");
Console.WriteLine($"Cardholder: {CardholderName}");
Console.WriteLine($"Invoice: {InvoiceNumber}");
Console.WriteLine($"Batch: {BatchNumber}");
Console.WriteLine($"Issuer: {IssuerId} - {GetIssuerDescription(IssuerId)}");

if (RespCode == "00")
{
Console.WriteLine("✅ TRANSACTION APPROVED");
}
else
{
Console.WriteLine("❌ TRANSACTION DECLINED");
}
}

private string GetRespCodeDescription(string respCode)
{
switch (respCode)
{
case "00": return "Approve";
case "54": return "Decline Expired Card";
case "55": return "Decline Incorrect PIN";
case "P2": return "Read Card Error";
case "P3": return "User press Cancel on EDC";
case "Z3": return "EMV Card Decline";
case "CE": return "Connection Error/Line Busy";
case "TO": return "Connection Timeout";
case "PT": return "EDC Problem";
case "S2": return "TRANSAKSI GAGAL - ULANGI TRANSAKSI DI EDC";
case "S3": return "TXN BLM DIPROSES - MINTA SCAN QR";
case "S4": return "TXN EXPIRED - ULANGI TRANSAKSI";
case "TN": return "Topup Tunai Not Ready";
default: return "Unknown Response Code";
}
}
private string GetIssuerDescription(string issuerId)
{
switch (issuerId)
{
case "00": return "Debit AAA";
case "01": return "Visa";
case "02": return "Mastercard";
case "03": return "AMEX";
case "04": return "Diners Club";
case "05": return "JCB";
case "06": return "Union Pay";
case "07": return "AAA Card";
case "08": return "Others";
case "09": return "AMEX AAA";
case "10": return "AAA Syariah";
case "11": return "Cash AAA";
case "12": return "Smartcash";
case "13": return "Debit MC AAA";
case "14": return "Flazz AAA";
case "15": return "JCB AAA";
case "16": return "Maestro";
case "17": return "Master AAA";
case "18": return "Unionpay AAA";
case "19": return "Visa AAA";
case "20": return "Visa SQ";

default: return "Unknown Issuer";
}
}
}
// Class helper untuk fungsi utilitas
public static class AAAUtility
{
public static string GenerateInvoiceNumber()
{
return DateTime.Now.ToString("HHmmss");
}
public static void Delay(int milliseconds)
{
Thread.Sleep(milliseconds);
}
public static AAARequest CreateSaleRequest(decimal amount, string invoiceNumber = null)
{
return new AAARequest
{
Version = 0x33,
TransType = "01",
TransAmount = amount,
InvoiceNumber = invoiceNumber ?? GenerateInvoiceNumber(),
InstallmentFlag = 'N',
RedeemFlag = 'N',
DCCFlag = 'N'
};
}
public static AAARequest CreateSaleWithCashRequest(decimal amount, decimal cashAmount, string invoiceNumber = null)
{
return new AAARequest
{
Version = 0x33,
TransType = "02",
TransAmount = amount,
OtherAmount = cashAmount,
InvoiceNumber = invoiceNumber ?? GenerateInvoiceNumber()
};
}
public static AAARequest CreateRefundRequest(decimal amount, string originalInvoiceNumber)
{
return new AAARequest
{
Version = 0x33,
TransType = "03",
TransAmount = amount,
InvoiceNumber = originalInvoiceNumber
};
}
public static AAARequest CreateVoidRequest(string originalInvoiceNumber)
{
return new AAARequest
{
Version = 0x33,
TransType = "08",
CancelReason = "02",
InvoiceNumber = originalInvoiceNumber
};
}
public static AAARequest CreateSettlementRequest()
{
return new AAARequest
{
Version = 0x33,
TransType = "10"
};
}
public static AAARequest CreateEchoTestRequest()
{
return new AAARequest
{
Version = 0x33,
TransType = "17"
};
}
}
}
// Main Program class
class Program
{
static void Main(string[] args)
{
Console.WriteLine("==========================================");
Console.WriteLine("EDC COMMUNICATION );
Console.WriteLine("==========================================");
using (EDCSenderApp.EDCSender edc = new EDCSenderApp.EDCSender())
{
int choice;
Console.WriteLine("\nšŸ“” Pilih koneksi:");
Console.WriteLine("1. Connect via Serial (COM Port)");
Console.WriteLine("2. Connect via TCP/IP");
Console.WriteLine("3. Exit");
Console.Write("Pilihan: ");

if (!int.TryParse(Console.ReadLine(), out choice))
{
Console.WriteLine("❌ Input tidak valid!");
return;
}
bool connected = false;
switch (choice)
{
case 1:
Console.Write("Masukkan COM Port (contoh: COM1, COM3): ");
string port = Console.ReadLine();
Console.Write("Masukkan Baud Rate (default: 9600): ");
if (!int.TryParse(Console.ReadLine(), out int baudRate))
{
baudRate = 9600;
}
connected = edc.ConnectSerial(port, baudRate);
break;
case 2:
Console.Write("Masukkan IP Address: ");
string ip = Console.ReadLine();
Console.Write("Masukkan Port (default: 5000): ");
if (!int.TryParse(Console.ReadLine(), out int tcpPort))
{
tcpPort = 5000;
}
connected = edc.ConnectTCP(ip, tcpPort);
break;
case 3:
return;
default:
Console.WriteLine("❌ Pilihan tidak valid!");
return;
}
if (!connected)
{
Console.WriteLine("❌ Gagal connect ke device!");
return;
}
while (true)
{
Console.WriteLine("\nšŸ’³ MENU TRANSAKSI EDC AAA:");
Console.WriteLine("1. Sale");
Console.WriteLine("2. Sale with Cashback");
Console.WriteLine("3. Refund");
Console.WriteLine("4. Void");
Console.WriteLine("5. Settlement");
Console.WriteLine("6. Echo Test");
Console.WriteLine("7. Custom Transaction");
Console.WriteLine("8. Disconnect & Exit");
Console.Write("Pilihan: ");

if (!int.TryParse(Console.ReadLine(), out choice))
{
Console.WriteLine("❌ Input tidak valid!");
continue;
}
EDCSenderApp.AAARequest request = null;
bool success = false;
try
{
switch (choice)
{
case 1:
Console.Write("Masukkan amount (contoh: 100000): ");
if (decimal.TryParse(Console.ReadLine(), out decimal saleAmount))
{
request = EDCSenderApp.AAAUtility.CreateSaleRequest(saleAmount);
success = true;
}
else
{
Console.WriteLine("❌ Amount tidak valid!");
}
break;
case 2:
Console.Write("Masukkan amount sale: ");
if (decimal.TryParse(Console.ReadLine(), out decimal saleAmount2))
{
Console.Write("Masukkan amount cashback: ");
if (decimal.TryParse(Console.ReadLine(), out decimal cashAmount))
{
request = EDCSenderApp.AAAUtility.CreateSaleWithCashRequest(saleAmount2, cashAmount);
success = true;
}
}
break;
case 3:
Console.Write("Masukkan amount refund: ");
if (decimal.TryParse(Console.ReadLine(), out decimal refundAmount))
{
Console.Write("Masukkan invoice number original: ");
string originalInvoice = Console.ReadLine();
request = EDCSenderApp.AAAUtility.CreateRefundRequest(refundAmount, originalInvoice);
success = true;
}
break;
case 4:
Console.Write("Masukkan invoice number yang akan di-void: ");
string voidInvoice = Console.ReadLine();
request = EDCSenderApp.AAAUtility.CreateVoidRequest(voidInvoice);
success = true;
break;
case 5:
request = EDCSenderApp.AAAUtility.CreateSettlementRequest();
success = true;
break;
case 6:
request = EDCSenderApp.AAAUtility.CreateEchoTestRequest();
success = true;
break;
case 7:
request = CreateCustomTransaction();
success = request != null;
break;

case 8:
edc.Disconnect();
Console.WriteLine("šŸ‘‹ Program selesai.");
return;

default:
Console.WriteLine("❌ Pilihan tidak valid!");
continue;
}

if (success && request != null)
{
byte[] message = edc.CreateEDCMessage(request);

if (edc.SendData(message))

{

EDCSenderApp.AAAUtility.Delay(3000);

EDCSenderApp.AAAResponse response = edc.ReceiveResponse();

if (response != null)

{

response.Display();

if (response.RespCode == "00" && choice == 1)

{

Console.WriteLine($"šŸ’¾ Simpan info: Invoice#{response.InvoiceNumber}, RRN#{response.RRN}");

}

}

else

{

Console.WriteLine("❌ Tidak ada response yang valid dari EDC");

}

}

}

}

catch (Exception ex)
{
Console.WriteLine($"❌ Error: {ex.Message}");
}
}
}
}

static EDCSenderApp.AAARequest CreateCustomTransaction()
{
Console.WriteLine("\nšŸ”§ CUSTOM TRANSACTION BUILDER");

var request = new EDCSenderApp.AAARequest();

Console.Write("Transaction Type (01=Sale, 02=Sale+Cash, 03=Refund, dll): ");
request.TransType = Console.ReadLine();

Console.Write("Amount: ");

if (decimal.TryParse(Console.ReadLine(), out decimal amount))

request.TransAmount = amount;

Console.Write("Other Amount: ");

if (decimal.TryParse(Console.ReadLine(), out decimal otherAmount))

request.OtherAmount = otherAmount;

Console.Write("Invoice Number: ");

request.InvoiceNumber = Console.ReadLine();




Console.Write("PAN (kosongkan jika tidak manual entry): ");

request.PAN = Console.ReadLine();


Console.Write("Expiry Date (YYMM): ");

request.ExpiryDate = Console.ReadLine();

return request;
}
}


Program ini adalah aplikasi untuk berkomunikasi dengan mesin EDC (Electronic Data Capture) mesin yang biasa digunakan untuk transaksi kartu debit/kredit di toko-toko. Program bisa mengirim perintah transaksi ke mesin EDC dan menerima responsnya.

šŸ”Œ Cara Menghubungkan ke Mesin EDC

1. Lewat Kabel Serial (COM Port)

· Seperti menghubungkan ke printer lama
· Contoh: COM1, COM3, dll
· Kecepatan data: biasanya 9600

2. Lewat Jaringan (TCP/IP)
· Seperti menghubungkan ke internet
· Butuh alamat IP dan nomor port
· Contoh: 192.168.1.100:5000

šŸ’³ Jenis Transaksi yang Tersedia

1. SALE - Transaksi Belanja Biasa
· Untuk pembayaran biasa dengan kartu
· Contoh: belanja Rp 100,000

2. SALE WITH CASHBACK - Belanja + Tarik Tunai
· Bisa tarik uang tunai sekaligus belanja
· Contoh: belanja Rp 50,000 + tarik tunai Rp 100,000

3. REFUND - Pengembalian Dana
· Kalau ada transaksi yang perlu dibatalkan dan uang dikembalikan
· Butuh nomor invoice transaksi asli

4. VOID - Pembatalan Transaksi
· Membatalkan transaksi yang sudah terjadi
· Harus dilakukan hari yang sama

5. SETTLEMENT - Tutup Hari
· Untuk menyelesaikan semua transaksi harian
· Biasanya dilakukan malam hari

6. ECHO TEST - Cek Koneksi
· Mengecek apakah koneksi ke mesin EDC berjalan baik
· Seperti "test call" ke mesin EDC

šŸ“¦ Cara Kerja Program
Langkah 1: Buat Pesan Transaksi
Program membuat pesan yang berisi:
· Jenis transaksi (sale, refund, dll)
· Jumlah uang
· Nomor invoice
· Data kartu (jika perlu)

Langkah 2: Bungkus Pesan dengan Format Khusus

Pesan dibungkus dengan format khusus AAA:

· STX → Tanda mulai pesan
· Panjang Data → Ukuran pesan
· Data Transaksi → Isi pesan (150 karakter)
· ETX → Tanda akhir pesan
· LRC → Kode pengecekan error

Langkah 3: Kirim ke Mesin EDC

· Pesan dikirim via COM Port atau TCP/IP
· Program tunggu respons dari mesin

Langkah 4: Terima dan Baca Respons
· Mesin EDC balas dengan data transaksi
· Program baca dan tampilkan hasilnya:
· ✅ Disetujui atau ❌ Ditolak
· Kode approval
· Nomor referensi (RRN)
· Nama pemegang kartu
· dll

šŸŽ® Cara Menggunakan

Saat Program Dijalankan:

1. Pilih jenis koneksi: Serial atau TCP/IP
2. Masukkan setting koneksi:
· Untuk Serial: COM port dan kecepatan
· Untuk TCP: alamat IP dan port
3. Pilih jenis transaksi dari menu
4. Ikuti petunjuk yang diminta
Contoh Transaksi Sale:
```
Pilih: 1 (Sale)
Amount: 50000
```
Program akan:

· Buat pesan transaksi Rp 50,000
· Kirim ke mesin EDC
· Tampilkan hasil: disetujui/ditolak + detailnya

šŸ“Š Hasil yang Ditampilkan

Setiap transaksi akan menampilkan:

· Status: ✅ Berhasil atau ❌ Gagal

· Kode Response:
· "00" = Disetujui
· "54" = Kartu expired
· "55" = PIN salah
· dll

· Nomor Approval: Kode dari bank
· RRN: Nomor referensi transaksi
· Invoice Number: Nomor struk
· Data Kartu: Jenis kartu dan issuer

⚠️ Hal Penting yang Perlu Diketahui

1. Harus Terhubung ke mesin EDC dulu sebelum transaksi
2. Format Data harus tepat sesuai spesifikasi AAA
3. Timeout bisa terjadi jika mesin lama merespons
4. Simpan Data invoice dan RRN untuk referensi
5. Settlement wajib dilakukan setiap hari

šŸ”§ Untuk Troubleshooting

· Gunakan Echo Test untuk cek koneksi
· Periksa kabel dan koneksi jaringan
· Pastikan mesin EDC dalam keadaan siap
· Cek log pesan yang dikirim/diterima

Program ini pada dasarnya adalah penerjemah antara komputer dengan mesin EDC, menggunakan bahasa (protokol) yang dimengerti oleh mesin EDC

0 Comment: