fixed ecr connectivity and functionalities
This commit is contained in:
parent
048f802c18
commit
80a8c2b148
@ -102,11 +102,6 @@
|
||||
|
||||
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.utsmm.kbz.service.SerialPortService"
|
||||
android:foregroundServiceType="connectedDevice"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -40,10 +40,6 @@ import com.nexgo.oaf.apiv3.APIProxy;
|
||||
import com.nexgo.oaf.apiv3.DeviceEngine;
|
||||
import com.nexgo.oaf.apiv3.device.pinpad.PinPad;
|
||||
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.repo.Repository;
|
||||
import com.utsmyanmar.baselib.ui.AnimationDialog;
|
||||
@ -561,8 +557,8 @@ public class MainActivity extends AppCompatActivity implements
|
||||
// Clean up RxJava disposables
|
||||
compositeDisposable.clear();
|
||||
// Reset screen mode
|
||||
// Disconnect ECR
|
||||
ECRHelper.INSTANCE.disconnect();
|
||||
// Disconnect serial port
|
||||
ECRHelper.INSTANCE.releaseSerialPort();
|
||||
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
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.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
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.VoidQrPaymentRequest;
|
||||
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.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.ecr.ModernECRFactory;
|
||||
//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.TerminalUtil;
|
||||
import com.utsmyanmar.checkxread.sdk.NexGoSDK;
|
||||
import com.utsmyanmar.ecr.ECRHelper;
|
||||
import com.utsmyanmar.ecr.ECRProcess;
|
||||
import com.utsmyanmar.ecr.data.ResultOf;
|
||||
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.printx.PrintXReceipt;
|
||||
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.enums.CurrencyType;
|
||||
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.ModernECRManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class MainFragment extends DataBindingFragment {
|
||||
@ -150,38 +133,43 @@ public class MainFragment extends DataBindingFragment {
|
||||
mainViewModel = getFragmentScopeViewModel(MainViewModel.class);
|
||||
tmsProcessViewModel = getFragmentScopeViewModel(TMSProcessViewModel.class);
|
||||
|
||||
// Initialize ModernECRManager
|
||||
// ModernECRFactory factory = new ModernECRFactory(requireContext(), sharedViewModel);
|
||||
// modernECRManager = new ViewModelProvider(this, factory).get(ModernECRManager.class);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// Register to listen for serial data
|
||||
|
||||
// Setup ECR Service
|
||||
if(SystemParamsOperation.getInstance().getECRStatus()){
|
||||
LocalBroadcastManager.getInstance(requireContext())
|
||||
.registerReceiver(serialReceiver,
|
||||
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));
|
||||
}
|
||||
|
||||
LogUtil.d(TAG,"ECR service initialized!");
|
||||
if (SystemParamsOperation.getInstance().getECRStatus()) {
|
||||
ECRHelper.INSTANCE.setOnECRReceive(bytes -> {
|
||||
if (getActivity() == null) {
|
||||
return null;
|
||||
}
|
||||
requireActivity().runOnUiThread(() -> handleSerialData(bytes));
|
||||
return null;
|
||||
});
|
||||
ECRHelper.INSTANCE.initSerialPort(requireContext().getApplicationContext());
|
||||
LogUtil.d(TAG, "ECR SerialPortDriver listening");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
LocalBroadcastManager.getInstance(requireContext())
|
||||
.unregisterReceiver(serialReceiver);
|
||||
ECRHelper.INSTANCE.setOnECRReceive(bytes -> null);
|
||||
}
|
||||
|
||||
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
|
||||
@ -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
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -314,9 +275,6 @@ public class MainFragment extends DataBindingFragment {
|
||||
// enableHomeButton();
|
||||
}
|
||||
|
||||
private SerialPortDriver port;
|
||||
private boolean isRunning = false;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle 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
|
||||
protected DataBindingConfig getDataBindingConfig() {
|
||||
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
@ -606,9 +606,9 @@ public class TMSSetupsImpl implements TMSSetups{
|
||||
} else if (TextUtils.equals(name, "upi_tc_enabled")) {
|
||||
SystemParamsOperation.getInstance().setUpiTCEnabled(parseBoolean(data));
|
||||
} else if (TextUtils.equals(name,"master_terminal_capability")) {
|
||||
if(!data.isEmpty()) {
|
||||
emvParamOperation.updatePayPassTerminalCapability(data);
|
||||
}
|
||||
// if(!data.isEmpty()) {
|
||||
// emvParamOperation.updatePayPassTerminalCapability(data);
|
||||
// }
|
||||
} else if (TextUtils.equals(name,"speedup_contactless_enable")) {
|
||||
SystemParamsOperation.getInstance().setSpeedUpContactless(parseBoolean(data));
|
||||
} else if (TextUtils.equals(name,"manual_entry_pin_enable")) {
|
||||
|
||||
1
cmhl/.gitignore
vendored
Normal file
1
cmhl/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
35
cmhl/build.gradle
Normal file
35
cmhl/build.gradle
Normal 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
0
cmhl/consumer-rules.pro
Normal file
21
cmhl/proguard-rules.pro
vendored
Normal file
21
cmhl/proguard-rules.pro
vendored
Normal 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
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
4
cmhl/src/main/AndroidManifest.xml
Normal file
4
cmhl/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
148
cmhl/src/main/java/com/kizzy/cmhl/EcrManager.java
Normal file
148
cmhl/src/main/java/com/kizzy/cmhl/EcrManager.java
Normal 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;
|
||||
}
|
||||
}
|
||||
40
cmhl/src/main/java/com/kizzy/cmhl/EcrProtocol.java
Normal file
40
cmhl/src/main/java/com/kizzy/cmhl/EcrProtocol.java
Normal 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"; //
|
||||
|
||||
}
|
||||
49
cmhl/src/main/java/com/kizzy/cmhl/EcrRequestDispatcher.java
Normal file
49
cmhl/src/main/java/com/kizzy/cmhl/EcrRequestDispatcher.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
cmhl/src/main/java/com/kizzy/cmhl/EcrRequestListener.java
Normal file
17
cmhl/src/main/java/com/kizzy/cmhl/EcrRequestListener.java
Normal 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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
cmhl/src/main/java/com/kizzy/cmhl/models/EcrCommand.java
Normal file
6
cmhl/src/main/java/com/kizzy/cmhl/models/EcrCommand.java
Normal file
@ -0,0 +1,6 @@
|
||||
package com.kizzy.cmhl.models;
|
||||
|
||||
public interface EcrCommand {
|
||||
byte getCommandCode();
|
||||
byte[] toTlvBytes();
|
||||
}
|
||||
35
cmhl/src/main/java/com/kizzy/cmhl/models/EcrPacket.java
Normal file
35
cmhl/src/main/java/com/kizzy/cmhl/models/EcrPacket.java
Normal 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);
|
||||
}
|
||||
}
|
||||
26
cmhl/src/main/java/com/kizzy/cmhl/models/PingRequest.java
Normal file
26
cmhl/src/main/java/com/kizzy/cmhl/models/PingRequest.java
Normal 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
|
||||
}
|
||||
}
|
||||
42
cmhl/src/main/java/com/kizzy/cmhl/models/PingResponse.java
Normal file
42
cmhl/src/main/java/com/kizzy/cmhl/models/PingResponse.java
Normal 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); }
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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); }
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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); }
|
||||
}
|
||||
}
|
||||
57
cmhl/src/main/java/com/kizzy/cmhl/models/PreAuthRequest.java
Normal file
57
cmhl/src/main/java/com/kizzy/cmhl/models/PreAuthRequest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
124
cmhl/src/main/java/com/kizzy/cmhl/models/PreAuthResponse.java
Normal file
124
cmhl/src/main/java/com/kizzy/cmhl/models/PreAuthResponse.java
Normal 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); }
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
155
cmhl/src/main/java/com/kizzy/cmhl/models/QrPaymentResponse.java
Normal file
155
cmhl/src/main/java/com/kizzy/cmhl/models/QrPaymentResponse.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
cmhl/src/main/java/com/kizzy/cmhl/models/SaleRequest.java
Normal file
57
cmhl/src/main/java/com/kizzy/cmhl/models/SaleRequest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
140
cmhl/src/main/java/com/kizzy/cmhl/models/SaleResponse.java
Normal file
140
cmhl/src/main/java/com/kizzy/cmhl/models/SaleResponse.java
Normal 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); }
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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); }
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
cmhl/src/main/java/com/kizzy/cmhl/models/VoidRequest.java
Normal file
46
cmhl/src/main/java/com/kizzy/cmhl/models/VoidRequest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
93
cmhl/src/main/java/com/kizzy/cmhl/models/VoidResponse.java
Normal file
93
cmhl/src/main/java/com/kizzy/cmhl/models/VoidResponse.java
Normal 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); }
|
||||
}
|
||||
}
|
||||
78
cmhl/src/main/java/com/kizzy/cmhl/utils/ByteUtils.java
Normal file
78
cmhl/src/main/java/com/kizzy/cmhl/utils/ByteUtils.java
Normal 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};
|
||||
}
|
||||
}
|
||||
172
cmhl/src/test/java/com/kizzy/cmhl/ExampleUnitTest.java
Normal file
172
cmhl/src/test/java/com/kizzy/cmhl/ExampleUnitTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
ecr-service-lib/.gitignore
vendored
28
ecr-service-lib/.gitignore
vendored
@ -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
|
||||
@ -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'))
|
||||
Binary file not shown.
3
ecr-service/.idea/.gitignore
vendored
3
ecr-service/.idea/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@ -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
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -31,12 +31,9 @@ android {
|
||||
|
||||
dependencies {
|
||||
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.appcompat:appcompat:1.3.1'
|
||||
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 'com.google.code.gson:gson:2.9.0'
|
||||
|
||||
|
||||
@ -1,25 +1,29 @@
|
||||
package com.utsmyanmar.ecr
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.nexgo.oaf.apiv3.APIProxy
|
||||
import com.nexgo.oaf.apiv3.SdkResult
|
||||
import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity
|
||||
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 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 {
|
||||
|
||||
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 receiveThread: Thread? = null
|
||||
private val receiving = AtomicBoolean(false)
|
||||
|
||||
var onBindSuccess: () -> Unit = { }
|
||||
var onBindFailure: () -> Unit = { }
|
||||
@ -32,12 +36,19 @@ object ECRHelper {
|
||||
|
||||
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) {
|
||||
try {
|
||||
if (serialPort != null) {
|
||||
LogUtil.d(TAG, "Serial port already connected")
|
||||
onBindSuccess()
|
||||
return
|
||||
}
|
||||
|
||||
val deviceEngine = APIProxy.getDeviceEngine(context)
|
||||
val port = deviceEngine.getSerialPortDriver(0)
|
||||
val port = deviceEngine.getSerialPortDriver(PORT_NO)
|
||||
|
||||
val entity = SerialCfgEntity().apply {
|
||||
baudRate = 115200
|
||||
@ -49,44 +60,46 @@ object ECRHelper {
|
||||
val result = port.connect(entity)
|
||||
if (result == SdkResult.Success) {
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
ex.printStackTrace()
|
||||
ECRUtil.connected = false
|
||||
onBindFailure()
|
||||
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) {
|
||||
try {
|
||||
val port = serialPort
|
||||
if (port != null) {
|
||||
val result = port.send(bytes, bytes.size)
|
||||
if (result == SdkResult.Success) {
|
||||
LogUtil.d(TAG, "Serial send success")
|
||||
onSendSuccess()
|
||||
} else {
|
||||
LogUtil.e(TAG, "Serial send failed: $result")
|
||||
onSendFailure(result, "Serial send failed")
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
if (result == SdkResult.Success) {
|
||||
LogUtil.d(TAG, "Serial send success, len=${bytes.size}")
|
||||
onSendSuccess()
|
||||
} else {
|
||||
LogUtil.e(TAG, "Serial send failed: $result")
|
||||
onSendFailure(result, "Serial send failed")
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
ex.printStackTrace()
|
||||
@ -94,98 +107,63 @@ object ECRHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Everything below is unchanged ────────────────────────────────────────
|
||||
|
||||
fun connect(bundle: Bundle) {
|
||||
call {
|
||||
anyExecute(ecrService) {
|
||||
connect(bundle, ecrConnection)
|
||||
}
|
||||
fun clrBuffer() {
|
||||
try {
|
||||
serialPort?.clrBuffer()
|
||||
} catch (ex: Throwable) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
call { anyExecute(ecrService) { disconnect() } }
|
||||
releaseSerialPort()
|
||||
}
|
||||
|
||||
fun registerECRListener() {
|
||||
call { anyExecute(ecrService) { register(ecrListener) } }
|
||||
}
|
||||
|
||||
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) {
|
||||
fun releaseSerialPort() {
|
||||
receiving.set(false)
|
||||
try {
|
||||
val bridgeService = ecrService
|
||||
if (bridgeService != null) {
|
||||
block()
|
||||
} else {
|
||||
onECRDisconnected(-100, "The bind ECRService failure")
|
||||
}
|
||||
receiveThread?.interrupt()
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
receiveThread = null
|
||||
|
||||
try {
|
||||
val result = serialPort?.disconnect() ?: SdkResult.SerialPort_Port_Not_Open
|
||||
LogUtil.d(TAG, "SerialPortDriver disconnect: $result")
|
||||
} catch (ex: Throwable) {
|
||||
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() }
|
||||
}
|
||||
}
|
||||
@ -33,9 +33,9 @@ include ':mpulib'
|
||||
|
||||
//include ':ecr-client-lib'
|
||||
//include ':ecr-service'
|
||||
//include ':ecr-service-lib'
|
||||
include ':xpay'
|
||||
include ':ecr'
|
||||
include ':ecr-service-lib'
|
||||
include ':link-service-lib'
|
||||
include ':qrgen-lib'
|
||||
include ':nexdlkey-lib'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user