Compare commits

..

2 Commits

7 changed files with 475 additions and 220 deletions

View File

@ -34,6 +34,7 @@ import com.utsmyanmar.baselib.ui.AnimationDialog;
import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.ecr.ECRHelper;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
@ -51,6 +52,7 @@ import com.utsmm.kbz.util.tms.TMSUtil;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -414,17 +416,55 @@ public class MainActivity extends AppCompatActivity implements
private void handleAutoSettlementIntent(Intent intent) {
if (intent == null) return;
boolean auto = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
if (!auto) return;
com.utsmyanmar.paylibs.model.PayDetail payDetail = (com.utsmyanmar.paylibs.model.PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL");
if (payDetail != null) {
// Handle regular auto settlement
boolean auto = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
if (auto) {
com.utsmyanmar.paylibs.model.PayDetail payDetail = (com.utsmyanmar.paylibs.model.PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL");
if (payDetail != null) {
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT);
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Navigation error: " + e.getMessage());
}
}
return;
}
// Handle QR auto settlement
boolean autoQR = intent.getBooleanExtra("AUTO_QR_SETTLEMENT", false);
if (autoQR) {
int qrSaleCount = intent.getIntExtra("QR_SALE_COUNT", 0);
long qrSaleAmount = intent.getLongExtra("QR_SALE_AMOUNT", 0L);
int qrRefundCount = intent.getIntExtra("QR_REFUND_COUNT", 0);
long qrRefundAmount = intent.getLongExtra("QR_REFUND_AMOUNT", 0L);
long totalAmount = intent.getLongExtra("QR_TOTAL_AMOUNT", 0L);
List<PayDetail> qrTransList = (List<PayDetail>) intent.getSerializableExtra("QR_TRANS_LIST");
// Create QR settlement PayDetail
com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(false);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
// Note: QR transactions list is not passed to avoid serialization issues
// The settlement summary is sufficient for the receipt
sharedViewModel.payDetails.setValue(qrTransList);
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT);
sharedViewModel.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT);
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Navigation error: " + e.getMessage());
LogUtil.e(TAG, "QR Auto Settlement navigation error: " + e.getMessage());
}
}
}

View File

@ -145,7 +145,7 @@ public class MainFragment extends DataBindingFragment {
delayFunctionCall(()-> {
NexGoSDK.getInstance().cancelCheckCard();
NexGoSDK.getInstance().closeReader();
disableHomeButton();
enableHomeButton();
disableTaskButton();
BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar();
});
@ -194,14 +194,8 @@ public class MainFragment extends DataBindingFragment {
}
private void checkDownload() {
Log.d(TAG, "Calling from Check download: ");
if (!SystemParamsOperation.getInstance().isDownloadedParams() && SystemParamsOperation.getInstance().isActive() && tmsProcessViewModel.getIsCalled().getValue() == null) {
@ -244,7 +238,7 @@ public class MainFragment extends DataBindingFragment {
sharedViewModel.setManualEntryStatus(SystemParamsOperation.getInstance().getManualEntryStatus());
// generateMockQR();
generateMockQR();
}
/*
@ -831,15 +825,15 @@ public class MainFragment extends DataBindingFragment {
}
// private void generateMockQR() {
// String transDate = "23/12/25";
// String transTime = "12:06:30";
// TradeData tradeData = MockData.getInstance().generateMockDataWithTime(TransactionsType.MMQR, 1,transDate,transTime);
// PayDetail payDetail = tradeData.getPayDetail();
// sharedViewModel.insertPayDetail(payDetail);
// LogUtil.d(TAG,"------------------- Inserted Mocked Data ------------------");
// LogUtil.d(TAG,"trans date : "+payDetail.getTransDate() +"- Trans Time: "+payDetail.getTransTime());
// }
private void generateMockQR() {
String transDate = "24/12/25";
String transTime = "12:06:30";
TradeData tradeData = MockData.getInstance().generateMockDataWithTime(TransactionsType.MMQR, 1,transDate,transTime);
PayDetail payDetail = tradeData.getPayDetail();
sharedViewModel.insertPayDetail(payDetail);
LogUtil.d(TAG,"------------------- Inserted Mocked Data ------------------");
LogUtil.d(TAG,"trans date : "+payDetail.getTransDate() +"- Trans Time: "+payDetail.getTransTime());
}
public class ClickEvent {

View File

@ -1,10 +1,15 @@
package com.utsmm.kbz.service;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import java.util.Calendar;
public class AutoAlarmReceiver extends BroadcastReceiver {
@ -12,17 +17,84 @@ public class AutoAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
LogUtil.d(TAG, "AutoAlarmReceiver triggered at: " + new java.util.Date().toString());
boolean isTestAlarm = intent.getBooleanExtra("TEST_ALARM", false);
if (isTestAlarm) {
LogUtil.d(TAG, "This is a test alarm - AutoAlarmReceiver is working correctly!");
scheduleNextDayAlarm(context);
try {
Intent serviceIntent = new Intent(context, AutoSettleService.class);
context.startService(serviceIntent);
LogUtil.d(TAG, "AutoSettleService started successfully");
} catch (Exception e) {
LogUtil.e(TAG, "Failed to start AutoSettleService: " + e.getMessage());
e.printStackTrace();
}
}
// Start the service to send the API request
Intent serviceIntent = new Intent(context, AutoSettleService.class);
context.startService(serviceIntent);
private void scheduleNextDayAlarm(Context context) {
try {
String timeStr = SystemParamsOperation.getInstance().getClearBatchTime();
LogUtil.d(TAG, " Rescheduling next day's alarm for timeStr: " + timeStr);
LogUtil.d(TAG, "AutoSettleService started");
if (timeStr == null || timeStr.trim().isEmpty()) {
LogUtil.d(TAG, "Clear batch time is empty, cannot reschedule");
return;
}
String[] parts = timeStr.trim().split(":");
if (parts.length != 2) {
LogUtil.d(TAG, "Invalid time format: " + timeStr);
return;
}
int hour = Integer.parseInt(parts[0]);
int minute = Integer.parseInt(parts[1]);
// Validate time range
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
LogUtil.d(TAG, "Invalid time values - hour: " + hour + ", minute: " + minute);
return;
}
// Set up next day's alarm
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 1); // Next day
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long nextTriggerTime = calendar.getTimeInMillis();
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager == null) {
LogUtil.e(TAG, "AlarmManager is null, cannot reschedule");
return;
}
Intent intent = new Intent(context, AutoAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
1001,
intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Schedule next day's alarm
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} else {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -11,6 +11,7 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.utsmyanmar.baselib.repo.Repository;
@ -20,7 +21,6 @@ import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX;
@ -35,28 +35,34 @@ public class AutoSettleService extends Service {
public static final String ACTION_DATA_RECEIVED = BuildConfig.APPLICATION_ID + ".ACTION_DATA_RECEIVED";
public static final String EXTRA_DATA = BuildConfig.APPLICATION_ID + ".EXTRA_DATA";
private Handler handler;
private SettlementViewModel settlementViewModel;
// Flags to prevent infinite loops
private boolean regularSettlementCompleted = false;
private boolean qrSettlementCompleted = false;
// Observers to keep reference for cleanup
private Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>> regularSettlementObserver;
private Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>> qrSettlementObserver;
@Inject
Repository repository;
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager)getSystemService( NOTIFICATION_SERVICE ) ;
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext() , default_notification_channel_id ) ;
mBuilder.setContentTitle( "Alarm manager is running!" ) ;
mBuilder.setContentText( "Wiping out the transactions!" ) ;
mBuilder.setTicker( "Notification Listener Service Example" ) ;
mBuilder.setContentTitle( "Auto Settlement Running" ) ;
mBuilder.setContentText( "Processing transactions..." ) ;
mBuilder.setTicker( "Auto Settlement Service" ) ;
mBuilder.setSmallIcon(R.drawable. ic_launcher_foreground ) ;
mBuilder.setAutoCancel( true ) ;
if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) {
int importance = NotificationManager. IMPORTANCE_HIGH ;
NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "NOTIFICATION_CHANNEL_NAME" , importance) ;
NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "AUTO_SETTLEMENT_CHANNEL" , importance) ;
mBuilder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel) ;
@ -74,15 +80,16 @@ public class AutoSettleService extends Service {
@SuppressLint("CheckResult")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Perform the Settlement request here
LogUtil.d(TAG, "<<<<<<<<<<<<<<< Auto Settlement Service Started >>>>>>>>>>>>>>>>.");
try {
sendDataToViewModel("Hello there");
sendDataToViewModel("Auto settlement initiated");
createNotification();
if (SystemParamsOperation.getInstance().getSettlementStatus()) {
performSettlement();
// performSettlement();
regularSettlementCompleted = true;
performQRSettlement();
} else {
LogUtil.d(TAG, "Settlement is disabled in system parameters");
stopSelf();
@ -97,175 +104,315 @@ public class AutoSettleService extends Service {
}
private void performSettlement() {
if (regularSettlementCompleted) {
return;
}
final ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(
com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion.VERSION_1993,
com.utsmyanmar.paylibs.isobuilder.ISOMode.BOTH_HEADER_TPDU,
com.utsmyanmar.paylibs.utils.enums.HostName.BPC
).build();
repository.getSettlementPOS().observeForever(list -> {
int saleCount = 0;
long saleAmount = 0L;
int preCount = 0;
long preAmount = 0L;
int refundCount = 0;
long refundAmount = 0L;
int caCount = 0;
long caAmount = 0L;
regularSettlementObserver = new Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>>() {
@Override
public void onChanged(java.util.List<com.utsmyanmar.paylibs.model.PayDetail> list) {
if (regularSettlementCompleted) {
return;
}
regularSettlementCompleted = true;
if (list != null && !list.isEmpty()) {
for (com.utsmyanmar.paylibs.model.PayDetail pay : list) {
if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SALE && !pay.isCanceled) {
saleCount++;
saleAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.PRE_SALE_COMPLETE && !pay.isCanceled) {
preCount++;
preAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.REFUND) {
refundCount++;
refundAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.CASH_ADVANCE) {
caCount++;
caAmount += pay.getAmount();
// Remove observer immediately to prevent infinite loop
repository.getSettlementPOS().removeObserver(this);
int saleCount = 0;
long saleAmount = 0L;
int preCount = 0;
long preAmount = 0L;
int refundCount = 0;
long refundAmount = 0L;
int caCount = 0;
long caAmount = 0L;
if (list != null && !list.isEmpty()) {
for (com.utsmyanmar.paylibs.model.PayDetail pay : list) {
if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SALE && !pay.isCanceled) {
saleCount++;
saleAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.PRE_SALE_COMPLETE && !pay.isCanceled) {
preCount++;
preAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.REFUND) {
refundCount++;
refundAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.CASH_ADVANCE) {
caCount++;
caAmount += pay.getAmount();
}
}
}
}
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(true);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(true);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
String bitmap = com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig.BPC_SETTLEMENT;
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.name);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SETTLEMENT);
payDetail.setProcessCode(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.processCode);
payDetail.setBatchNo(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum());
String bitmap = com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig.BPC_SETTLEMENT;
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.name);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SETTLEMENT);
payDetail.setProcessCode(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.processCode);
payDetail.setBatchNo(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum());
com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
saleCount,
saleAmount,
preCount,
preAmount,
refundCount,
refundAmount,
caCount,
caAmount
);
payDetail.setSettleDataObj(settleData);
com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
saleCount, saleAmount, preCount, preAmount, refundCount, refundAmount, caCount, caAmount);
payDetail.setSettleDataObj(settleData);
long totalAmount = saleAmount + preAmount + refundAmount + caAmount;
String settlementData;
if (refundAmount != 0L) {
long creditTotal = saleAmount + preAmount + caAmount;
long subTotal = creditTotal - refundAmount;
if (subTotal < 0L) {
settlementData = "D" + String.format(java.util.Locale.getDefault(), "%012d", Math.abs(subTotal));
long totalAmount = saleAmount + preAmount + refundAmount + caAmount;
String settlementData;
if (refundAmount != 0L) {
long creditTotal = saleAmount + preAmount + caAmount;
long subTotal = creditTotal - refundAmount;
if (subTotal < 0L) {
settlementData = "D" + String.format(java.util.Locale.getDefault(), "%012d", Math.abs(subTotal));
} else {
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", subTotal);
}
} else {
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", subTotal);
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", totalAmount);
}
} else {
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", totalAmount);
}
payDetail.setSettleData(settlementData);
payDetail.setAmount(totalAmount);
payDetail.setSettleData(settlementData);
payDetail.setAmount(totalAmount);
tradeData.setPayDetail(payDetail);
tradeData.setField60(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum());
tradeData.setPayDetail(payDetail);
tradeData.setField60(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum());
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, com.utsmyanmar.paylibs.utils.MessageType.SETTLEMENT);
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, new com.utsmyanmar.paylibs.network.ISOCallback() {
@Override
public void onReceive(byte[] bytes, int length) {
java.util.Map<String, com.utsmyanmar.paylibs.model.MsgField> responseMap = isoMsgX.parseISOPackets(bytes, length);
if (responseMap != null) {
String resultStr = "";
try {
resultStr = responseMap.get("F039").getDataStr();
} catch (NullPointerException e) {
e.printStackTrace();
payDetail.setIsNeedReversal(true);
return;
}
payDetail.setTradeAnswerCode(resultStr);
if (TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_ACCEPT) || TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_APPROVED)) {
payDetail.setIsNeedReversal(false);
} else if (TextUtils.equals(resultStr, "95") || TextUtils.equals(resultStr, "095")) {
payDetail.setIsNeedReversal(true);
}
}
}
@Override
public void onError(String msg) {
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().switchIp();
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, this);
}
@Override
public void onComplete() {
LogUtil.d(TAG, "Auto settlement transaction completed successfully");
if (list != null && !list.isEmpty()) {
for (com.utsmyanmar.paylibs.model.PayDetail p : list) {
repository.deletePayDetail(p);
}
}
repository.insertPayDetail(payDetail);
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, com.utsmyanmar.paylibs.utils.MessageType.SETTLEMENT);
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, new com.utsmyanmar.paylibs.network.ISOCallback() {
@Override
public void onReceive(byte[] bytes, int length) {
java.util.Map<String, com.utsmyanmar.paylibs.model.MsgField> responseMap = isoMsgX.parseISOPackets(bytes, length);
if (responseMap != null) {
String resultStr = "";
try {
resultStr = responseMap.get("F039").getDataStr();
} catch (NullPointerException e) {
com.utsmyanmar.paylibs.model.PayDetail cleanPayDetail = new com.utsmyanmar.paylibs.model.PayDetail();
cleanPayDetail.setTransType(payDetail.getTransType());
cleanPayDetail.setTransactionType(payDetail.getTransactionType());
cleanPayDetail.setProcessCode(payDetail.getProcessCode());
cleanPayDetail.setBatchNo(payDetail.getBatchNo());
cleanPayDetail.setSettleData(payDetail.getSettleData());
cleanPayDetail.setAmount(payDetail.getAmount());
cleanPayDetail.setTradeAnswerCode(payDetail.getTradeAnswerCode());
cleanPayDetail.setIsNeedReversal(payDetail.getIsNeedReversal());
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
uiIntent.putExtra("AUTO_SETTLEMENT", true);
uiIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
uiIntent.putExtra("EXTRA_PAY_DETAIL", cleanPayDetail);
uiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(uiIntent);
LogUtil.d(TAG, "MainActivity started successfully with auto settlement result");
} catch (Exception e) {
LogUtil.e(TAG, "Error starting MainActivity: " + e.getMessage());
e.printStackTrace();
payDetail.setIsNeedReversal(true);
return;
}
payDetail.setTradeAnswerCode(resultStr);
if (TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_ACCEPT) || TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_APPROVED)) {
payDetail.setIsNeedReversal(false);
} else if (TextUtils.equals(resultStr, "95") || TextUtils.equals(resultStr, "095")) {
payDetail.setIsNeedReversal(true);
try {
Intent fallbackIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
fallbackIntent.putExtra("AUTO_SETTLEMENT", true);
fallbackIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
fallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(fallbackIntent);
LogUtil.d(TAG, "MainActivity started with fallback approach");
} catch (Exception fallbackException) {
LogUtil.e(TAG, "Fallback also failed: " + fallbackException.getMessage());
}
}
checkIfBothSettlementsComplete();
}
});
}
};
repository.getSettlementPOS().observeForever(regularSettlementObserver);
}
private void performQRSettlement() {
if (qrSettlementCompleted) {
return;
}
qrSettlementObserver = new Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>>() {
@Override
public void onChanged(java.util.List<com.utsmyanmar.paylibs.model.PayDetail> payDetailList) {
if (qrSettlementCompleted) {
return;
}
qrSettlementCompleted = true;
@Override
public void onError(String msg) {
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().switchIp();
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, this);
}
// Remove observer immediately to prevent infinite loop
repository.getTransactionHistory().removeObserver(this);
@Override
public void onComplete() {
LogUtil.d(TAG, "Auto settlement transaction completed successfully");
if (payDetailList != null) {
int qrSaleCount = 0;
long qrSaleAmount = 0;
int qrRefundCount = 0;
long qrRefundAmount = 0;
if (list != null && !list.isEmpty()) {
for (com.utsmyanmar.paylibs.model.PayDetail p : list) {
repository.deletePayDetail(p);
}
}
repository.insertPayDetail(payDetail);
java.util.ArrayList<com.utsmyanmar.paylibs.model.PayDetail> qrTransactionsList = new java.util.ArrayList<>();
java.util.ArrayList<com.utsmyanmar.paylibs.model.PayDetail> qrTransListAll = new java.util.ArrayList<>();
try {
// Remove the non-serializable SettleData object before passing through Intent
// Create a clean copy of PayDetail without the problematic SettleData object
com.utsmyanmar.paylibs.model.PayDetail cleanPayDetail = new com.utsmyanmar.paylibs.model.PayDetail();
cleanPayDetail.setTransType(payDetail.getTransType());
cleanPayDetail.setTransactionType(payDetail.getTransactionType());
cleanPayDetail.setProcessCode(payDetail.getProcessCode());
cleanPayDetail.setBatchNo(payDetail.getBatchNo());
cleanPayDetail.setSettleData(payDetail.getSettleData());
cleanPayDetail.setAmount(payDetail.getAmount());
cleanPayDetail.setTradeAnswerCode(payDetail.getTradeAnswerCode());
cleanPayDetail.setIsNeedReversal(payDetail.getIsNeedReversal());
// Note: Not setting SettleDataObj as it's not serializable
for (com.utsmyanmar.paylibs.model.PayDetail payDetail : payDetailList) {
// Filter for successful QR transactions only
if ((payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value
|| payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value)
&& payDetail.getQrTransStatus() == 1) {
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
uiIntent.putExtra("AUTO_SETTLEMENT", true);
uiIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
uiIntent.putExtra("EXTRA_PAY_DETAIL", cleanPayDetail);
uiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(uiIntent);
qrTransactionsList.add(payDetail);
LogUtil.d(TAG, "MainActivity started successfully with auto settlement result");
} catch (Exception e) {
LogUtil.e(TAG, "Error starting MainActivity: " + e.getMessage());
e.printStackTrace();
// Fallback: start MainActivity without the PayDetail extra
try {
Intent fallbackIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
fallbackIntent.putExtra("AUTO_SETTLEMENT", true);
fallbackIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
fallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(fallbackIntent);
LogUtil.d(TAG, "MainActivity started with fallback approach");
} catch (Exception fallbackException) {
LogUtil.e(TAG, "Fallback also failed: " + fallbackException.getMessage());
if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value) {
qrSaleCount++;
qrSaleAmount += payDetail.getAmount();
} else if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value) {
qrRefundCount++;
qrRefundAmount += payDetail.getAmount();
}
} else if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value
|| payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value) {
// Include all QR transactions for deletion (successful and failed)
qrTransListAll.add(payDetail);
}
}
} catch (IllegalStateException e) {
LogUtil.e(TAG, "QR Auto Settlement: Database cursor error - likely due to large data size: " + e);
checkIfBothSettlementsComplete();
return;
}
// Stop the service
stopSelf();
if (!qrTransactionsList.isEmpty()) {
LogUtil.d(TAG, "Processing QR Auto Settlement with " + qrTransactionsList.size() + " transactions");
long totalAmount = qrSaleAmount - qrRefundAmount;
// Increment batch number for QR settlement
SystemParamsOperation.getInstance().getIncrementBatchNo();
// Create settlement data for QR transactions
com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(false);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
payDetail.setBatchNo(SystemParamsOperation.getInstance().getCurrentBatchNum());
// Insert QR settlement record
repository.insertPayDetail(payDetail);
// Delete settled QR transactions
for (com.utsmyanmar.paylibs.model.PayDetail pay : qrTransactionsList) {
repository.deletePayDetail(pay);
}
// Delete all other QR transactions (failed ones)
for (com.utsmyanmar.paylibs.model.PayDetail pay : qrTransListAll) {
repository.deletePayDetail(pay);
}
// Push e-receipt data
try {
com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest request =
com.utsmm.kbz.util.EReceiptUtil.getInstance().generateQRSettlement(payDetail);
LogUtil.d(TAG, "QR Settlement e-receipt data prepared");
} catch (Exception e) {
LogUtil.e(TAG, "Error preparing QR settlement e-receipt: " + e.getMessage());
}
try {
// Start MainActivity with QR settlement data for automatic printing
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
uiIntent.putExtra("AUTO_QR_SETTLEMENT", true);
uiIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.value);
uiIntent.putExtra("QR_SALE_COUNT", qrSaleCount);
uiIntent.putExtra("QR_SALE_AMOUNT", qrSaleAmount);
uiIntent.putExtra("QR_REFUND_COUNT", qrRefundCount);
uiIntent.putExtra("QR_REFUND_AMOUNT", qrRefundAmount);
uiIntent.putExtra("QR_TOTAL_AMOUNT", totalAmount);
uiIntent.putExtra("QR_TRANS_LIST", qrTransactionsList);
uiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(uiIntent);
LogUtil.d(TAG, "MainActivity started successfully with QR auto settlement result");
} catch (Exception e) {
LogUtil.e(TAG, "Error starting MainActivity for QR settlement: " + e.getMessage());
e.printStackTrace();
}
LogUtil.d(TAG, "QR Auto Settlement completed successfully");
} else {
LogUtil.d(TAG, "No QR transactions found for auto settlement");
}
}
});
});
checkIfBothSettlementsComplete();
}
};
repository.getTransactionHistory().observeForever(qrSettlementObserver);
}
private void checkIfBothSettlementsComplete() {
// Stop service only when both settlements are processed (or skipped if no data)
if (regularSettlementCompleted && qrSettlementCompleted) {
LogUtil.d(TAG, "Both settlements completed, stopping service");
stopSelf();
}
}
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
}
@Nullable
@ -278,4 +425,19 @@ public class AutoSettleService extends Service {
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
// Clean up observers to prevent memory leaks
if (regularSettlementObserver != null && repository != null) {
repository.getSettlementPOS().removeObserver(regularSettlementObserver);
}
if (qrSettlementObserver != null && repository != null) {
repository.getTransactionHistory().removeObserver(qrSettlementObserver);
}
LogUtil.d(TAG, "AutoSettleService destroyed and cleaned up");
}
}

View File

@ -113,7 +113,8 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
}
if (resultStr.equals(Constant.ANSWER_CODE_ACCEPT) || resultStr.equals(Constant.ANSWER_CODE_APPROVED)) {
/* ISO 8583 RESPONSE */
if(sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.SETTLEMENT.value) {
if(sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.SETTLEMENT.value
&& sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.MMQR_SETTLEMENT.value) {
if(SystemParamsOperation.getInstance().isAlertSound()) {
startSound(getResourceString(R.string.txt_audio_thank_you));

View File

@ -32,6 +32,7 @@ import com.utsmm.kbz.util.tms.TMSUtil;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
@ -219,7 +220,7 @@ public class TMSProcessFragment extends DataBindingFragment {
CurrencyType currencyType = SystemParamsOperation.getInstance().getCurrencyType();
sharedViewModel.set_currencyText(currencyType.name);
// tmsProcessViewModel.loadEmvParameters();
// scheduleAutoSettlement();
scheduleAutoSettlement();
navigateToMain();
}
});
@ -279,10 +280,12 @@ public class TMSProcessFragment extends DataBindingFragment {
LogUtil.d(TAG, "Current time: " + new java.util.Date(currentTime).toString());
LogUtil.d(TAG, "Initial trigger time: " + new java.util.Date(triggerAtMillis).toString());
if (currentTime > triggerAtMillis) {
if (triggerAtMillis <= currentTime) {
calendar.add(Calendar.DAY_OF_YEAR, 1);
triggerAtMillis = calendar.getTimeInMillis();
LogUtil.d(TAG, "Trigger time is in the past, scheduling for next day: " + new java.util.Date(triggerAtMillis).toString());
LogUtil.d(TAG, "Updated trigger time for NEXT DAY: " + new java.util.Date(triggerAtMillis).toString());
} else {
LogUtil.d(TAG, "Scheduling alarm for TODAY: " + new java.util.Date(triggerAtMillis).toString());
}
Intent intent = new Intent(requireContext(), AutoAlarmReceiver.class);
@ -294,50 +297,33 @@ public class TMSProcessFragment extends DataBindingFragment {
);
try {
// For Android 12+ (API 31+), we need to handle exact alarms differently
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
// Check if the app can schedule exact alarms
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
LogUtil.d(TAG, "Auto settlement alarm scheduled successfully for: " + new java.util.Date(triggerAtMillis).toString());
alarmManager.cancel(pendingIntent);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
} else {
LogUtil.d(TAG, "App doesn't have permission to schedule exact alarms. Using inexact alarm.");
// Use inexact alarm as fallback
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
LogUtil.d(TAG, "Auto settlement inexact alarm scheduled for: " + new java.util.Date(triggerAtMillis).toString());
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
} else {
// For older Android versions
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
LogUtil.d(TAG, "Auto settlement alarm scheduled successfully for: " + new java.util.Date(triggerAtMillis).toString());
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
// Also try to schedule a test alarm 30 seconds from now for debugging
long testTriggerTime = currentTime + 30000; // 30 seconds from now
Intent testIntent = new Intent(requireContext(), AutoAlarmReceiver.class);
testIntent.putExtra("TEST_ALARM", true);
PendingIntent testPendingIntent = PendingIntent.getBroadcast(
requireContext(),
1002,
testIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Schedule test alarm
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent);
}
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent);
}
LogUtil.d(TAG, "Test alarm scheduled for: " + new java.util.Date(testTriggerTime).toString());
long hoursUntilTrigger = (triggerAtMillis - currentTime) / 1000 / 60 / 60;
long minutesUntilTrigger = ((triggerAtMillis - currentTime) / 1000 / 60) % 60;
LogUtil.d(TAG, "Auto settlement alarm setup completed. Next occurrence: " + new Date(triggerAtMillis).toString());
LogUtil.d(TAG, "Time until next trigger: " + hoursUntilTrigger + " hours, " + minutesUntilTrigger + " minutes");
} catch (Exception e) {
LogUtil.d(TAG, "Error scheduling alarm: " + e.getMessage());
LogUtil.e(TAG, "Error scheduling main alarm: " + e.getMessage());
e.printStackTrace();
LogUtil.e(TAG, "Alarm scheduling failed - Check if app has SCHEDULE_EXACT_ALARM permission");
LogUtil.e(TAG, "Failed alarm details - Target time: " + new Date(triggerAtMillis).toString());
}
}

View File

@ -201,7 +201,7 @@ public abstract class BaseXPrint {
}
protected void dashBreakEnding() {
printer.appendPrnStr("--------X----------X----------", fontNormal, AlignEnum.LEFT,false);
printer.appendPrnStr("--------X----------X---------", fontNormal, AlignEnum.LEFT,false);
}