Compare commits

...

5 Commits

Author SHA1 Message Date
MooN
ff68ee6a1f qr fixes 2025-12-01 15:58:06 +06:30
MooN
a0ffff71c4 qr decimal fix 2025-11-28 09:43:25 +06:30
MooN
2441ba5141 e-receipt for qr fixed 2025-11-27 19:18:49 +06:30
MooN
90c9a48197 Merge branch 'merge-latest' into geo_fench 2025-11-26 10:13:16 +06:30
1ac8c14b09 updated some codes 2025-11-25 17:12:42 +07:00
32 changed files with 730 additions and 163 deletions

View File

@ -11,7 +11,7 @@ android {
compileSdk 34
defaultConfig {
applicationId "com.utsmm.kbz" //mpu
applicationId "com.utsmm.kbz.uat" //mpu
minSdk 24
targetSdk 33
versionCode 10

Binary file not shown.

View File

@ -4,15 +4,15 @@
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.utsmm.kbz.mpu",
"applicationId": "com.utsmm.kbz",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 8,
"versionName": "1.08",
"versionCode": 10,
"versionName": "1.10",
"outputFile": "app-release.apk"
}
],

View File

@ -144,10 +144,10 @@ public class SettingsFragment extends DataBindingFragment {
private void updateDemoSettings() {
boolean isDemo = SystemParamsOperation.getInstance().getDemoStatus();
// binding.demoSwitch.setChecked(isDemo);
// binding.demoSummary.setText(isDemo ?
// "Demo mode active - Test transactions without real processing" :
// "Simulator mode active - Real transaction processing");
binding.demoSwitch.setChecked(isDemo);
binding.demoSummary.setText(isDemo ?
"Demo mode active - Test transactions without real processing" :
"Simulator mode active - Real transaction processing");
}
private void updateAlertSoundSettings() {

View File

@ -9,7 +9,9 @@ import androidx.annotation.Nullable;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptCardRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TimeoutCallback;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
@ -25,6 +27,8 @@ import com.utsmm.kbz.util.ecr.CoreUtils;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.text.DecimalFormat;
public class ProcessingFragment extends DataBindingFragment {
private static final String TAG = ProcessingFragment.class.getSimpleName();
@ -248,11 +252,22 @@ public class ProcessingFragment extends DataBindingFragment {
private void observeData(ProcessingTransaction processingTransaction) {
processingTransaction.getTransStatus().observe(getViewLifecycleOwner(), transResultStatus -> {
LogUtil.d(TAG,"Transaction RESULT :"+transResultStatus);
PayDetail payDetail = isEmvTrans()
? emvTransactionProcessViewModel.payDetailResult.getValue()
: transProcessViewModel.payDetailResult.getValue();
LogUtil.d(TAG, "PayDetail from VM: " + payDetail);
switch (transResultStatus) {
case FAIL:
sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, false, "FAILED"));
case SUCCESS:
sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, true, "SUCCESS"));
case OFFLINE_SUCCESS:
if(SystemParamsOperation.getInstance().getDemoStatus()) {
delayFunctionCall(()->{
@ -365,4 +380,108 @@ public class ProcessingFragment extends DataBindingFragment {
});
}
private String mapDE3ToShortCode(String de3) {
if (de3 == null) return "UNK";
switch (de3) {
case "000000": return "S"; // Sale
case "020000": return "V"; // Void
case "200000": return "R"; // Refund
case "030000": return "P"; // Preauth
case "310000": return "PC"; // Preauth Complete
}
return "UNK"; // Unknown
}
public static String mapTransactionType(int type) {
switch (type) {
case 1: // SALE
return "S";
case 2: // VOID SALE
return "V";
case 4: // REFUND
return "R";
case 5: // PRE-AUTH
return "P";
case 6: // PRE-AUTH VOID
return "PV";
case 7: // PRE-AUTH COMPLETE
return "PC";
case 8: // PRE-AUTH COMPLETE VOID
return "PCV";
case 9: // CASH OUT
return "CAV";
case 18: // TIP ADJUST
return "TA";
case 20: // QR PAYMENT
return "QR";
case 34: // QR REFUND
return "QRV";
default:
return "UNK";
}
}
private String mapCurrency(String currency) {
if (currency == null) return "MMK";
switch (currency) {
case "104": return "MMK";
case "840": return "USD";
case "764": return "THB";
case "702": return "SGD";
case "978": return "EUR";
default: return currency; // Already alphabetic? return as-is
}
}
private EReceiptCardRequest buildEReceiptCardReceipt(PayDetail payDetail, boolean isSuccess, String reason){
//current timestamp
String currentTimestamp = new java.text.SimpleDateFormat(
"MMddHHmmss", java.util.Locale.getDefault()
).format(new java.util.Date());
//DeviceInfo
String serial = TMSUtil.getInstance().getSerialNumber();
String appId = requireActivity().getPackageName();
//TerminalInfo
String terminalId = SystemParamsOperation.getInstance().getTerminalId();
String merchantId = SystemParamsOperation.getInstance().getMerchantId();
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
EReceiptCardRequest request = new EReceiptCardRequest();
request.setDE2(payDetail.getCardNo());
request.setDE3(mapDE3ToShortCode(payDetail.getProcessCode()));
request.setDE4(amount);
request.setDE7(currentTimestamp);
request.setDE11(payDetail.getVoucherNo());
request.setDE22(mapTransactionType(payDetail.getTransactionType()));
request.setDE37(payDetail.getReferNo());
request.setDE38(payDetail.getAuthNo());
request.setDE39(isSuccess ? "A" : "E");
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setDE49(mapCurrency(payDetail.getCurrencyCode()));
request.setSerial(serial);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setAppId(appId);
request.setDescription(reason);
return request;
}
}

View File

@ -8,6 +8,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.MalformedJsonException;
import com.utsmyanmar.baselib.emv.EmvParamOperation;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
@ -242,7 +243,7 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
try {
SiriusError siriusError = new Gson().fromJson(error.response().errorBody().string(), SiriusError.class);
LogUtil.d(TAG,siriusError.getMessage());
} catch (IOException ex) {
} catch (IOException | JsonSyntaxException ex) {
ex.printStackTrace();
}

View File

@ -425,6 +425,7 @@ public class SharedViewModel extends ViewModel {
public void pushReceipt(Object body){
Log.d("push receipt", new Gson().toJson(body));
repository.sendReceipt(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View File

@ -25,6 +25,7 @@ import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmm.kbz.util.ecr.CoreUtils;
import java.text.DecimalFormat;
import java.util.Locale;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
@ -135,10 +136,14 @@ public class QRConnectingFragment extends DataBindingFragment implements DataBin
}
private void generateQR() {
long originalAmount = payDetail.getAmount();
// String amount = String.format(Locale.getDefault(), "%.2f", payDetail.getAmount()/100.0);
String amount = String.format(Locale.getDefault(), "%d", (int)(payDetail.getAmount()/100.0));
// String amount = String.format(Locale.getDefault(), "%d", (int)(payDetail.getAmount()/100.0)); //this method have problem for 10055 to 100.00 auto convert (should convert to 100.55)
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
KPayQRRequest.QrRequest kPayQRRequest = KPayViewModel.createQR(amount, merchantId);

View File

@ -11,8 +11,10 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.KPayRefund;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptQRRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
@ -34,6 +36,8 @@ import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.text.DecimalFormat;
public class QRRefundFragment extends DataBindingFragment {
private SharedViewModel sharedViewModel;
@ -196,6 +200,8 @@ public class QRRefundFragment extends DataBindingFragment {
payDetail.setReferNo(referenceNo);
payDetail.setIsCanceled(true);
// sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, true, "Refund success"));
retrievedUpdatePayDetail(referenceNo);
} else {
@ -210,6 +216,8 @@ public class QRRefundFragment extends DataBindingFragment {
}
payDetail.setTradeResultDes(errorMsg);
// sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, true, "RESP CODE [68]\n Refund Failed"));
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
}
@ -328,4 +336,6 @@ public class QRRefundFragment extends DataBindingFragment {
}
}
}
}

View File

@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.DemoQRStatusRequest;
import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest;
@ -314,29 +315,7 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
retrievedUpdatePayDetail(refLabel, payDetail,false);
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
String currentTimeStamp = new java.text.SimpleDateFormat("MMddHHmmss", java.util.Locale.getDefault())
.format(new java.util.Date());
String serial = SystemParamsOperation.getInstance().getCurrentSerialNum();
String packageName = getActivity().getPackageName();
EReceiptQRRequest request = new EReceiptQRRequest();
request.setDE3("QR");
request.setDE4(amount);
request.setDE7(currentTimeStamp);
request.setDE37(payDetail.getReferNo());
request.setDE39("A");
request.setDE49("MMK");
request.setSerial(serial);
request.setAppId(packageName);
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setDE11(payDetail.getVoucherNo());
request.setDescription("qr pay success");
sharedViewModel.pushReceipt(request);
sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, true, "QR Pay Success"));
return;
}
@ -346,27 +325,7 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
if(payDetail.getQrTransStatus() != -1) {
payDetail.setQrTransStatus(2);
}
String currentTimeStamp = new java.text.SimpleDateFormat("MMddHHmmss", java.util.Locale.getDefault())
.format(new java.util.Date());
String serial = SystemParamsOperation.getInstance().getCurrentSerialNum();
String packageName = getActivity().getPackageName();
EReceiptQRRequest request = new EReceiptQRRequest();
request.setDE3("QR");
request.setDE4("0");
request.setDE7(currentTimeStamp);
request.setDE37(payDetail.getReferNo());
request.setDE39("D");
request.setDE49("MMK");
request.setSerial(serial);
request.setAppId(packageName);
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setDE11(payDetail.getVoucherNo());
request.setDescription("qr timeout");
sharedViewModel.pushReceipt(request);
sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, false, "QR Expired!"));
sharedViewModel.payDetail.postValue(payDetail);
safeNavigateToRouteId();
@ -374,26 +333,8 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
} catch (Exception e) {
LogUtil.d(TAG, "On Exception::");
sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, false, "QR Failed! :" + e.getMessage()));
e.printStackTrace();
String currentTimeStamp = new java.text.SimpleDateFormat("MMddHHmmss", java.util.Locale.getDefault())
.format(new java.util.Date());
String serial = SystemParamsOperation.getInstance().getCurrentSerialNum();
String packageName = getActivity().getPackageName();
EReceiptQRRequest request = new EReceiptQRRequest();
request.setDE3("QR");
request.setDE4("0");
request.setDE7(currentTimeStamp);
request.setDE37(payDetail.getReferNo());
request.setDE39("D");
request.setDE49("MMK");
request.setSerial(serial);
request.setAppId(packageName);
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setDE11(payDetail.getVoucherNo());
request.setDescription("qr failed");
sharedViewModel.pushReceipt(request);
if (count == totalCount) {
if(payDetail.getQrTransStatus() != -1) {
@ -486,4 +427,58 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
});
}
private String mapCurrency(String currency) {
if (currency == null) return "MMK";
switch (currency) {
case "104": return "MMK";
case "840": return "USD";
case "764": return "THB";
case "702": return "SGD";
case "978": return "EUR";
default: return currency; // Already alphabetic? return as-is
}
}
private EReceiptQRRequest buildEReceiptQRReceipt(PayDetail payDetail, boolean isSuccess, String reason) {
// Timestamp (MMddHHmmss)
String currentTimestamp = new java.text.SimpleDateFormat(
"MMddHHmmss", java.util.Locale.getDefault()
).format(new java.util.Date());
// Device Info
String serial = TMSUtil.getInstance().getSerialNumber();
String appId = requireActivity().getPackageName();
// Terminal Info
// String terminalId = SystemParamsOperation.getInstance().getTerminalId();
// String merchantId = SystemParamsOperation.getInstance().getMerchantId();
// Amount convert (long cents double 0.00)
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
EReceiptQRRequest request = new EReceiptQRRequest();
request.setDE3("QR"); // QR Process Code
request.setDE4(amount); // Amount
request.setDE7(currentTimestamp); // Timestamp
request.setDE37(payDetail.getReferNo()); // Reference No
request.setDE39(isSuccess ? "A" : "E"); // A=Approved, D=Declined/Timeout
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setDE49(mapCurrency(payDetail.getCurrencyCode())); // Currency
request.setSerial(serial);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setDE11(payDetail.getVoucherNo()); // STAN
request.setAppId(appId);
request.setDescription(reason); // Anything: success / timeout / error message
return request;
}
}

View File

@ -166,7 +166,7 @@ public class ManualEntryFragment extends DataBindingFragment {
public void onConfirm() {
if(manualEntryViewModel.validateData()) {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID) {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_COMPLETE) {
if(manualEntryViewModel.get_cardScheme().getValue() != null && manualEntryViewModel.get_cardScheme().getValue() == CardScheme.MPU) {
processMPUCard();

View File

@ -2,8 +2,20 @@ package com.utsmm.kbz.ui.navigation;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.nexgo.oaf.apiv3.APIProxy;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.DeviceInfo;
import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.printer.AlignEnum;
import com.nexgo.oaf.apiv3.device.printer.GrayLevelEnum;
import com.nexgo.oaf.apiv3.device.printer.LineOptionEntity;
import com.nexgo.oaf.apiv3.device.printer.Printer;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.sirius.SiriusHost;
import com.utsmyanmar.baselib.network.model.sirius.SiriusMerchant;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.sign_on.EchoTestProcess;
@ -13,6 +25,8 @@ import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import java.util.List;
public class NaviMainFragment extends DataBindingFragment {
private OnFragmentInteractionListener mListener;
@ -137,6 +151,7 @@ public class NaviMainFragment extends DataBindingFragment {
});
}
public void onClickLogOff() {
showLoadingDialog("Sending LogOff!");
@ -176,6 +191,148 @@ public class NaviMainFragment extends DataBindingFragment {
mListener.onClickFunction();
}
private GrayLevelEnum getGrayLevel() {
int gray = 2;
GrayLevelEnum grayLevelEnum = GrayLevelEnum.LEVEL_1;
switch (gray) {
case 0:
grayLevelEnum = GrayLevelEnum.LEVEL_0;
break;
case 1:
grayLevelEnum = GrayLevelEnum.LEVEL_1;
break;
case 2:
grayLevelEnum = GrayLevelEnum.LEVEL_2;
break;
case 3:
grayLevelEnum = GrayLevelEnum.LEVEL_3;
break;
case 4:
grayLevelEnum = GrayLevelEnum.LEVEL_4;
break;
default:
grayLevelEnum = GrayLevelEnum.LEVEL_1;
break;
}
return grayLevelEnum;
}
public void onClickPrintConfig() {
DeviceEngine deviceEngine = APIProxy.getDeviceEngine(requireContext());
Printer printer = deviceEngine.getPrinter();
DeviceInfo deviceInfo = deviceEngine.getDeviceInfo();
int FONT_NORMAL = 24;
int FONT_HEADER = 32;
int status = printer.getStatus();
if (status != SdkResult.Success) {
Log.d("Printer", "Printer error: " + status);
return;
}
SystemParamsOperation sp = SystemParamsOperation.getInstance();
String addr1 = sp.getMerchantAddress();
String addr2 = sp.getMerchantAddress2();
String phone = sp.getMerchantPhoneNo();
if (addr2 == null) addr2 = "";
if (phone == null || phone.trim().isEmpty()) phone = "";
printer.setGray(getGrayLevel());
// ================== Header ==================
printer.appendPrnStr("==== Device Configs ====", FONT_HEADER, AlignEnum.CENTER, true);
printer.appendPrnStr(" ", FONT_NORMAL, AlignEnum.CENTER, false);
if(!TextUtils.isEmpty(addr1))
printer.appendPrnStr(addr1, FONT_NORMAL, AlignEnum.CENTER, true);
if(!TextUtils.isEmpty(addr2))
printer.appendPrnStr(addr2, FONT_NORMAL, AlignEnum.CENTER, true);
if(!TextUtils.isEmpty(phone))
printer.appendPrnStr(phone, FONT_NORMAL, AlignEnum.CENTER, true);
printer.appendPrnStr(" ", FONT_NORMAL, AlignEnum.CENTER, false);
// ================== Device Info ==================
printer.appendPrnStr("Device S/N", deviceInfo.getSn(), FONT_NORMAL, true);
printer.appendPrnStr(" ", FONT_NORMAL, AlignEnum.CENTER, false);
// ================== Host Section ==================
printer.appendPrnStr("HOSTS", FONT_HEADER, AlignEnum.LEFT, true);
printer.appendPrnStr(" ", FONT_NORMAL, AlignEnum.CENTER, false);
// ---- Primary Host ----
printer.appendPrnStr("Name :", sp.getHostName(), FONT_NORMAL, false);
printTwoColWrapped(printer, "IP :", sp.getIpAddress(), FONT_NORMAL, false);
printTwoColWrapped(printer, "Sec IP :", sp.getSecIpAddress(), FONT_NORMAL, false);
printer.appendPrnStr("MID :", sp.getMerchantId(), FONT_NORMAL, false);
printer.appendPrnStr("TID :", sp.getTerminalId(), FONT_NORMAL, false);
printer.appendPrnStr("--------------------------------", FONT_NORMAL, AlignEnum.LEFT, false);
// ---- Secondary Host ----
if (!TextUtils.isEmpty(sp.getSecHostName())) {
printer.appendPrnStr("Name :", sp.getSecHostName(), FONT_NORMAL, false);
printTwoColWrapped(printer, "IP :", sp.getSecHostIpAddress(), FONT_NORMAL, false);
printTwoColWrapped(printer, "Sec IP :", sp.getSecHostSecIpAddress(), FONT_NORMAL, false);
printer.appendPrnStr("MID :", sp.getSecHostMerchantId(), FONT_NORMAL, false);
printer.appendPrnStr("TID :", sp.getSecHostTerminalId(), FONT_NORMAL, false);
printer.appendPrnStr("--------------------------------", FONT_NORMAL, AlignEnum.LEFT, false);
}
// ================== Start Printing ==================
printer.startPrint(true, ret -> {
if(ret == SdkResult.Success){
Log.d("Printer", "Print success");
} else {
Log.d("Printer", "Print failed: " + ret);
}
});
}
private void printTwoColWrapped(Printer p, String left, String right, int font, Boolean isBold) {
if (right == null) right = "";
int maxRight = 24; // recommended width for right column
// If right text fits print normally
if (right.length() <= maxRight) {
p.appendPrnStr(left, right, font, isBold);
return;
}
// Otherwise wrap it
int start = 0;
boolean firstLine = true;
while (start < right.length()) {
int end = Math.min(start + maxRight, right.length());
String part = right.substring(start, end);
if (firstLine) {
p.appendPrnStr(left, part, font, isBold);
firstLine = false;
} else {
// subsequent lines: blank left column
p.appendPrnStr(" ", part, font, isBold);
}
start += maxRight;
}
}
public void onClickVersion(){
mListener.onClickVersion();
}
@ -184,7 +341,6 @@ public class NaviMainFragment extends DataBindingFragment {
mListener.onClickExit();
}
}
public interface OnFragmentInteractionListener {
@ -195,5 +351,6 @@ public class NaviMainFragment extends DataBindingFragment {
void onClickVersion();
void onClickExit();
}
}

View File

@ -14,14 +14,18 @@ import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmm.kbz.ui.kpay.QRRefundFragment;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmm.kbz.util.ecr.CoreUtils;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.KPayRefund;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptQRRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.system.SystemDateTime;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.POSUtil;
import java.text.DecimalFormat;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
@ -140,6 +144,8 @@ public class QRRefundProcessFragment extends DataBindingFragment {
payDetail.setReferNo(referenceNo);
payDetail.setIsCanceled(true);
sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, true, "Refund success"));
retrievedUpdatePayDetail(referenceNo);
} else {
@ -153,7 +159,8 @@ public class QRRefundProcessFragment extends DataBindingFragment {
errorMsg = response.getResponse().getMsg();
}
payDetail.setTradeResultDes(errorMsg);
sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, false, "Refund Failed! "+errorMsg));
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
}
@ -212,4 +219,58 @@ public class QRRefundProcessFragment extends DataBindingFragment {
}
}
}
private String mapCurrency(String currency) {
if (currency == null) return "MMK";
switch (currency) {
case "104": return "MMK";
case "840": return "USD";
case "764": return "THB";
case "702": return "SGD";
case "978": return "EUR";
default: return currency; // Already alphabetic? return as-is
}
}
private EReceiptQRRequest buildEReceiptQRReceipt(PayDetail payDetail, boolean isSuccess, String reason) {
// Timestamp (MMddHHmmss)
String currentTimestamp = new java.text.SimpleDateFormat(
"MMddHHmmss", java.util.Locale.getDefault()
).format(new java.util.Date());
// Device Info
String serial = TMSUtil.getInstance().getSerialNumber();
String appId = requireActivity().getPackageName();
// Terminal Info
// String terminalId = SystemParamsOperation.getInstance().getTerminalId();
// String merchantId = SystemParamsOperation.getInstance().getMerchantId();
String terminalId = TransactionUtil.getInstance().getQRTerminalId();
String merchantId = TransactionUtil.getInstance().getQRMerchantId();
// Amount convert (long cents double 0.00)
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
EReceiptQRRequest request = new EReceiptQRRequest();
request.setDE3("QRV"); // QR Process Code(fix later with get from config)
request.setDE4(amount); // Amount
request.setDE7(currentTimestamp); // Timestamp
request.setDE37(payDetail.getReferNo()); // Reference No
request.setDE39(isSuccess ? "A" : "E"); // A=Approved, E=Error/Declined/Timeout
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setDE49(mapCurrency(payDetail.getCurrencyCode())); // Currency(fix later with get from config)
request.setSerial(serial);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setDE11(payDetail.getVoucherNo()); // STAN
request.setAppId(appId);
request.setDescription(reason); // Anything: success / timeout / error message
return request;
}
}

View File

@ -696,7 +696,7 @@ public class TMSSetupsImpl implements TMSSetups{
int value = Integer.parseInt(data);
return value == 1;
} catch (Exception e) {
e.printStackTrace();
// e.printStackTrace();
return Boolean.parseBoolean(data);
}
}

View File

@ -233,11 +233,11 @@ public class TMSUtil {
String secHostIp = SystemParamsOperation.getInstance().getSecIpAddress();
String keyIndex = SystemParamsOperation.getInstance().getTMKIndex();
if(tid.length() == 8 && mid.length() == 15 && !hostIp.isEmpty() && !keyIndex.isEmpty()) {
if(tid.length() == 8 && (mid.length() == 11 || mid.length() == 15) && !hostIp.isEmpty() && !keyIndex.isEmpty()) {
tmsValidity = new TMSValidity(ValidityStatus.SUCCESS,"Success");
} else if(tid.length() != 8) {
tmsValidity = new TMSValidity(ValidityStatus.FAILURE,"Tid is invalid!");
} else if(mid.length() != 15) {
} else if(mid.length() != 15 && mid.length() != 11 ) {
tmsValidity = new TMSValidity(ValidityStatus.FAILURE,"Mid is invalid!");
} else if(hostIp.isEmpty()) {
tmsValidity = new TMSValidity(ValidityStatus.FAILURE,"Pri-Ip is invalid!");

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -266,8 +266,7 @@
<ImageView
android:layout_width="46dp"
android:layout_height="46dp"
android:src="@drawable/ic_qr_pay"
app:tint="@color/colorPrimary"
android:src="@drawable/ic_mmqr"
android:layout_marginBottom="6dp" />
<TextView

View File

@ -316,82 +316,82 @@
<!-- </androidx.cardview.widget.CardView>-->
<!-- Demo Mode Card -->
<!-- <androidx.cardview.widget.CardView-->
<!-- android:id="@+id/demoCard"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginBottom="12dp"-->
<!-- android:clickable="true"-->
<!-- android:foreground="?android:attr/selectableItemBackground"-->
<!-- android:onClick="@{()->click.onDemoClick()}"-->
<!-- app:cardBackgroundColor="@color/white"-->
<!-- app:cardCornerRadius="16dp"-->
<!-- app:cardElevation="2dp">-->
<androidx.cardview.widget.CardView
android:id="@+id/demoCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{()->click.onDemoClick()}"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<!-- <LinearLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:gravity="center_vertical"-->
<!-- android:orientation="horizontal"-->
<!-- android:padding="20dp">-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="20dp">
<!-- <androidx.cardview.widget.CardView-->
<!-- android:layout_width="48dp"-->
<!-- android:layout_height="48dp"-->
<!-- android:layout_marginEnd="16dp"-->
<!-- app:cardBackgroundColor="@color/colorPrimary"-->
<!-- app:cardCornerRadius="24dp"-->
<!-- app:cardElevation="0dp">-->
<androidx.cardview.widget.CardView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="16dp"
app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="24dp"
app:cardElevation="0dp">
<!-- <ImageView-->
<!-- android:layout_width="24dp"-->
<!-- android:layout_height="24dp"-->
<!-- android:layout_gravity="center"-->
<!-- android:src="@drawable/ic_swap"-->
<!-- app:tint="@color/white" />-->
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:src="@drawable/ic_swap"
app:tint="@color/white" />
<!-- </androidx.cardview.widget.CardView>-->
</androidx.cardview.widget.CardView>
<!-- <LinearLayout-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:orientation="vertical">-->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:fontFamily="@font/rubik_medium"-->
<!-- android:text="Demo Mode"-->
<!-- android:textColor="@color/colorTextTitle"-->
<!-- android:textSize="18sp"-->
<!-- android:textStyle="bold"-->
<!-- tools:fontFamily="sans-serif-medium" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/rubik_medium"
android:text="Demo Mode"
android:textColor="@color/colorTextTitle"
android:textSize="18sp"
android:textStyle="bold"
tools:fontFamily="sans-serif-medium" />
<!-- <TextView-->
<!-- android:id="@+id/demoSummary"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="2dp"-->
<!-- android:fontFamily="@font/rubik_regular"-->
<!-- android:text="Demo mode active - Test transactions"-->
<!-- android:textColor="@color/colorTextContent"-->
<!-- android:textSize="14sp"-->
<!-- tools:fontFamily="sans-serif" />-->
<TextView
android:id="@+id/demoSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:fontFamily="@font/rubik_regular"
android:text="Demo mode active - Test transactions"
android:textColor="@color/colorTextContent"
android:textSize="14sp"
tools:fontFamily="sans-serif" />
<!-- </LinearLayout>-->
</LinearLayout>
<!-- <Switch-->
<!-- android:id="@+id/demoSwitch"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginStart="8dp"-->
<!-- android:clickable="false"-->
<!-- android:focusable="false" />-->
<Switch
android:id="@+id/demoSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:clickable="false"
android:focusable="false" />
<!-- </LinearLayout>-->
</LinearLayout>
<!-- </androidx.cardview.widget.CardView>-->
</androidx.cardview.widget.CardView>
<!-- Sound Alerts Card -->
<!-- <androidx.cardview.widget.CardView-->

View File

@ -377,6 +377,81 @@
android:textStyle="bold"
tools:fontFamily="sans-serif-medium" />
<!-- Print Config Card -->
<androidx.cardview.widget.CardView
android:id="@+id/print_config"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{()->click.onClickPrintConfig()}"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="20dp">
<androidx.cardview.widget.CardView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="16dp"
app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="24dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:src="@drawable/ic_config"
app:tint="@color/white" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/rubik_medium"
android:text="Print Config"
android:textColor="@color/colorTextTitle"
android:textSize="18sp"
android:textStyle="bold"
tools:fontFamily="sans-serif-medium" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:fontFamily="@font/rubik_regular"
android:text="Print device configs"
android:textColor="@color/colorTextContent"
android:textSize="14sp"
tools:fontFamily="sans-serif" />
</LinearLayout>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_right_arrow"
app:tint="@color/colorPrimary" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Function Card -->
<androidx.cardview.widget.CardView
android:id="@+id/tv_function"

View File

@ -1,5 +1,5 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">KBZ</string>
<string name="app_name">KBZ UAT</string>
<string name="app_name_sit">SMILE-SIT</string>
<string name="app_name_uat">SMILE-UAT</string>
<string name="app_name_plus">SMILE-PLUS</string>

View File

@ -14,6 +14,8 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
buildConfigField "String", "ERECEIPT_SECRET", "\"${ERECEIPT_SECRET}\""
// Room compiler options
javaCompileOptions {

View File

@ -285,8 +285,8 @@ public class NetworkModule {
tmsAddress = getTMSUrlFromNative();
}
String baseUrl = tmsAddress.trim() + "/api/v1/";
// String baseUrl = tmsAddress.trim() + "/";
// String baseUrl = tmsAddress.trim() + "/api/v1/";
String baseUrl = tmsAddress.trim() + "/";
final Gson gson =
@ -398,9 +398,11 @@ public class NetworkModule {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
// String baseUrl = "http://receipt-nest.utsmyanmar.com/";
String baseUrl = "https://api-tms-uat.kbzbank.com:8443/receipt/";
return new Retrofit.Builder()
.baseUrl("http://receipt-nest.utsmyanmar.com/") // base URL
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)

View File

@ -0,0 +1,135 @@
package com.utsmyanmar.baselib.network.model.e_receipt;
public class EReceiptCardRequest {
private String DE2;
private String DE22;
private String DE3;
private String DE39;
private String DE41;
private String DE42;
private String serial;
private String DE49;
private String DE37;
private String DE7;
private String DE4;
private String appId;
private String invoiceNumber;
private String DE11;
private String description;
private String DE38;
public EReceiptCardRequest() {}
// GETTERS
public String getDE3() {
return DE3;
}
public String getDE39() {
return DE39;
}
public String getDE41() {
return DE41;
}
public String getDE42() {
return DE42;
}
public String getSerial() {
return serial;
}
public String getDE49() {
return DE49;
}
public String getDE37() {
return DE37;
}
public String getDE7() {
return DE7;
}
public String getDE4() {
return DE4;
}
public String getAppId(){
return appId;
}
public String getInvoiceNumber(){
return invoiceNumber;
}
public String getDE11(){
return DE11;
}
public String getDescription(){
return description;
}
public String getDE2(){ return DE2; }
public String getDE38(){ return DE38; }
public String getDE22(){ return DE22; }
// SETTERS
public void setDE3(String DE3) {
this.DE3 = DE3;
}
public void setDE39(String DE39) {
this.DE39 = DE39;
}
public void setDE41(String DE41) {
this.DE41 = DE41;
}
public void setDE42(String DE42) {
this.DE42 = DE42;
}
public void setSerial(String serial) {
this.serial = serial;
}
public void setDE49(String DE49) {
this.DE49 = DE49;
}
public void setDE37(String DE37) {
this.DE37 = DE37;
}
public void setDE7(String DE7) {
this.DE7 = DE7;
}
public void setDE4(String DE4) {
this.DE4 = DE4;
}
public void setAppId(String appId){
this.appId = appId;
}
public void setInvoiceNumber(String invoiceNumber){
this.invoiceNumber = invoiceNumber;
}
public void setDE11(String DE11){
this.DE11 = DE11;
}
public void setDescription(String description){
this.description = description;
}
public void setDE2(String DE2){ this.DE2 = DE2; }
public void setDE38(String DE38){ this.DE38 = DE38; }
public void setDE22(String DE22){ this.DE22 = DE22; }
}

View File

@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.utsmyanmar.baselib.BuildConfig;
import com.utsmyanmar.baselib.db.dao.EmvDetailDao;
import com.utsmyanmar.baselib.db.dao.PayDetailDao;
import com.utsmyanmar.baselib.db.model.EmvDetail;
@ -106,7 +107,8 @@ public class Repository {
return siriusApiService.getParams(siriusRequest);
}
public Observable<EReceiptResponse> sendReceipt(Object body){
String apiSecret = "8f4df38d1001bcc4620b5c736c66a03eef4653eb3ba31105faa2f2ee294c4a46";
// String apiSecret = "y812J21lhha11OS";
String apiSecret = BuildConfig.ERECEIPT_SECRET;
String timestamp = String.valueOf(System.currentTimeMillis());
String bodyString = new Gson().toJson(body);
String dataToHash = bodyString + apiSecret + timestamp;

View File

@ -28,4 +28,5 @@ android.useAndroidX=true
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
# Disable Android Studio's Jetifier to avoid conflicts
android.enableJetifier=false
android.enableJetifier=false
ERECEIPT_SECRET=y812J21lhha11OS

View File

@ -177,10 +177,10 @@ public class TransactionsOperation {
if (responseMap.get("F038") != null) {
transPayDetail.setApprovalCode(Objects.requireNonNull(responseMap.get("F038")).getDataStr());
} else {
if(hostName == HostName.BPC) {
transPayDetail.setIsNeedReversal(true);
return;
}
// if(hostName == HostName.BPC) {
// transPayDetail.setIsNeedReversal(true);
// return;
// }
}

View File

@ -42,8 +42,8 @@ public class SystemParamsSettings implements Serializable {
// private String tmsAddress = "https://tms.smile-mm.com";
// private String tmsAddress = "http://128.199.170.203";
private String tmsAddress = "http://sirius-nest.utsmyanmar.com";
// private String tmsAddress = "https://api-tms-uat.kbzbank.com:8443/sirius";
// private String tmsAddress = "http://sirius-nest.utsmyanmar.com";
private String tmsAddress = "https://api-tms-uat.kbzbank.com:8443/sirius";
private String terminalCapability = "E0E8C8";

View File

@ -99,13 +99,15 @@ public class BitmapConfig {
// commented on Nov 13, 2024
// public static final String BPC_PRE_AUTH_SALE_COMPLETE = "3230058028C09800";
// public static final String BPC_PRE_AUTH_SALE_COMPLETE = "3230058028C09A00";
public static final String BPC_PRE_AUTH_SALE_COMPLETE = "3230058028C09200"; // for tmk
// public static final String BPC_PRE_AUTH_SALE_COMPLETE = "3230058028C09200"; // for tmk
public static final String BPC_PRE_AUTH_SALE_COMPLETE = "7234058028C09200"; // for tmk ,bpc
// public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "7230058028C09000";
// commented on Nov 13,2024
// public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "7230058028C19800"; // added DE 2
public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "7234058008C09000"; // for tmk MPU
// public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "7234058008C09000"; // for tmk MPU
public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "7230058008C09000"; // for KBZ MPU
// public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "3230058028C19800";
// public static final String BPC_PRE_AUTH_SALE_VOID_REVERSAL = "3230058028C19A00"; //DE55
//

View File

@ -316,6 +316,7 @@ public class FieldUtils {
break;
case 14: // ExpireDate YYMM
String expDate = payDetail.getEXPDate();
expDate += "31";
if (expDate != null && expDate.trim().length() > 0) {
field.setDataStr(expDate);
} else {
@ -331,6 +332,7 @@ public class FieldUtils {
}
break;
case 22: //POS Service Entry Mode
String value;
if (AidlConstants.CardType.MAGNETIC.getValue() == cardType) { // 磁条
value = "02";
@ -393,8 +395,6 @@ public class FieldUtils {
field.setDataStr("61"); // BPC
// field.setDataStr("00");// MPU
} if (payDetail.getTransactionType() == PRE_AUTH_VOID.value) { // || payDetail.getTransactionType() == PRE_AUTH_VOID.value BPC need 06
field.setDataStr("06");
} else {
field.setDataStr("00");
}