fixed ecr connectivity and functionalities

This commit is contained in:
kizzy 2026-07-30 16:56:43 +07:00
parent 048f802c18
commit 80a8c2b148
52 changed files with 2264 additions and 1708 deletions

View File

@ -102,11 +102,6 @@
</service> </service>
<service
android:name="com.utsmm.kbz.service.SerialPortService"
android:foregroundServiceType="connectedDevice"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View File

@ -40,10 +40,6 @@ import com.nexgo.oaf.apiv3.APIProxy;
import com.nexgo.oaf.apiv3.DeviceEngine; import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.device.pinpad.PinPad; import com.nexgo.oaf.apiv3.device.pinpad.PinPad;
import com.nexgo.oaf.apiv3.device.pinpad.WorkKeyTypeEnum; import com.nexgo.oaf.apiv3.device.pinpad.WorkKeyTypeEnum;
import com.nexgo.oaf.apiv3.device.serialport.SerialPortDriver;
import com.nexgo.oaf.apiv3.device.usbserial.OnUsbSerialReadListener;
import com.nexgo.oaf.apiv3.device.usbserial.UsbSerial;
import com.nexgo.oaf.apiv3.device.usbserial.UsbSerialCfgEntity;
import com.utsmyanmar.baselib.BaseApplication; import com.utsmyanmar.baselib.BaseApplication;
import com.utsmyanmar.baselib.repo.Repository; import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.baselib.ui.AnimationDialog; import com.utsmyanmar.baselib.ui.AnimationDialog;
@ -561,8 +557,8 @@ public class MainActivity extends AppCompatActivity implements
// Clean up RxJava disposables // Clean up RxJava disposables
compositeDisposable.clear(); compositeDisposable.clear();
// Reset screen mode // Reset screen mode
// Disconnect ECR // Disconnect serial port
ECRHelper.INSTANCE.disconnect(); ECRHelper.INSTANCE.releaseSerialPort();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar(); BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
} }

View File

@ -1,16 +1,7 @@
package com.utsmm.kbz; package com.utsmm.kbz;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
@ -38,16 +29,9 @@ import com.kizzy.cmhl.models.SaleRequest;
import com.kizzy.cmhl.models.SettlementRequest; import com.kizzy.cmhl.models.SettlementRequest;
import com.kizzy.cmhl.models.VoidQrPaymentRequest; import com.kizzy.cmhl.models.VoidQrPaymentRequest;
import com.kizzy.cmhl.models.VoidRequest; import com.kizzy.cmhl.models.VoidRequest;
import com.nexgo.oaf.apiv3.APIProxy;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity;
import com.nexgo.oaf.apiv3.device.serialport.SerialPortDriver;
import com.nexgo.oaf.apiv3.emv.AidEntity; import com.nexgo.oaf.apiv3.emv.AidEntity;
import com.nexgo.oaf.apiv3.emv.CapkEntity; import com.nexgo.oaf.apiv3.emv.CapkEntity;
//import com.pos.connection.bridge.binder.ECRConstant;
import com.utsmm.kbz.service.SerialPortService;
import com.utsmm.kbz.util.MockData; import com.utsmm.kbz.util.MockData;
//import com.utsmm.kbz.util.ecr.ModernECRFactory; //import com.utsmm.kbz.util.ecr.ModernECRFactory;
//import com.utsmm.kbz.util.ecr.ModernECRManager; //import com.utsmm.kbz.util.ecr.ModernECRManager;
@ -59,6 +43,7 @@ import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.DialogCallback; import com.utsmyanmar.baselib.util.DialogCallback;
import com.utsmyanmar.baselib.util.TerminalUtil; import com.utsmyanmar.baselib.util.TerminalUtil;
import com.utsmyanmar.checkxread.sdk.NexGoSDK; import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.ecr.ECRHelper;
import com.utsmyanmar.ecr.ECRProcess; import com.utsmyanmar.ecr.ECRProcess;
import com.utsmyanmar.ecr.data.ResultOf; import com.utsmyanmar.ecr.data.ResultOf;
import com.utsmyanmar.ecr.data.model.Transactions; import com.utsmyanmar.ecr.data.model.Transactions;
@ -68,7 +53,6 @@ import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.print.PrintHelper; import com.utsmyanmar.paylibs.print.PrintHelper;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt; import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.utils.POSUtil; import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation; import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType; import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
import com.utsmyanmar.paylibs.utils.enums.TransMenu; import com.utsmyanmar.paylibs.utils.enums.TransMenu;
@ -110,7 +94,6 @@ import com.utsmyanmar.paylibs.utils.params.Params;
//import com.utsmm.kbz.util.ecr.ModernECRFactory; //import com.utsmm.kbz.util.ecr.ModernECRFactory;
//import com.utsmm.kbz.util.ecr.ModernECRManager; //import com.utsmm.kbz.util.ecr.ModernECRManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@AndroidEntryPoint @AndroidEntryPoint
public class MainFragment extends DataBindingFragment { public class MainFragment extends DataBindingFragment {
@ -150,38 +133,43 @@ public class MainFragment extends DataBindingFragment {
mainViewModel = getFragmentScopeViewModel(MainViewModel.class); mainViewModel = getFragmentScopeViewModel(MainViewModel.class);
tmsProcessViewModel = getFragmentScopeViewModel(TMSProcessViewModel.class); tmsProcessViewModel = getFragmentScopeViewModel(TMSProcessViewModel.class);
// Initialize ModernECRManager
// ModernECRFactory factory = new ModernECRFactory(requireContext(), sharedViewModel);
// modernECRManager = new ViewModelProvider(this, factory).get(ModernECRManager.class);
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
// Register to listen for serial data
// Setup ECR Service if (SystemParamsOperation.getInstance().getECRStatus()) {
if(SystemParamsOperation.getInstance().getECRStatus()){ ECRHelper.INSTANCE.setOnECRReceive(bytes -> {
LocalBroadcastManager.getInstance(requireContext()) if (getActivity() == null) {
.registerReceiver(serialReceiver, return null;
new IntentFilter(SerialPortService.ACTION_DATA_RECEIVED));
// Start the service
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requireContext().startForegroundService(
new Intent(requireContext(), SerialPortService.class));
} }
requireActivity().runOnUiThread(() -> handleSerialData(bytes));
LogUtil.d(TAG,"ECR service initialized!"); return null;
});
ECRHelper.INSTANCE.initSerialPort(requireContext().getApplicationContext());
LogUtil.d(TAG, "ECR SerialPortDriver listening");
} }
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
LocalBroadcastManager.getInstance(requireContext()) ECRHelper.INSTANCE.setOnECRReceive(bytes -> null);
.unregisterReceiver(serialReceiver); }
private void handleSerialData(byte[] data) {
if (data == null || data.length == 0) {
return;
}
LogUtil.d(TAG, "Serial received len=" + data.length);
if (SystemParamsOperation.getInstance().isCMHLEnabled()) {
ECRActionCHML(data);
} else {
ECRAction(new String(data, StandardCharsets.UTF_8));
}
} }
@Override @Override
@ -244,33 +232,6 @@ public class MainFragment extends DataBindingFragment {
} }
} }
private BroadcastReceiver serialReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra(SerialPortService.EXTRA_DATA);
LogUtil.d("USB", "UI received: " + data);
// update your UI here
// LogUtil.d(TAG, "Received : " + msg);
if (sharedViewModel.isEcrFinished.getValue() != null && sharedViewModel.isEcrFinished.getValue()) {
if(SystemParamsOperation.getInstance().isCMHLEnabled()) {
ECRActionCHML(ByteUtil.hexStr2Bytes(data));
} else {
ECRAction(data);
}
} else {
if(SystemParamsOperation.getInstance().isCMHLEnabled()) {
ECRActionCHML(ByteUtil.hexStr2Bytes(data));
} else {
ECRAction(data);
}
}
}
};
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -314,9 +275,6 @@ public class MainFragment extends DataBindingFragment {
// enableHomeButton(); // enableHomeButton();
} }
private SerialPortDriver port;
private boolean isRunning = false;
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@ -412,19 +370,6 @@ public class MainFragment extends DataBindingFragment {
} }
private boolean checkECRServiceAppExistOrNot() {
PackageInfo packageInfo;
try {
packageInfo = requireActivity().getPackageManager().getPackageInfo("com.kizzy.ecrservice", 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
return packageInfo != null;
}
@Override @Override
protected DataBindingConfig getDataBindingConfig() { protected DataBindingConfig getDataBindingConfig() {

View File

@ -1,112 +0,0 @@
package com.utsmm.kbz.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.nexgo.oaf.apiv3.APIProxy;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity;
import com.nexgo.oaf.apiv3.device.serialport.SerialPortDriver;
import com.utsmm.kbz.R;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.nio.charset.StandardCharsets;
public class SerialPortService extends Service {
private static final String TAG = "USB";
private SerialPortDriver port;
private volatile boolean isRunning = false;
private Thread receiveThread;
// Broadcast action so any Activity/Fragment can observe
public static final String ACTION_DATA_RECEIVED = "com.yourapp.SERIAL_DATA";
public static final String EXTRA_DATA = "data";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForegroundNotification(); // required for foreground service
connectAndListen();
return START_STICKY; // restarts if killed
}
private void connectAndListen() {
DeviceEngine deviceEngine = APIProxy.getDeviceEngine(this);
port = deviceEngine.getSerialPortDriver(0);
SerialCfgEntity entity = new SerialCfgEntity();
entity.setBaudRate(115200);
entity.setDataBits(8);
entity.setParity('n');
entity.setStopBits(1);
int result = port.connect(entity);
if (result == SdkResult.Success) {
LogUtil.d(TAG, "Connected");
isRunning = true;
startReceiveThread();
} else {
LogUtil.e(TAG, "Connection failed");
}
}
private void startReceiveThread() {
receiveThread = new Thread(() -> {
byte[] buffer = new byte[2048];
while (isRunning) {
int len = port.recv(buffer, buffer.length, 3000);
if (len > 0) {
String msg = new String(buffer, 0, len, StandardCharsets.UTF_8);
LogUtil.d(TAG, "Received: " + msg);
broadcastData(msg); // notify UI
}
}
});
receiveThread.start();
}
private void broadcastData(String msg) {
Intent intent = new Intent(ACTION_DATA_RECEIVED);
intent.putExtra(EXTRA_DATA, msg);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onDestroy() {
isRunning = false;
if (port != null) port.disconnect();
super.onDestroy();
}
private void startForegroundNotification() {
NotificationChannel channel = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channel = new NotificationChannel(
"serial_channel", "Serial Port", NotificationManager.IMPORTANCE_LOW);
getSystemService(NotificationManager.class).createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "serial_channel")
.setContentTitle("Serial Port Active")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build();
startForeground(1, notification);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) { return null; }
}

View File

@ -606,9 +606,9 @@ public class TMSSetupsImpl implements TMSSetups{
} else if (TextUtils.equals(name, "upi_tc_enabled")) { } else if (TextUtils.equals(name, "upi_tc_enabled")) {
SystemParamsOperation.getInstance().setUpiTCEnabled(parseBoolean(data)); SystemParamsOperation.getInstance().setUpiTCEnabled(parseBoolean(data));
} else if (TextUtils.equals(name,"master_terminal_capability")) { } else if (TextUtils.equals(name,"master_terminal_capability")) {
if(!data.isEmpty()) { // if(!data.isEmpty()) {
emvParamOperation.updatePayPassTerminalCapability(data); // emvParamOperation.updatePayPassTerminalCapability(data);
} // }
} else if (TextUtils.equals(name,"speedup_contactless_enable")) { } else if (TextUtils.equals(name,"speedup_contactless_enable")) {
SystemParamsOperation.getInstance().setSpeedUpContactless(parseBoolean(data)); SystemParamsOperation.getInstance().setSpeedUpContactless(parseBoolean(data));
} else if (TextUtils.equals(name,"manual_entry_pin_enable")) { } else if (TextUtils.equals(name,"manual_entry_pin_enable")) {

1
cmhl/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

35
cmhl/build.gradle Normal file
View File

@ -0,0 +1,35 @@
plugins {
id 'com.android.library'
}
android {
namespace 'com.kizzy.cmhl'
compileSdk 34
defaultConfig {
minSdk 23 // Lowered to 23 for broader device compatibility
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
}

0
cmhl/consumer-rules.pro Normal file
View File

21
cmhl/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,26 @@
package com.kizzy.cmhl;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.kizzy.cmhl.test", appContext.getPackageName());
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,148 @@
package com.kizzy.cmhl;
import com.kizzy.cmhl.models.EcrCommand;
import com.kizzy.cmhl.models.EcrPacket;
import com.kizzy.cmhl.utils.ByteUtils;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
public class EcrManager {
/**
* Builds the full packet bytes from an EcrCommand (Request or Response).
* Structure: STX LLL(2) VERSION COMMAND DATA... ETX LRC
* LLL is BCD encoded length of (VERSION + COMMAND + DATA).
* LRC = XOR of (LLL + VERSION + DATA + ETX).
*/
public static byte[] buildPacket(EcrCommand command) {
byte[] tlvData = command.toTlvBytes();
int bodyLength = 1 + 1 + tlvData.length;
byte[] lll = ByteUtils.intToBcd(bodyLength);
ByteBuffer buffer = ByteBuffer.allocate(1 + 2 + bodyLength + 1 + 1);
buffer.put(EcrProtocol.STX);
buffer.put(lll);
buffer.put(EcrProtocol.VERSION);
buffer.put(command.getCommandCode());
buffer.put(tlvData);
buffer.put(EcrProtocol.ETX);
byte[] packet = buffer.array();
byte lrc = 0;
int etxIndex = packet.length - 2;
for (int i = 1; i < etxIndex; i++) { // added <= instead of <
lrc ^= packet[i];
}
packet[packet.length - 1] = lrc;
return packet;
}
// public static byte[] buildPacket(EcrCommand command) {
// byte[] tlvData = command.toTlvBytes();
// int bodyLength = 1 + 1 + tlvData.length; // Version(1) + Command(1) + Data
//
// // Convert bodyLength to BCD
// byte[] lll = ByteUtils.intToBcd(bodyLength);
//
// ByteBuffer buffer = ByteBuffer.allocate(1 + 2 + bodyLength + 1 + 1);
//
// buffer.put(EcrProtocol.STX);
// buffer.put(lll);
// buffer.put(EcrProtocol.VERSION);
// buffer.put(command.getCommandCode());
// buffer.put(tlvData);
// buffer.put(EcrProtocol.ETX);
//
// byte[] packet = buffer.array();
//
// // Calculate LRC
// // From LLL (index 1) to ETX (index packet.length - 2), inclusive
// byte lrc = 0;
// for (int i = 1; i < packet.length - 1; i++) {
// lrc ^= packet[i];
// }
// packet[packet.length - 1] = lrc;
//
// return packet;
// }
/**
* Parses raw bytes into an EcrPacket.
* Validates STX and LRC.
*/
public static EcrPacket parsePacket(byte[] packet) throws Exception {
if (packet == null || packet.length < 5) {
throw new Exception("Invalid packet length");
}
if (packet[0] != EcrProtocol.STX) {
throw new Exception("Invalid STX");
}
byte receivedLrc = packet[packet.length - 1];
byte calculatedLrc = 0;
for (int i = 1; i < packet.length - 1; i++) {
if (i == 4) continue;
calculatedLrc ^= packet[i];
}
if (receivedLrc != calculatedLrc) {
System.out.println("LRC Mismatch: Recv=" + receivedLrc + " Calc=" + calculatedLrc);
}
int lll = bcdToInt(packet[1], packet[2]);
byte commandCode = packet[4];
int dataEnd = packet.length - 2;
byte[] data = new byte[dataEnd - 5];
System.arraycopy(packet, 5, data, 0, data.length);
Map<Byte, byte[]> tlvMap = parseTlv(data);
return new EcrPacket(commandCode, tlvMap);
}
private static int bcdToInt(byte b1, byte b2) {
int thousands = (b1 >> 4) & 0x0F;
int hundreds = b1 & 0x0F;
int tens = (b2 >> 4) & 0x0F;
int ones = b2 & 0x0F;
return thousands * 1000 + hundreds * 100 + tens * 10 + ones;
}
private static Map<Byte, byte[]> parseTlv(byte[] data) {
Map<Byte, byte[]> map = new HashMap<>();
int i = 0;
while (i < data.length) {
if (i + 1 >= data.length) break;
byte tag = data[i++];
int len = data[i++] & 0xFF;
if (i + len > data.length) {
break;
}
byte[] value = new byte[len];
System.arraycopy(data, i, value, 0, len);
map.put(tag, value);
i += len;
}
return map;
}
}

View File

@ -0,0 +1,40 @@
package com.kizzy.cmhl;
public class EcrProtocol {
public static final byte STX = 0x02;
public static final byte ETX = 0x03;
public static final byte VERSION = 0x01;
public static final byte ACK = 0x06;
// Commands
public static final byte CMD_PING = 0x01;
public static final byte CMD_SALE = 0x03;
public static final byte CMD_VOID = 0x04;
public static final byte CMD_SETTLEMENT = 0x05;
public static final byte CMD_PRE_AUTH = 0x06;
public static final byte CMD_PRE_AUTH_COMPLETION = 0x07;
public static final byte CMD_PRE_AUTH_CANCELLATION = 0x08;
public static final byte CMD_QR_PAYMENT = 0x0B;
public static final byte CMD_VOID_QR_PAYMENT = 0x0C;
// Tags
public static final byte TAG_PAN = 0x02;
public static final byte TAG_AMOUNT = 0x04;
public static final byte TAG_STAN = 0x11;
public static final byte TAG_TIME = 0x12;
public static final byte TAG_DATE = 0x13;
public static final byte TAG_EXP_DATE = 0x14;
public static final byte TAG_CARD_TYPE = 0x15;
public static final byte TAG_PAYMENT_TYPE = 0x16;
public static final byte TAG_POS_ENTRY = 0x22;
public static final byte TAG_RRN = 0x37;
public static final byte TAG_APPROVAL_CODE = 0x38;
public static final byte TAG_RESPONSE_CODE = 0x39;
public static final byte TAG_TID = 0x41;
public static final byte TAG_MID = 0x42;
public static final byte TAG_CURRENCY_CODE = 0x49;
public static final byte TAG_INVOICE_NUMBER = 0x62;
public static final String CARD_TYPE_ALL = "00"; //
}

View File

@ -0,0 +1,49 @@
package com.kizzy.cmhl;
import com.kizzy.cmhl.models.EcrPacket;
import com.kizzy.cmhl.models.*;
public class EcrRequestDispatcher {
public static void dispatch(EcrPacket packet, EcrRequestListener listener) {
if (packet == null || listener == null) return;
try {
switch (packet.getCommandCode()) {
case EcrProtocol.CMD_PING:
listener.onPingRequest(new PingRequest(packet));
break;
case EcrProtocol.CMD_SALE:
listener.onSaleRequest(new SaleRequest(packet));
break;
case EcrProtocol.CMD_VOID:
listener.onVoidRequest(new VoidRequest(packet));
break;
case EcrProtocol.CMD_SETTLEMENT:
listener.onSettlementRequest(new SettlementRequest(packet));
break;
case EcrProtocol.CMD_PRE_AUTH:
listener.onPreAuthRequest(new PreAuthRequest(packet));
break;
case EcrProtocol.CMD_PRE_AUTH_COMPLETION:
listener.onPreAuthCompletionRequest(new PreAuthCompletionRequest(packet));
break;
case EcrProtocol.CMD_PRE_AUTH_CANCELLATION:
listener.onPreAuthCancellationRequest(new PreAuthCancellationRequest(packet));
break;
case EcrProtocol.CMD_QR_PAYMENT:
listener.onQrPaymentRequest(new QrPaymentRequest(packet));
break;
case EcrProtocol.CMD_VOID_QR_PAYMENT:
listener.onVoidQrPaymentRequest(new VoidQrPaymentRequest(packet));
break;
default:
listener.onUnknownRequest(packet);
break;
}
} catch (Exception e) {
listener.onUnknownRequest(packet);
}
}
}

View File

@ -0,0 +1,17 @@
package com.kizzy.cmhl;
import com.kizzy.cmhl.models.*;
public interface EcrRequestListener {
void onPingRequest(PingRequest request);
void onSaleRequest(SaleRequest request);
void onVoidRequest(VoidRequest request);
void onSettlementRequest(SettlementRequest request);
void onPreAuthRequest(PreAuthRequest request);
void onPreAuthCompletionRequest(PreAuthCompletionRequest request);
void onPreAuthCancellationRequest(PreAuthCancellationRequest request);
void onQrPaymentRequest(QrPaymentRequest request);
void onVoidQrPaymentRequest(VoidQrPaymentRequest request);
void onUnknownRequest(EcrPacket packet);
}

View File

@ -0,0 +1,63 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import com.kizzy.cmhl.utils.ByteUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public abstract class BaseEcrResponse implements EcrCommand {
protected String responseCode;
protected String tid;
protected String mid;
protected String time;
protected String date;
// Getters
public String getResponseCode() { return responseCode; }
public String getTid() { return tid; }
public String getMid() { return mid; }
public String getTime() { return time; }
public String getDate() { return date; }
public boolean isApproved() {
return "00".equals(responseCode);
}
protected void writeCommonTags(ByteArrayOutputStream baos) throws IOException {
writeTag(baos, EcrProtocol.TAG_RESPONSE_CODE, responseCode);
writeTag(baos, EcrProtocol.TAG_TIME, time);
writeTag(baos, EcrProtocol.TAG_DATE, date);
writeTag(baos, EcrProtocol.TAG_TID, tid);
writeTag(baos, EcrProtocol.TAG_MID, mid);
}
protected void parseCommonTags(EcrPacket packet) {
this.responseCode = packet.getValueAsString(EcrProtocol.TAG_RESPONSE_CODE);
this.time = packet.getValueAsString(EcrProtocol.TAG_TIME);
this.date = packet.getValueAsString(EcrProtocol.TAG_DATE);
this.tid = packet.getValueAsString(EcrProtocol.TAG_TID);
this.mid = packet.getValueAsString(EcrProtocol.TAG_MID);
}
protected void writeTag(ByteArrayOutputStream baos, byte tag, String value) throws IOException {
if (value != null) {
byte[] bytes = value.getBytes(StandardCharsets.US_ASCII);
baos.write(tag);
baos.write((byte) bytes.length);
baos.write(bytes);
}
}
protected void writeTagBinary(ByteArrayOutputStream baos, byte tag, String hexString) throws IOException {
if (hexString != null) {
byte[] bytes = ByteUtils.hexStringToByteArray(hexString);
baos.write(tag);
baos.write((byte) bytes.length);
baos.write(bytes);
}
}
}

View File

@ -0,0 +1,6 @@
package com.kizzy.cmhl.models;
public interface EcrCommand {
byte getCommandCode();
byte[] toTlvBytes();
}

View File

@ -0,0 +1,35 @@
package com.kizzy.cmhl.models;
import java.util.Map;
/**
* Represents a generic parsed ECR packet (Command + TLV Data).
* Can be a Request or a Response depending on context.
*/
public class EcrPacket {
private byte commandCode;
private Map<Byte, byte[]> tlvData;
public EcrPacket(byte commandCode, Map<Byte, byte[]> tlvData) {
this.commandCode = commandCode;
this.tlvData = tlvData;
}
public byte getCommandCode() {
return commandCode;
}
public Map<Byte, byte[]> getTlvData() {
return tlvData;
}
public byte[] getValue(byte tag) {
return tlvData.get(tag);
}
public String getValueAsString(byte tag) {
byte[] val = tlvData.get(tag);
if (val == null) return null;
return new String(val);
}
}

View File

@ -0,0 +1,26 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
public class PingRequest implements EcrCommand {
public PingRequest() {}
public PingRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PING) {
throw new IllegalArgumentException("Invalid command for PingRequest");
}
}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PING;
}
@Override
public byte[] toTlvBytes() {
return new byte[0]; // Empty body
}
}

View File

@ -0,0 +1,42 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class PingResponse extends BaseEcrResponse {
public PingResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PING) {
}
parseCommonTags(packet);
}
private PingResponse(Builder builder) {
this.responseCode = builder.responseCode;
}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PING;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeTag(baos, EcrProtocol.TAG_RESPONSE_CODE, responseCode);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public PingResponse build() { return new PingResponse(this); }
}
}

View File

@ -0,0 +1,69 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PreAuthCancellationRequest implements EcrCommand {
private String referenceNumber;
private String cardType;
private String approvalCode;
public PreAuthCancellationRequest(String referenceNumber, String cardType, String approvalCode) {
this.referenceNumber = referenceNumber;
this.cardType = cardType;
this.approvalCode = approvalCode;
}
public PreAuthCancellationRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PRE_AUTH_CANCELLATION) {
throw new IllegalArgumentException("Invalid command");
}
this.referenceNumber = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.cardType = packet.getValueAsString(EcrProtocol.TAG_CARD_TYPE);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
}
public String getCardType() { return cardType; }
public String getReferenceNumber() { return referenceNumber; }
public String getApprovalCode() { return approvalCode; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PRE_AUTH_CANCELLATION;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (referenceNumber != null) {
byte[] bytes = referenceNumber.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_RRN);
baos.write((byte) bytes.length);
baos.write(bytes);
}
if (approvalCode != null) {
byte[] bytes = approvalCode.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_APPROVAL_CODE);
baos.write((byte) bytes.length);
baos.write(bytes);
}
if (cardType != null) {
byte[] bytes = cardType.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_CARD_TYPE);
baos.write((byte) bytes.length);
baos.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,70 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class PreAuthCancellationResponse extends BaseEcrResponse {
private String invoiceNumber;
private String approvalCode;
public PreAuthCancellationResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PRE_AUTH_CANCELLATION) {}
parseCommonTags(packet);
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
}
private PreAuthCancellationResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.time = builder.time;
this.date = builder.date;
this.tid = builder.tid;
this.mid = builder.mid;
this.invoiceNumber = builder.invoiceNumber;
this.approvalCode = builder.approvalCode;
}
public String getInvoiceNumber() { return invoiceNumber; }
public String getApprovalCode() { return approvalCode; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PRE_AUTH_CANCELLATION;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeCommonTags(baos);
writeTag(baos, EcrProtocol.TAG_INVOICE_NUMBER, invoiceNumber);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
private String time;
private String date;
private String tid;
private String mid;
private String invoiceNumber;
private String approvalCode;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public PreAuthCancellationResponse build() { return new PreAuthCancellationResponse(this); }
}
}

View File

@ -0,0 +1,70 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PreAuthCompletionRequest implements EcrCommand {
private String referenceNumber;
private String cardType;
private String approvalCode;
public PreAuthCompletionRequest(String referenceNumber, String cardType, String approvalCode) {
this.referenceNumber = referenceNumber;
this.cardType = cardType;
this.approvalCode = approvalCode;
}
public PreAuthCompletionRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PRE_AUTH_COMPLETION) {
throw new IllegalArgumentException("Invalid command");
}
this.referenceNumber = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.cardType = packet.getValueAsString(EcrProtocol.TAG_CARD_TYPE);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
}
public String getCardType() { return cardType; }
public String getReferenceNumber() { return referenceNumber; }
public String getApprovalCode() { return approvalCode; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PRE_AUTH_COMPLETION;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (referenceNumber != null) {
byte[] bytes = referenceNumber.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_RRN);
baos.write((byte) bytes.length);
baos.write(bytes);
}
if (approvalCode != null) {
byte[] bytes = approvalCode.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_APPROVAL_CODE);
baos.write((byte) bytes.length);
baos.write(bytes);
}
if (cardType != null) {
byte[] bytes = cardType.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_CARD_TYPE);
baos.write((byte) bytes.length);
baos.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,117 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class PreAuthCompletionResponse extends BaseEcrResponse {
private String amount;
private String pan;
private String expDate;
private String stan;
private String approvalCode;
private String rrn;
private String invoiceNumber;
private String entry;
public PreAuthCompletionResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PRE_AUTH_COMPLETION) {}
parseCommonTags(packet);
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.pan = packet.getValueAsString(EcrProtocol.TAG_PAN);
this.expDate = packet.getValueAsString(EcrProtocol.TAG_EXP_DATE);
this.stan = packet.getValueAsString(EcrProtocol.TAG_STAN);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
this.rrn = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
}
private PreAuthCompletionResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.time = builder.time;
this.date = builder.date;
this.tid = builder.tid;
this.mid = builder.mid;
this.amount = builder.amount;
this.pan = builder.pan;
this.expDate = builder.expDate;
this.stan = builder.stan;
this.approvalCode = builder.approvalCode;
this.rrn = builder.rrn;
this.invoiceNumber = builder.invoiceNumber;
this.entry = builder.entry;
}
// Getters
public String getAmount() { return amount; }
public String getPan() { return pan; }
public String getExpDate() { return expDate; }
public String getStan() { return stan; }
public String getApprovalCode() { return approvalCode; }
public String getRrn() { return rrn; }
public String getInvoiceNumber() { return invoiceNumber; }
public String getEntry() { return entry; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PRE_AUTH_COMPLETION;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeCommonTags(baos);
writeTag(baos, EcrProtocol.TAG_AMOUNT, amount);
writeTag(baos, EcrProtocol.TAG_PAN, pan);
writeTag(baos, EcrProtocol.TAG_EXP_DATE, expDate);
writeTag(baos, EcrProtocol.TAG_STAN, stan);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
writeTag(baos, EcrProtocol.TAG_RRN, rrn);
writeTag(baos, EcrProtocol.TAG_INVOICE_NUMBER, invoiceNumber);
writeTag(baos, EcrProtocol.TAG_POS_ENTRY, entry);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
private String time;
private String date;
private String tid;
private String mid;
private String amount;
private String pan;
private String expDate;
private String stan;
private String approvalCode;
private String rrn;
private String invoiceNumber;
private String entry;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setAmount(String amount) { this.amount = amount; return this; }
public Builder setPan(String pan) { this.pan = pan; return this; }
public Builder setExpDate(String expDate) { this.expDate = expDate; return this; }
public Builder setStan(String stan) { this.stan = stan; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public Builder setRrn(String rrn) { this.rrn = rrn; return this; }
public Builder setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; return this; }
public Builder setEntry(String entry) { this.entry = entry; return this; }
public PreAuthCompletionResponse build() { return new PreAuthCompletionResponse(this); }
}
}

View File

@ -0,0 +1,57 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PreAuthRequest implements EcrCommand {
private String amount;
public PreAuthRequest(String amount) {
this.amount = formatAmount(amount);
}
public PreAuthRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PRE_AUTH) {
throw new IllegalArgumentException("Invalid command");
}
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
}
public String getAmount() { return amount; }
private String formatAmount(String amount) {
if (amount == null) return "000000000000";
if (amount.length() > 12) return amount.substring(0, 12);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 12 - amount.length(); i++) {
sb.append('0');
}
sb.append(amount);
return sb.toString();
}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PRE_AUTH;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (amount != null) {
byte[] bytes = amount.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_AMOUNT);
baos.write((byte) bytes.length);
baos.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,124 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class PreAuthResponse extends BaseEcrResponse {
private String amount;
private String pan;
private String expDate;
private String stan;
private String approvalCode;
private String rrn;
private String invoiceNumber;
private String cardType;
private String entry;
public PreAuthResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_PRE_AUTH) {}
parseCommonTags(packet);
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.pan = packet.getValueAsString(EcrProtocol.TAG_PAN);
this.expDate = packet.getValueAsString(EcrProtocol.TAG_EXP_DATE);
this.stan = packet.getValueAsString(EcrProtocol.TAG_STAN);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
this.rrn = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
}
private PreAuthResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.time = builder.time;
this.date = builder.date;
this.tid = builder.tid;
this.mid = builder.mid;
this.amount = builder.amount;
this.pan = builder.pan;
this.expDate = builder.expDate;
this.stan = builder.stan;
this.approvalCode = builder.approvalCode;
this.rrn = builder.rrn;
this.invoiceNumber = builder.invoiceNumber;
this.entry = builder.entry;
this.cardType = builder.cardType;
}
// Getters
public String getAmount() { return amount; }
public String getPan() { return pan; }
public String getExpDate() { return expDate; }
public String getStan() { return stan; }
public String getApprovalCode() { return approvalCode; }
public String getRrn() { return rrn; }
public String getInvoiceNumber() { return invoiceNumber; }
public String getEntry() {return entry;}
public String getCardType() {return cardType;}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_PRE_AUTH;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeCommonTags(baos);
writeTag(baos, EcrProtocol.TAG_AMOUNT, amount);
writeTag(baos, EcrProtocol.TAG_PAN, pan);
writeTag(baos, EcrProtocol.TAG_EXP_DATE, expDate);
writeTag(baos, EcrProtocol.TAG_STAN, stan);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
writeTag(baos, EcrProtocol.TAG_RRN, rrn);
writeTag(baos, EcrProtocol.TAG_INVOICE_NUMBER, invoiceNumber);
writeTag(baos, EcrProtocol.TAG_POS_ENTRY, entry);
writeTag(baos, EcrProtocol.TAG_CARD_TYPE, cardType);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
private String time;
private String date;
private String tid;
private String mid;
private String amount;
private String pan;
private String expDate;
private String stan;
private String approvalCode;
private String rrn;
private String invoiceNumber;
private String cardType;
private String entry;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setAmount(String amount) { this.amount = amount; return this; }
public Builder setPan(String pan) { this.pan = pan; return this; }
public Builder setExpDate(String expDate) { this.expDate = expDate; return this; }
public Builder setStan(String stan) { this.stan = stan; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public Builder setRrn(String rrn) { this.rrn = rrn; return this; }
public Builder setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; return this; }
public Builder setCardType(String cardType) { this.cardType = cardType; return this; }
public Builder setEntry(String entry) { this.entry = entry; return this;}
public PreAuthResponse build() { return new PreAuthResponse(this); }
}
}

View File

@ -0,0 +1,74 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class QrPaymentRequest implements EcrCommand {
private String amount; // 12 digits
private String currencyCode; // 3 digits
public QrPaymentRequest(String amount, String currencyCode) {
this.amount = formatAmount(amount);
this.currencyCode = currencyCode;
}
public QrPaymentRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_QR_PAYMENT) {
throw new IllegalArgumentException("Invalid command code for QrPaymentRequest");
}
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.currencyCode = packet.getValueAsString(EcrProtocol.TAG_CURRENCY_CODE);
}
public String getAmount() {
return amount;
}
public String getCurrencyCode() {
return currencyCode;
}
private String formatAmount(String amount) {
if (amount == null) return "000000000000";
if (amount.length() > 12) return amount.substring(0, 12);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 12 - amount.length(); i++) {
sb.append('0');
}
sb.append(amount);
return sb.toString();
}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_QR_PAYMENT;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (amount != null) {
byte[] amountBytes = amount.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_AMOUNT);
baos.write((byte) amountBytes.length);
baos.write(amountBytes);
}
if (currencyCode != null) {
byte[] currencyBytes = currencyCode.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_CURRENCY_CODE);
baos.write((byte) currencyBytes.length);
baos.write(currencyBytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,155 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class QrPaymentResponse implements EcrCommand {
private String responseCode;
private String amount;
private String pan;
private String expDate;
private String stan;
private String time;
private String date;
private String approvalCode;
private String rrn;
private String posEntryMode;
private String tid;
private String mid;
private String invoiceNumber;
private String paymentType;
private String currencyCode;
public QrPaymentResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_QR_PAYMENT) {
}
this.responseCode = packet.getValueAsString(EcrProtocol.TAG_RESPONSE_CODE);
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.pan = packet.getValueAsString(EcrProtocol.TAG_PAN);
this.expDate = packet.getValueAsString(EcrProtocol.TAG_EXP_DATE);
this.stan = packet.getValueAsString(EcrProtocol.TAG_STAN);
this.time = packet.getValueAsString(EcrProtocol.TAG_TIME);
this.date = packet.getValueAsString(EcrProtocol.TAG_DATE);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
this.rrn = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.posEntryMode = packet.getValueAsString(EcrProtocol.TAG_POS_ENTRY);
this.tid = packet.getValueAsString(EcrProtocol.TAG_TID);
this.mid = packet.getValueAsString(EcrProtocol.TAG_MID);
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
this.paymentType = packet.getValueAsString(EcrProtocol.TAG_PAYMENT_TYPE);
this.currencyCode = packet.getValueAsString(EcrProtocol.TAG_CURRENCY_CODE);
}
private QrPaymentResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.amount = builder.amount;
this.pan = builder.pan;
this.expDate = builder.expDate;
this.stan = builder.stan;
this.time = builder.time;
this.date = builder.date;
this.approvalCode = builder.approvalCode;
this.rrn = builder.rrn;
this.posEntryMode = builder.posEntryMode;
this.tid = builder.tid;
this.mid = builder.mid;
this.invoiceNumber = builder.invoiceNumber;
this.paymentType = builder.paymentType;
this.currencyCode = builder.currencyCode;
}
public boolean isApproved() {
return "00".equals(responseCode);
}
// Getters
public String getResponseCode() { return responseCode; }
public String getAmount() { return amount; }
public String getPan() { return pan; }
public String getInvoiceNumber() { return invoiceNumber; }
public String getApprovalCode() { return approvalCode; }
public String getRrn() { return rrn; }
public String getTid() { return tid; }
public String getMid() { return mid; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_QR_PAYMENT;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeTag(baos, EcrProtocol.TAG_AMOUNT, amount);
writeTag(baos, EcrProtocol.TAG_RESPONSE_CODE, responseCode);
writeTag(baos, EcrProtocol.TAG_PAN, pan);
writeTag(baos, EcrProtocol.TAG_EXP_DATE, expDate);
writeTag(baos, EcrProtocol.TAG_STAN, stan);
writeTag(baos, EcrProtocol.TAG_TIME, time);
writeTag(baos, EcrProtocol.TAG_DATE, date);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
writeTag(baos, EcrProtocol.TAG_RRN, rrn);
writeTag(baos, EcrProtocol.TAG_POS_ENTRY, posEntryMode);
writeTag(baos, EcrProtocol.TAG_TID, tid);
writeTag(baos, EcrProtocol.TAG_MID, mid);
writeTag(baos, EcrProtocol.TAG_INVOICE_NUMBER, invoiceNumber);
writeTag(baos, EcrProtocol.TAG_PAYMENT_TYPE, paymentType);
writeTag(baos, EcrProtocol.TAG_CURRENCY_CODE, currencyCode);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
private void writeTag(ByteArrayOutputStream baos, byte tag, String value) throws IOException {
if (value != null) {
byte[] bytes = value.getBytes(StandardCharsets.US_ASCII);
baos.write(tag);
baos.write((byte) bytes.length);
baos.write(bytes);
}
}
public static class Builder {
private String responseCode;
private String amount;
private String pan;
private String expDate;
private String stan;
private String time;
private String date;
private String approvalCode;
private String rrn;
private String posEntryMode;
private String tid;
private String mid;
private String invoiceNumber;
private String paymentType;
private String currencyCode;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setAmount(String amount) { this.amount = amount; return this; }
public Builder setPan(String pan) { this.pan = pan; return this; }
public Builder setExpDate(String expDate) { this.expDate = expDate; return this; }
public Builder setStan(String stan) { this.stan = stan; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public Builder setRrn(String rrn) { this.rrn = rrn; return this; }
public Builder setPosEntryMode(String posEntryMode) { this.posEntryMode = posEntryMode; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; return this; }
public Builder setPaymentType(String paymentType) { this.paymentType = paymentType; return this; }
public Builder setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; return this; }
public QrPaymentResponse build() {
return new QrPaymentResponse(this);
}
}
}

View File

@ -0,0 +1,57 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class SaleRequest implements EcrCommand {
private String amount;
public SaleRequest(String amount) {
this.amount = formatAmount(amount);
}
public SaleRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_SALE) {
throw new IllegalArgumentException("Invalid command");
}
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
}
public String getAmount() { return amount; }
private String formatAmount(String amount) {
if (amount == null) return "000000000000";
if (amount.length() > 12) return amount.substring(0, 12);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 12 - amount.length(); i++) {
sb.append('0');
}
sb.append(amount);
return sb.toString();
}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_SALE;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (amount != null) {
byte[] bytes = amount.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_AMOUNT);
baos.write((byte) bytes.length);
baos.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,140 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class SaleResponse extends BaseEcrResponse {
private String amount;
private String pan;
private String expDate;
private String stan;
private String approvalCode;
private String referenceNum;
private String invoiceNumber;
private String entry;
private String currency;
private String cardType;
public SaleResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_SALE) {}
parseCommonTags(packet);
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.pan = packet.getValueAsString(EcrProtocol.TAG_PAN);
this.expDate = packet.getValueAsString(EcrProtocol.TAG_EXP_DATE);
this.stan = packet.getValueAsString(EcrProtocol.TAG_STAN);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
this.referenceNum = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
}
private SaleResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.time = builder.time;
this.date = builder.date;
this.tid = builder.tid;
this.mid = builder.mid;
this.amount = builder.amount;
this.pan = builder.pan;
this.expDate = builder.expDate;
this.stan = builder.stan;
this.approvalCode = builder.approvalCode;
this.referenceNum = builder.referenceNum;
this.invoiceNumber = builder.invoiceNumber;
this.entry = builder.entry;
this.cardType = builder.cardType;
this.currency = builder.currency;
}
// Getters
public String getAmount() { return amount; }
public String getPan() { return pan; }
public String getExpDate() { return expDate; }
public String getStan() { return stan; }
public String getApprovalCode() { return approvalCode; }
public String getReferenceNum() { return referenceNum; }
public String getInvoiceNumber() { return invoiceNumber; }
public String getEntry() { return entry; }
public String getCardType() { return cardType; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_SALE;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
// writeCommonTags(baos);
writeTag(baos, EcrProtocol.TAG_AMOUNT, amount);
writeTag(baos, EcrProtocol.TAG_RESPONSE_CODE, responseCode);
writeTag(baos, EcrProtocol.TAG_PAN, pan);
writeTag(baos, EcrProtocol.TAG_EXP_DATE, expDate);
writeTag(baos, EcrProtocol.TAG_STAN, stan);
writeTag(baos, EcrProtocol.TAG_TIME, time);
writeTag(baos, EcrProtocol.TAG_DATE, date);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
writeTag(baos, EcrProtocol.TAG_RRN, referenceNum);
writeTag(baos, EcrProtocol.TAG_POS_ENTRY, entry);
writeTag(baos, EcrProtocol.TAG_TID, tid);
writeTag(baos, EcrProtocol.TAG_MID, mid);
writeTag(baos, EcrProtocol.TAG_INVOICE_NUMBER, invoiceNumber);
writeTagBinary(baos, EcrProtocol.TAG_CARD_TYPE, cardType);
writeTag(baos, EcrProtocol.TAG_CURRENCY_CODE, currency);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
private String time;
private String date;
private String tid;
private String mid;
private String amount;
private String pan;
private String expDate;
private String stan;
private String entry;
private String cardType;
private String approvalCode;
private String referenceNum;
private String invoiceNumber;
private String currency;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setAmount(String amount) { this.amount = amount; return this; }
public Builder setPan(String pan) { this.pan = pan; return this; }
public Builder setExpDate(String expDate) { this.expDate = expDate; return this; }
public Builder setStan(String stan) { this.stan = stan; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public Builder setReferenceNum(String rrn) { this.referenceNum = rrn; return this; }
public Builder setEntry(String entry) { this.entry = entry; return this;}
public Builder setCardType(String cardType) { this.cardType = cardType; return this;}
public Builder setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; return this; }
public Builder setCurrency(String currency) { this.currency = currency; return this; }
public SaleResponse build() { return new SaleResponse(this); }
}
}

View File

@ -0,0 +1,46 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class SettlementRequest implements EcrCommand {
private String cardType;
public SettlementRequest(String cardType) {
this.cardType = cardType;
}
public SettlementRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_SETTLEMENT) {
throw new IllegalArgumentException("Invalid command");
}
this.cardType = packet.getValueAsString(EcrProtocol.TAG_CARD_TYPE);
}
public String getCardType() { return cardType; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_SETTLEMENT;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (cardType != null) {
byte[] bytes = cardType.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_CARD_TYPE);
baos.write((byte) bytes.length);
baos.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,55 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class SettlementResponse extends BaseEcrResponse {
public SettlementResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_SETTLEMENT) {}
parseCommonTags(packet);
}
private SettlementResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.time = builder.time;
this.date = builder.date;
this.tid = builder.tid;
this.mid = builder.mid;
}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_SETTLEMENT;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeCommonTags(baos);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
private String time;
private String date;
private String tid;
private String mid;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public SettlementResponse build() { return new SettlementResponse(this); }
}
}

View File

@ -0,0 +1,60 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class VoidQrPaymentRequest implements EcrCommand {
private String invoiceNumber;
private String paymentType;
public VoidQrPaymentRequest(String invoiceNumber, String paymentType) {
this.invoiceNumber = invoiceNumber;
this.paymentType = paymentType;
}
public VoidQrPaymentRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_VOID_QR_PAYMENT) {
throw new IllegalArgumentException("Invalid command code for VoidQrPaymentRequest");
}
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
this.paymentType = packet.getValueAsString(EcrProtocol.TAG_PAYMENT_TYPE);
}
public String getInvoiceNumber() { return invoiceNumber; }
public String getPaymentType() { return paymentType; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_VOID_QR_PAYMENT;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
// Invoice Number
if (invoiceNumber != null) {
byte[] invoiceBytes = invoiceNumber.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_INVOICE_NUMBER);
baos.write((byte) invoiceBytes.length);
baos.write(invoiceBytes);
}
// Payment Type
if (paymentType != null) {
byte[] typeBytes = paymentType.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_PAYMENT_TYPE);
baos.write((byte) typeBytes.length);
baos.write(typeBytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,138 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class VoidQrPaymentResponse implements EcrCommand {
private String responseCode;
private String amount;
private String pan;
private String expDate;
private String stan;
private String time;
private String date;
private String approvalCode;
private String rrn;
private String posEntryMode;
private String tid;
private String mid;
private String currencyCode;
// Parsing Constructor (ECR Role)
public VoidQrPaymentResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_VOID_QR_PAYMENT) {
}
this.responseCode = packet.getValueAsString(EcrProtocol.TAG_RESPONSE_CODE);
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.pan = packet.getValueAsString(EcrProtocol.TAG_PAN);
this.expDate = packet.getValueAsString(EcrProtocol.TAG_EXP_DATE);
this.stan = packet.getValueAsString(EcrProtocol.TAG_STAN);
this.time = packet.getValueAsString(EcrProtocol.TAG_TIME);
this.date = packet.getValueAsString(EcrProtocol.TAG_DATE);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
this.rrn = packet.getValueAsString(EcrProtocol.TAG_RRN);
this.posEntryMode = packet.getValueAsString(EcrProtocol.TAG_POS_ENTRY);
this.tid = packet.getValueAsString(EcrProtocol.TAG_TID);
this.mid = packet.getValueAsString(EcrProtocol.TAG_MID);
this.currencyCode = packet.getValueAsString(EcrProtocol.TAG_CURRENCY_CODE);
}
// Building Constructor (EDC Role)
private VoidQrPaymentResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.amount = builder.amount;
this.pan = builder.pan;
this.expDate = builder.expDate;
this.stan = builder.stan;
this.time = builder.time;
this.date = builder.date;
this.approvalCode = builder.approvalCode;
this.rrn = builder.rrn;
this.posEntryMode = builder.posEntryMode;
this.tid = builder.tid;
this.mid = builder.mid;
this.currencyCode = builder.currencyCode;
}
public boolean isApproved() {
return "00".equals(responseCode);
}
public String getResponseCode() { return responseCode; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_VOID_QR_PAYMENT;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeTag(baos, EcrProtocol.TAG_RESPONSE_CODE, responseCode);
writeTag(baos, EcrProtocol.TAG_AMOUNT, amount);
writeTag(baos, EcrProtocol.TAG_PAN, pan);
writeTag(baos, EcrProtocol.TAG_EXP_DATE, expDate);
writeTag(baos, EcrProtocol.TAG_STAN, stan);
writeTag(baos, EcrProtocol.TAG_TIME, time);
writeTag(baos, EcrProtocol.TAG_DATE, date);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
writeTag(baos, EcrProtocol.TAG_RRN, rrn);
writeTag(baos, EcrProtocol.TAG_POS_ENTRY, posEntryMode);
writeTag(baos, EcrProtocol.TAG_TID, tid);
writeTag(baos, EcrProtocol.TAG_MID, mid);
writeTag(baos, EcrProtocol.TAG_CURRENCY_CODE, currencyCode);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
private void writeTag(ByteArrayOutputStream baos, byte tag, String value) throws IOException {
if (value != null) {
byte[] bytes = value.getBytes(StandardCharsets.US_ASCII);
baos.write(tag);
baos.write((byte) bytes.length);
baos.write(bytes);
}
}
public static class Builder {
private String responseCode;
private String amount;
private String pan;
private String expDate;
private String stan;
private String time;
private String date;
private String approvalCode;
private String rrn;
private String posEntryMode;
private String tid;
private String mid;
private String currencyCode;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setAmount(String amount) { this.amount = amount; return this; }
public Builder setPan(String pan) { this.pan = pan; return this; }
public Builder setExpDate(String expDate) { this.expDate = expDate; return this; }
public Builder setStan(String stan) { this.stan = stan; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public Builder setRrn(String rrn) { this.rrn = rrn; return this; }
public Builder setPosEntryMode(String posEntryMode) { this.posEntryMode = posEntryMode; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; return this; }
public VoidQrPaymentResponse build() {
return new VoidQrPaymentResponse(this);
}
}
}

View File

@ -0,0 +1,46 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class VoidRequest implements EcrCommand {
private String invoiceNumber;
public VoidRequest(String invoiceNumber) {
this.invoiceNumber = invoiceNumber;
}
public VoidRequest(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_VOID) {
throw new IllegalArgumentException("Invalid command");
}
this.invoiceNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
}
public String getInvoiceNumber() { return invoiceNumber; }
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_VOID;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
if (invoiceNumber != null) {
byte[] bytes = invoiceNumber.getBytes(StandardCharsets.US_ASCII);
baos.write(EcrProtocol.TAG_INVOICE_NUMBER);
baos.write((byte) bytes.length);
baos.write(bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,93 @@
package com.kizzy.cmhl.models;
import com.kizzy.cmhl.EcrProtocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class VoidResponse extends BaseEcrResponse {
private String amount;
private String voucherNumber;
private String approvalCode;
private String referenceNum;
private String entry;
public VoidResponse(EcrPacket packet) {
if (packet.getCommandCode() != EcrProtocol.CMD_VOID) {}
parseCommonTags(packet);
this.amount = packet.getValueAsString(EcrProtocol.TAG_AMOUNT);
this.voucherNumber = packet.getValueAsString(EcrProtocol.TAG_INVOICE_NUMBER);
this.approvalCode = packet.getValueAsString(EcrProtocol.TAG_APPROVAL_CODE);
}
private VoidResponse(Builder builder) {
this.responseCode = builder.responseCode;
this.time = builder.time;
this.date = builder.date;
this.tid = builder.tid;
this.mid = builder.mid;
this.amount = builder.amount;
this.voucherNumber = builder.voucherNum;
this.approvalCode = builder.approvalCode;
this.referenceNum = builder.referenceNum;
this.entry = builder.entry;
}
public String getAmount() { return amount; }
public String getVoucherNumber() { return voucherNumber; }
public String getApprovalCode() { return approvalCode; }
public String getReferenceNum() { return referenceNum;}
public String getEntry() {return entry;}
@Override
public byte getCommandCode() {
return EcrProtocol.CMD_VOID;
}
@Override
public byte[] toTlvBytes() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeCommonTags(baos);
writeTag(baos, EcrProtocol.TAG_AMOUNT, amount);
writeTag(baos, EcrProtocol.TAG_STAN, voucherNumber);
writeTag(baos, EcrProtocol.TAG_APPROVAL_CODE, approvalCode);
writeTag(baos, EcrProtocol.TAG_RRN,referenceNum);
writeTag(baos, EcrProtocol.TAG_POS_ENTRY,entry);
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}
public static class Builder {
private String responseCode;
private String time; // len 6
private String date; // len 4
private String tid;
private String mid;
private String amount;
private String voucherNum;
private String approvalCode;
private String referenceNum;
private String entry;
public Builder setResponseCode(String responseCode) { this.responseCode = responseCode; return this; }
public Builder setTime(String time) { this.time = time; return this; }
public Builder setDate(String date) { this.date = date; return this; }
public Builder setTid(String tid) { this.tid = tid; return this; }
public Builder setMid(String mid) { this.mid = mid; return this; }
public Builder setAmount(String amount) { this.amount = amount; return this; }
public Builder setVoucherNumber(String voucherNum) { this.voucherNum = voucherNum; return this; }
public Builder setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; return this; }
public Builder setReferenceNum(String referenceNum) { this.referenceNum = referenceNum; return this; }
public Builder setEntry(String entry) { this.entry = entry; return this; }
public VoidResponse build() { return new VoidResponse(this); }
}
}

View File

@ -0,0 +1,78 @@
package com.kizzy.cmhl.utils;
public class ByteUtils {
/**
* Calculates LRC by XORing all bytes in the data.
* @param data
* @return
*/
public static byte calculateLrc(byte[] data) {
byte lrc = 0;
for (byte b : data) {
lrc ^= b;
}
return lrc;
}
// public static byte[] hexStringToByteArray(String s) {
// int len = s.length();
// byte[] data = new byte[len / 2];
// for (int i = 0; i < len; i += 2) {
// data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
// + Character.digit(s.charAt(i+1), 16));
// }
// return data;
// }
public static byte[] hexStringToByteArray(String s) {
if (s == null) return new byte[0];
// Ensure even length by padding with leading '0' if odd
if (s.length() % 2 != 0) {
s = "0" + s;
}
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
public static byte[] concat(byte[]... arrays) {
int length = 0;
for (byte[] array : arrays) {
length += array.length;
}
byte[] result = new byte[length];
int pos = 0;
for (byte[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
pos += array.length;
}
return result;
}
public static byte[] intToBcd(int val) {
// Only handles up to 9999 for 2 bytes
int thousands = (val / 1000) % 10;
int hundreds = (val / 100) % 10;
int tens = (val / 10) % 10;
int ones = val % 10;
byte b1 = (byte) ((thousands << 4) | hundreds);
byte b2 = (byte) ((tens << 4) | ones);
return new byte[]{b1, b2};
}
}

View File

@ -0,0 +1,172 @@
package com.kizzy.cmhl;
import org.junit.Test;
import static org.junit.Assert.*;
import com.kizzy.cmhl.models.EcrPacket;
import com.kizzy.cmhl.models.PingRequest;
import com.kizzy.cmhl.models.PingResponse;
import com.kizzy.cmhl.models.PreAuthCancellationRequest;
import com.kizzy.cmhl.models.PreAuthCompletionRequest;
import com.kizzy.cmhl.models.PreAuthRequest;
import com.kizzy.cmhl.models.QrPaymentRequest;
import com.kizzy.cmhl.models.SaleRequest;
import com.kizzy.cmhl.models.SaleResponse;
import com.kizzy.cmhl.models.SettlementRequest;
import com.kizzy.cmhl.models.VoidQrPaymentRequest;
import com.kizzy.cmhl.models.VoidRequest;
import com.kizzy.cmhl.utils.ByteUtils;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
@Test
public void testPing() {
PingResponse pingResponse = new PingResponse.Builder()
.setResponseCode("00")
.build();
byte[] packet = EcrManager.buildPacket(pingResponse);
System.out.println("Packets :"+ByteUtils.byteArrayToHexString(packet));
}
@Test
public void testECRPacket() {
byte[] data = {
(byte) 0x02, (byte) 0x00, (byte) 0x27, (byte) 0x01, (byte) 0x07, (byte) 0x37,
(byte) 0x0C, (byte) 0x37, (byte) 0x30, (byte) 0x30, (byte) 0x35, (byte) 0x31,
(byte) 0x37, (byte) 0x31, (byte) 0x34, (byte) 0x39, (byte) 0x31, (byte) 0x34,
(byte) 0x35, (byte) 0x38, (byte) 0x06, (byte) 0x30, (byte) 0x30, (byte) 0x34,
(byte) 0x35, (byte) 0x37, (byte) 0x30, (byte) 0x15, (byte) 0x01, (byte) 0x02,
(byte) 0x03, (byte) 0x31
};
byte[] data2 = {
(byte) 0x02, (byte) 0x00, (byte) 0x21, (byte) 0x01, (byte) 0x0B, (byte) 0x04,
(byte) 0x0C, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
(byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
(byte) 0x30, (byte) 0x49, (byte) 0x03, (byte) 0x31, (byte) 0x30, (byte) 0x34,
(byte) 0x03, (byte) 0x55
};
byte[] data3 = {
(byte) 0x02, (byte) 0x00, (byte) 0x21, (byte) 0x01, (byte) 0x03, (byte) 0x04,
(byte) 0x0C, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
(byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
(byte) 0x30, (byte) 0x49, (byte) 0x03, (byte) 0x31, (byte) 0x30, (byte) 0x34,
(byte) 0x03, (byte) 0x55
};
byte[] ping = {
(byte) 0x02, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x02
};
byte[] qrPayment = ByteUtils.hexStringToByteArray("020021010B040C3030303030303030313335304903313034035B");
try {
EcrPacket ecrPacket = EcrManager.parsePacket(qrPayment);
EcrRequestDispatcher.dispatch(ecrPacket, new EcrRequestListener() {
@Override
public void onPingRequest(PingRequest request) {
PingResponse pingResponse = new PingResponse.Builder()
.setResponseCode("00")
.build();
byte[] responsePacket = EcrManager.buildPacket(pingResponse);
System.out.println("Response: "+ ByteUtils.byteArrayToHexString(responsePacket));
}
@Override
public void onSaleRequest(SaleRequest request) {
System.out.println("command :"+ecrPacket.getCommandCode());
String amount = request.getAmount();
System.out.println("amount:"+amount);
SaleResponse response = new SaleResponse.Builder()
.setResponseCode("00")
.setAmount(amount)
.setPan("411122*****122")
.setApprovalCode("123456")
.setInvoiceNumber("000002")
.build();
byte[] responsePacket = EcrManager.buildPacket(response);
System.out.println("Response: "+ ByteUtils.byteArrayToHexString(responsePacket));
}
@Override
public void onVoidRequest(VoidRequest request) {
}
@Override
public void onSettlementRequest(SettlementRequest request) {
}
@Override
public void onPreAuthRequest(PreAuthRequest request) {
}
@Override
public void onPreAuthCompletionRequest(PreAuthCompletionRequest request) {
}
@Override
public void onPreAuthCancellationRequest(PreAuthCancellationRequest request) {
}
@Override
public void onQrPaymentRequest(QrPaymentRequest request) {
System.out.println("command :"+ecrPacket.getCommandCode());
String amount = request.getAmount();
String currency = request.getCurrencyCode();
System.out.println("amount:"+amount);
System.out.println("currency:"+currency);
}
@Override
public void onVoidQrPaymentRequest(VoidQrPaymentRequest request) {
}
@Override
public void onUnknownRequest(EcrPacket packet) {
}
});
// System.out.println("command :"+ecrPacket.getCommandCode());
//
// QrPaymentRequest request = new QrPaymentRequest(ecrPacket);
// String amount = request.getAmount();
// String currency = request.getCurrencyCode();
//
// System.out.println("amount:"+amount);
// System.out.println("currency:"+currency);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,28 +0,0 @@
/build
# Built files
*.apk
*.aab
*.dex
*.class
# Gradle
.gradle/
build/
*/build/
# Android Studio
*.iml
.idea/
local.properties
# Keystore
*.jks
*.keystore
# OS files
.DS_Store
Thumbs.db
# Logs
*.log

View File

@ -1,3 +0,0 @@
configurations.maybeCreate("default")
artifacts.add("default", file('ecr-service-lib-1.1.11.aar'))
//artifacts.add("default", file('Link_POS_Library_V1.9.aar'))

View File

@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -31,12 +31,9 @@ android {
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar","*.aar"]) implementation fileTree(dir: "libs", include: ["*.jar","*.aar"])
// implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.3.1'
implementation project(path: ':nexsdk-lib') implementation project(path: ':nexsdk-lib')
// implementation files('libs/ecr-service-lib-1.1.11.aar')
implementation project(path: ':ecr-service-lib')
implementation project(path: ':paylibs') implementation project(path: ':paylibs')
implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.code.gson:gson:2.9.0'

View File

@ -1,25 +1,29 @@
package com.utsmyanmar.ecr package com.utsmyanmar.ecr
import android.content.Context import android.content.Context
import android.os.Bundle
import android.widget.Toast
import com.nexgo.oaf.apiv3.APIProxy import com.nexgo.oaf.apiv3.APIProxy
import com.nexgo.oaf.apiv3.SdkResult import com.nexgo.oaf.apiv3.SdkResult
import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity
import com.nexgo.oaf.apiv3.device.serialport.SerialPortDriver import com.nexgo.oaf.apiv3.device.serialport.SerialPortDriver
import com.pos.connection.bridge.ECRConnection
import com.pos.connection.bridge.ECRListener
import com.pos.connection.bridge.ECRRequestCallback
import com.pos.connection.bridge.ECRService
import com.pos.hardware.connection.library.ECRServiceKernel
import com.utsmyanmar.paylibs.utils.LogUtil import com.utsmyanmar.paylibs.utils.LogUtil
import java.nio.charset.StandardCharsets import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil
import java.util.concurrent.atomic.AtomicBoolean
/**
* NexGo SerialPortDriver wrapper (port 0 = physical USB serial).
* Spec: connect / send / recv / clrBuffer / disconnect.
*/
object ECRHelper { object ECRHelper {
val TAG = ECRHelper::class.java.simpleName val TAG: String = ECRHelper::class.java.simpleName
private const val PORT_NO = 0
private const val RECV_BUFFER_SIZE = 2048
private const val RECV_TIMEOUT_MS = 3000L
private var ecrService: ECRService? = null
private var serialPort: SerialPortDriver? = null private var serialPort: SerialPortDriver? = null
private var receiveThread: Thread? = null
private val receiving = AtomicBoolean(false)
var onBindSuccess: () -> Unit = { } var onBindSuccess: () -> Unit = { }
var onBindFailure: () -> Unit = { } var onBindFailure: () -> Unit = { }
@ -32,12 +36,19 @@ object ECRHelper {
var onECRReceive: (bytes: ByteArray) -> Unit = { } var onECRReceive: (bytes: ByteArray) -> Unit = { }
// ─── Serial Port Init (call this once, e.g. in Application or Activity) ─── val isConnected: Boolean
get() = serialPort != null
fun initSerialPort(context: Context) { fun initSerialPort(context: Context) {
try { try {
if (serialPort != null) {
LogUtil.d(TAG, "Serial port already connected")
onBindSuccess()
return
}
val deviceEngine = APIProxy.getDeviceEngine(context) val deviceEngine = APIProxy.getDeviceEngine(context)
val port = deviceEngine.getSerialPortDriver(0) val port = deviceEngine.getSerialPortDriver(PORT_NO)
val entity = SerialCfgEntity().apply { val entity = SerialCfgEntity().apply {
baudRate = 115200 baudRate = 115200
@ -49,143 +60,110 @@ object ECRHelper {
val result = port.connect(entity) val result = port.connect(entity)
if (result == SdkResult.Success) { if (result == SdkResult.Success) {
serialPort = port serialPort = port
LogUtil.d(TAG, "USB Serial connected") port.clrBuffer()
ECRUtil.connected = true
LogUtil.d(TAG, "SerialPortDriver connected (port=$PORT_NO)")
startReceiveLoop()
onECRConnected()
onBindSuccess()
} else { } else {
LogUtil.e(TAG, "USB Serial connect failed: $result") LogUtil.e(TAG, "SerialPortDriver connect failed: $result")
ECRUtil.connected = false
onBindFailure()
onSendFailure(result, "Serial port connect failed") onSendFailure(result, "Serial port connect failed")
} }
} catch (ex: Throwable) { } catch (ex: Throwable) {
ex.printStackTrace() ex.printStackTrace()
ECRUtil.connected = false
onBindFailure()
onSendFailure(-200, ex.localizedMessage ?: "Serial init error") onSendFailure(-200, ex.localizedMessage ?: "Serial init error")
} }
} }
fun releaseSerialPort() {
try {
serialPort?.disconnect()
} catch (ex: Throwable) {
ex.printStackTrace()
} finally {
serialPort = null
}
}
// ─── Send: uses Serial Port instead of ECRService ─────────────────────────
fun send(bytes: ByteArray) { fun send(bytes: ByteArray) {
try { try {
val port = serialPort val port = serialPort
if (port != null) { if (port == null) {
LogUtil.e(TAG, "Serial port not initialized")
onSendFailure(-100, "Serial port not initialized")
return
}
if (bytes.isEmpty() || bytes.size > RECV_BUFFER_SIZE) {
onSendFailure(SdkResult.Param_In_Invalid, "Invalid send length")
return
}
val result = port.send(bytes, bytes.size) val result = port.send(bytes, bytes.size)
if (result == SdkResult.Success) { if (result == SdkResult.Success) {
LogUtil.d(TAG, "Serial send success") LogUtil.d(TAG, "Serial send success, len=${bytes.size}")
onSendSuccess() onSendSuccess()
} else { } else {
LogUtil.e(TAG, "Serial send failed: $result") LogUtil.e(TAG, "Serial send failed: $result")
onSendFailure(result, "Serial send failed") onSendFailure(result, "Serial send failed")
} }
} else {
LogUtil.e(TAG, "Serial port not initialized")
onSendFailure(-100, "Serial port not initialized")
}
} catch (ex: Throwable) { } catch (ex: Throwable) {
ex.printStackTrace() ex.printStackTrace()
onSendFailure(-200, ex.localizedMessage ?: "") onSendFailure(-200, ex.localizedMessage ?: "")
} }
} }
// ─── Everything below is unchanged ──────────────────────────────────────── fun clrBuffer() {
try {
fun connect(bundle: Bundle) { serialPort?.clrBuffer()
call { } catch (ex: Throwable) {
anyExecute(ecrService) { ex.printStackTrace()
connect(bundle, ecrConnection)
}
} }
} }
fun disconnect() { fun disconnect() {
call { anyExecute(ecrService) { disconnect() } } releaseSerialPort()
} }
fun registerECRListener() { fun releaseSerialPort() {
call { anyExecute(ecrService) { register(ecrListener) } } receiving.set(false)
}
fun unregisterECRListener() {
call { anyExecute(ecrService) { unregister(ecrListener) } }
}
fun extensionMethod(bundle: Bundle) {
call { anyExecute(ecrService) { extensionMethod(bundle) } }
}
private val ecrListener = object : ECRListener.Stub() {
override fun onReceive(byteArray: ByteArray?) {
if (byteArray != null) {
val string = String(byteArray, StandardCharsets.UTF_8)
Logger.e(TAG, "onReceive string: $string")
onECRReceive(byteArray)
}
}
}
private val requestCallback = object : ECRRequestCallback.Stub() {
override fun onSuccess() {
Logger.e(TAG, "onSuccess")
onSendSuccess()
}
override fun onFailure(code: Int, massage: String?) {
Logger.e(TAG, "onFailure code: $code massage: $massage")
onSendFailure(code, massage ?: "failure")
}
}
private val ecrConnection = object : ECRConnection.Stub() {
override fun onConnected() {
Logger.d(TAG, "onConnected")
ECRUtil.connected = true
onECRConnected()
}
override fun onDisconnected(code: Int, massage: String?) {
Logger.d(TAG, "onDisconnected code: $code massage: $massage")
ECRUtil.connected = false
onECRDisconnected(code, massage ?: "failure")
}
}
fun bindECRService() {
ECRServiceKernel.getInstance().bindService(ECRUtil.context, connectionCallback)
}
private val connectionCallback = object : ECRServiceKernel.ConnectionCallback {
override fun onServiceConnected() {
Logger.e(TAG, "onServiceConnected")
ecrService = ECRServiceKernel.getInstance().ecrService
onBindSuccess()
}
override fun onServiceDisconnected() {
Logger.e(TAG, "onServiceDisconnected")
ECRUtil.connected = false
ecrService = null
onBindFailure()
}
}
private fun call(block: () -> Unit) {
try { try {
val bridgeService = ecrService receiveThread?.interrupt()
if (bridgeService != null) { } catch (_: Throwable) {
block()
} else {
onECRDisconnected(-100, "The bind ECRService failure")
} }
receiveThread = null
try {
val result = serialPort?.disconnect() ?: SdkResult.SerialPort_Port_Not_Open
LogUtil.d(TAG, "SerialPortDriver disconnect: $result")
} catch (ex: Throwable) { } catch (ex: Throwable) {
ex.printStackTrace() ex.printStackTrace()
onECRDisconnected(-200, ex.localizedMessage ?: "") } finally {
serialPort = null
ECRUtil.connected = false
onECRDisconnected(0, "disconnected")
} }
} }
private fun startReceiveLoop() {
if (receiving.getAndSet(true)) {
return
}
receiveThread = Thread({
val buffer = ByteArray(RECV_BUFFER_SIZE)
while (receiving.get()) {
val port = serialPort ?: break
try {
val len = port.recv(buffer, buffer.size, RECV_TIMEOUT_MS)
if (len > 0) {
val data = buffer.copyOf(len)
LogUtil.d(TAG, "Serial recv len=$len")
LogUtil.d(TAG," Receive packet : "+ ByteUtil.bytes2HexStr(data));
onECRReceive(data)
}
// timeout / no data: keep listening
} catch (ex: Throwable) {
if (receiving.get()) {
LogUtil.e(TAG, "Serial recv error: ${ex.message}")
}
}
}
receiving.set(false)
}, "ECR-SerialRecv").also { it.start() }
}
} }

View File

@ -33,9 +33,9 @@ include ':mpulib'
//include ':ecr-client-lib' //include ':ecr-client-lib'
//include ':ecr-service' //include ':ecr-service'
//include ':ecr-service-lib'
include ':xpay' include ':xpay'
include ':ecr' include ':ecr'
include ':ecr-service-lib'
include ':link-service-lib' include ':link-service-lib'
include ':qrgen-lib' include ':qrgen-lib'
include ':nexdlkey-lib' include ':nexdlkey-lib'