Compare commits

...

34 Commits

Author SHA1 Message Date
MooN
8bf4d6da22 decimal fix
decimal fix
2025-11-23 15:55:48 +06:30
MooN
adfa73070f Update fragment_qr_refund_detail.xml
disable partial refund
2025-11-23 15:45:34 +06:30
MooN
a449a366e0 Update QRTransactionFragment.java 2025-11-23 15:24:26 +06:30
MooN
5d450e0a48 qr-e-receipt 2025-11-23 01:35:04 +06:30
MooN
1811fefec1 disable printing on qr_pay_failed 2025-11-20 15:50:29 +06:30
MooN
b484f69cb3 mmqr_interval
mmqr interval connection with TMS
2025-11-20 15:49:55 +06:30
MooN
8cd56777cc color testing 2025-11-19 15:58:02 +06:30
MooN
53f6414f70 Merge branch 'new_dashboard' into merge_with_mpu 2025-11-19 15:48:01 +06:30
MooN
bb60828767 Merge remote-tracking branch 'origin/mpu' into merge_with_mpu 2025-11-19 15:42:49 +06:30
MooN
749abb1455 square btn changed 2025-11-19 12:13:17 +06:30
a8624195b0 mpu certi ready 2025-11-19 02:07:54 +07:00
MooN
65eb17e3b9 new dashboard design 2025-11-18 22:05:34 +06:30
2f9f06ce87 mpu certi almost ready 2025-11-18 06:17:40 +07:00
41b6fb7782 mpu certi almost ready 2025-11-18 06:13:31 +07:00
MooN
7e2a589636 ui fix 2025-11-17 01:41:45 +06:30
MooN
7c266677e7 merge fix 2025-11-16 22:34:22 +06:30
MooN
063dfae87f Merge remote-tracking branch 'origin/master' into merge 2025-11-14 16:37:56 +06:30
MooN
9502ee7a04 13-Nov-25 2025-11-13 14:45:18 +06:30
MooN
5509bf4349 up 2025-11-13 11:27:36 +06:30
476e1d9eaa corrected refund flow 2025-11-13 02:49:21 +07:00
28d4672878 fixed password for refund 2025-11-13 01:01:01 +07:00
MooN
644df8211d qr_refundable_from_list 2025-11-12 21:16:22 +06:30
MooN
2358782f3d safe to route fix 2025-11-11 23:12:22 +06:30
MooN
f142df0144 refudn list 2025-11-11 22:58:55 +06:30
8da4b2399e fixed some formats 2025-11-11 17:26:54 +07:00
MooN
efb0c76f69 qr only history 2025-11-10 16:27:06 +06:30
MooN
d861c278a2 qr_generate 2025-11-09 16:32:15 +06:30
5efcc7ef24 fixed can't call sign on on demo mode 2025-11-07 15:34:00 +07:00
c7ac1e90bf fixed second generated QR 2025-11-07 14:04:59 +07:00
1b4a56c29f sec commit 2025-11-07 01:46:25 +07:00
a885d05b62 sec commit 2025-11-07 01:40:46 +07:00
1cedbc987c sec commit 2025-11-07 01:38:39 +07:00
0a2cc295a1 first commit 2025-11-06 13:38:40 +07:00
b5a82a5fd1 first commit 2025-11-06 01:08:32 +07:00
115 changed files with 4990 additions and 733 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" /> <bytecodeTargetLevel target="21" />
</component> </component>
</project> </project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -4,14 +4,6 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-10-28T19:40:02.148831Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=0123456789ABCDEF" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>

View File

@ -6,7 +6,7 @@
<GradleProjectSettings> <GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" /> <option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="temurin-17" /> <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />

View File

@ -1,9 +1,4 @@
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="temurin-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK" />
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project> </project>

View File

@ -14,8 +14,8 @@ android {
applicationId "com.utsmm.kbz" applicationId "com.utsmm.kbz"
minSdk 24 minSdk 24
targetSdk 33 targetSdk 33
versionCode 5 versionCode 8
versionName "1.05" versionName "1.08"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -90,7 +90,6 @@ dependencies {
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
def nav_version = "2.3.2" def nav_version = "2.3.2"
def lottieVersion = "3.5.0" def lottieVersion = "3.5.0"
def fragment_version = "1.2.0" def fragment_version = "1.2.0"
@ -196,6 +195,10 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'
androidTestImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
// Add Mockito dependency for mocking // Add Mockito dependency for mocking
testImplementation 'org.mockito:mockito-core:3.12.4' testImplementation 'org.mockito:mockito-core:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4' androidTestImplementation 'org.mockito:mockito-android:3.12.4'

BIN
app/release/app-release.apk Normal file

Binary file not shown.

View File

@ -11,8 +11,8 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 5, "versionCode": 7,
"versionName": "1.05", "versionName": "1.07",
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
], ],

View File

@ -10,6 +10,29 @@ import org.junit.runner.RunWith;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import com.sunmi.pay.hardware.aidl.AidlConstants;
import com.utsmm.kbz.util.MockData;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmyanmar.checkxread.model.CardDataX;
import com.utsmyanmar.checkxread.util.CardTypeX;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.isobuilder.ISOMode;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.model.enums.TransCVM;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.MessageType;
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.HostName;
import com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.params.Params;
import java.util.Locale;
/** /**
* Instrumented test, which will execute on an Android device. * Instrumented test, which will execute on an Android device.
* *
@ -23,4 +46,131 @@ public class ExampleInstrumentedTest {
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.utsmm.kbz", appContext.getPackageName()); assertEquals("com.utsmm.kbz", appContext.getPackageName());
} }
@Test
public void testVoidPacket() {
CardDataX cardDataX = MockData.getInstance().generateMPUCard();
TradeData tradeData = TransactionUtil.getInstance().initMPUTransaction(cardDataX, CardTypeX.IC);
String bitmap = BitmapConfig.MPU_NEW_VOID;
MessageType messageType = MessageType.FINANCIAL;
ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1987, ISOMode.ONLY_HEADER, HostName.FINEXUS)
.build();
TradeData transTradeData = setUpRebuildTransactions(tradeData, TransactionsType.VOID, HostName.FINEXUS);
transTradeData.getPayDetail().setAmount(50000);
PayDetail transPayDetail = transTradeData.getPayDetail();
byte[] sendBytes;
try {
sendBytes = isoMsgX.buildISOPackets(transTradeData, bitmap, messageType);
System.out.println("Hex Str : "+ ByteUtil.bytes2HexStr(sendBytes));
} catch (Exception e) {
e.printStackTrace();
}
}
private TradeData setUpRebuildTransactions(TradeData tradeData, TransactionsType transactionsType, HostName hostName) {
LogUtil.d(Constant.TAG, "Starting Online Transaction--" + hostName + "--" + transactionsType);
TradeData newTrade = Params.newTrade(true);
PayDetail newPay = newTrade.getPayDetail();
PayDetail oldPay = tradeData.getPayDetail();
newPay.setCardType(100);
newPay.setPINCipher("");
newPay.setHostName(hostName.name);
newPay.setTransType(transactionsType.name);
newPay.setProcessCode(transactionsType.processCode);
newPay.setCardNo(oldPay.getCardNo());
newPay.setCardHolderName(oldPay.getCardHolderName());
newPay.setEXPDate(oldPay.getEXPDate());
newPay.setTradeDate(oldPay.getTradeDate());
newPay.setTradeTime(oldPay.getTradeTime());
newPay.setAmount(oldPay.getAmount());
newPay.setTransactionType(transactionsType.value);
newPay.setAccountType(oldPay.getAccountType());
newPay.setCardInfo(oldPay.getCardInfo());
newPay.setSettlementEnabled(SystemParamsOperation.getInstance().getSettlementStatus());
// added on Nov, 13 2024
newPay.setICC55(oldPay.getICC55());
if(newPay.getICC55() != null && !newPay.getICC55().isEmpty()) {
newPay.setAppLabel(oldPay.getAppLabel());
newPay.setAppName(oldPay.getAppName());
newPay.setTSI(oldPay.getTSI());
newPay.setAID(oldPay.getAID());
newPay.setArqC(oldPay.getArqC());
newPay.setTVR(oldPay.getTVR());
}
if(transactionsType != TransactionsType.PRE_AUTH_COMPLETE) {
newPay.setTradeDateTime(oldPay.getTradeDateTime());
}
if (transactionsType == TransactionsType.VOID || transactionsType == TransactionsType.REFUND) {
String field60;
if(hostName == HostName.BPC) {
field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount());
} else {
field60 = String.format(Locale.getDefault(), "%012d", oldPay.getAmount());
}
// String field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount());
newPay.setReferNo(oldPay.getReferNo());
newPay.setTransCVM(TransCVM.SIGNATURE);
newTrade.setField60(field60);
} else if (transactionsType == TransactionsType.PRE_AUTH_VOID) {
newPay.setCardType(oldPay.getCardType());
newPay.setCustomOrderNo(oldPay.getVoucherNo());
newPay.setReferNo(oldPay.getReferNo());
newPay.setCardInfo(oldPay.getCardInfo());
newPay.setPINCipher(oldPay.getPINCipher());
newPay.setTempKSN(oldPay.getTempKSN());
newPay.setTransCVM(TransCVM.SIGNATURE);
// for manual entry reversal which need de 35
} else if (transactionsType == TransactionsType.TIP_ADJUSTMENT ) {
newPay.setReferNo(oldPay.getReferNo());
newPay.setApprovalCode(oldPay.getApprovalCode());
} else if (transactionsType == TransactionsType.PRE_AUTH_COMPLETE) {
newPay.setCardType(oldPay.getCardType());
newPay.setCardInfo(oldPay.getCardInfo());
newPay.setPINCipher(oldPay.getPINCipher());
newPay.setReferNo(oldPay.getReferNo());
newPay.setTempKSN(oldPay.getTempKSN());
} else if (transactionsType == TransactionsType.PRE_AUTH_COMPLETE_VOID) {
newPay.setVoucherNo(oldPay.getVoucherNo());
newPay.setCustomOrderNo(oldPay.getVoucherNo());
newPay.setReferNo(oldPay.getReferNo());
newPay.setCardInfo(oldPay.getCardInfo());
/*
* new requirements */
newPay.setCardType(AidlConstants.CardType.IC.getValue());
newPay.setPINCipher("55");
newPay.setTransCVM(TransCVM.SIGNATURE);
}
if(oldPay.getAccountType().equals("MPU")){
// newPay.setIsFreeSign(true);
newPay.setTransCVM(TransCVM.SIGNATURE);
}
newTrade.setPayDetail(newPay);
return newTrade;
}
} }

View File

@ -46,8 +46,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar" android:theme="@style/AppTheme.NoActionBar">
>
<!-- android:manageSpaceActivity="com.utsmyanmar.upos.config.UTSManageSpaceActivity"--> <!-- android:manageSpaceActivity="com.utsmyanmar.upos.config.UTSManageSpaceActivity"-->
<!-- <activity--> <!-- <activity-->
<!-- android:screenOrientation="portrait"--> <!-- android:screenOrientation="portrait"-->
@ -56,10 +55,11 @@
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.journeyapps.barcodescanner.CaptureActivity" <activity android:name="com.journeyapps.barcodescanner.CaptureActivity"

View File

@ -292,7 +292,7 @@ public class MainFragment extends DataBindingFragment {
private void updateButtonStatus() { private void updateButtonStatus() {
mainViewModel.settlementStatus.setValue(SystemParamsOperation.getInstance().getSettlementStatus()); mainViewModel.settlementStatus.setValue(SystemParamsOperation.getInstance().getSettlementStatus());
mainViewModel.kPayStatus.setValue(SystemParamsOperation.getInstance().getWavePayStatus()); mainViewModel.kPayStatus.setValue(SystemParamsOperation.getInstance().getQRPayStatus());
} }
private void setUpCarouselImages() { private void setUpCarouselImages() {
@ -849,6 +849,12 @@ public class MainFragment extends DataBindingFragment {
} }
} }
public void onClickQR(){
// navigate to new QR Pay fragment
routeId = R.id.action_nav_main_to_qrFragment;
safeNavigateToRouteId();
}
public void onClickQRPay() { public void onClickQRPay() {
String mmqrIp = SystemParamsOperation.getInstance().getSecHostIpAddress(); String mmqrIp = SystemParamsOperation.getInstance().getSecHostIpAddress();

View File

@ -94,7 +94,7 @@ public class MainViewModel extends ViewModel {
settlementStatus.setValue(SystemParamsOperation.getInstance().getSettlementStatus()); settlementStatus.setValue(SystemParamsOperation.getInstance().getSettlementStatus());
kPayStatus.setValue(SystemParamsOperation.getInstance().getWavePayStatus()); kPayStatus.setValue(SystemParamsOperation.getInstance().getQRPayStatus());
disabledMsg.setValue(SystemParamsOperation.getInstance().getDisabledMsg()); disabledMsg.setValue(SystemParamsOperation.getInstance().getDisabledMsg());
} }

View File

@ -5,5 +5,6 @@ public enum CardTransactionType {
MPU, MPU,
EMV, EMV,
MAG, MAG,
FALLBACK FALLBACK,
MOCK
} }

View File

@ -0,0 +1,76 @@
package com.utsmm.kbz.ui.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemQrPayButtonBinding;
import com.utsmm.kbz.ui.qr_pay.QRPayItem;
import java.util.List;
public class QRPayAdapter extends RecyclerView.Adapter<QRPayAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(int position);
}
private final List<QRPayItem> items;
private final OnItemClickListener listener;
public QRPayAdapter(List<QRPayItem> item, OnItemClickListener listener) {
this.items = item;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemQrPayButtonBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_qr_pay_button,
parent,
false
);
return new ViewHolder(binding);
}
private final int[] colors = new int[]{
R.color.colorPrimary,
R.color.amber,
R.color.forestGreen
};
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
QRPayItem item = items.get(position);
holder.binding.setText(item.title);
holder.binding.setIcon(item.icon);
holder.binding.btnItem.setOnClickListener(v -> {
if(listener != null){
listener.onItemClick(position);
}
});
}
@Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
final ItemQrPayButtonBinding binding;
public ViewHolder(@NonNull ItemQrPayButtonBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}

View File

@ -106,11 +106,15 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
public void onClickManualEntry() { public void onClickManualEntry() {
// showSingleInfoDialogAutoHide("Coming Soon!");
routeId = R.id.action_cardWaitingFragment_to_manualEntryFragment; routeId = R.id.action_cardWaitingFragment_to_manualEntryFragment;
safeNavigateToRouteId(); safeNavigateToRouteId();
} }
public void onClickTap() {
cardReadViewModel.setCardTransactionType(CardTransactionType.MOCK);
routeId = R.id.action_cardWaitingFragment_to_processingCardFragment;
safeNavigateToRouteId();
}
} }
@Override @Override

View File

@ -142,6 +142,10 @@ public class InputPasswordFragment extends DataBindingFragment implements DataBi
private void checkRoute() { private void checkRoute() {
switch (Objects.requireNonNull(sharedViewModel.transactionsType.getValue())) { switch (Objects.requireNonNull(sharedViewModel.transactionsType.getValue())) {
case MMQR_REFUND:
inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM);
routeId = R.id.action_inputPasswordFragment_to_QRRefundProcessFragment;
break;
case PRE_AUTH_COMPLETE_VOID: case PRE_AUTH_COMPLETE_VOID:
inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM); inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM);
sharedViewModel.transactionName.postValue(getResourceString(R.string.title_pre_auth_complete)); sharedViewModel.transactionName.postValue(getResourceString(R.string.title_pre_auth_complete));

View File

@ -7,6 +7,7 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.utsmm.kbz.util.MockData;
import com.utsmyanmar.baselib.fragment.DataBindingFragment; import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig; import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.checkxread.model.CardDataX; import com.utsmyanmar.checkxread.model.CardDataX;
@ -41,6 +42,8 @@ public class ProcessingCardFragment extends DataBindingFragment {
private TransProcessViewModel transProcessViewModel; private TransProcessViewModel transProcessViewModel;
private PinPadViewModel pinPadViewModel; private PinPadViewModel pinPadViewModel;
private EmvTransactionProcessViewModel emvTransactionViewModel; private EmvTransactionProcessViewModel emvTransactionViewModel;
private int routeId; private int routeId;
@Override @Override
@ -102,9 +105,24 @@ public class ProcessingCardFragment extends DataBindingFragment {
break; break;
case MAG: readMAGStripe(false,true); case MAG: readMAGStripe(false,true);
break; break;
case MOCK: mockMPUCard();
break;
} }
} }
private void mockMPUCard() {
LogUtil.d(TAG,"initialize mock card data...");
routeId = R.id.action_processingCardFragment_to_pinPadFragment;
transProcessViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
pinPadViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
CardDataX cardDataX = MockData.getInstance().generateMPUCard();
TradeData tradeData = TransactionUtil.getInstance().initMPUTransaction(cardDataX, CardTypeX.IC);
transProcessViewModel.setTradeData(tradeData);
pinPadViewModel.setTradeData(tradeData);
sharedViewModel.setCardDataExist(true);
safeNavigateToRouteId();
}
private void readMPUCard() { private void readMPUCard() {
cardReadViewModel.startReadXProcess(MPUXReadCard.getInstance(), new ReadCardResultX() { cardReadViewModel.startReadXProcess(MPUXReadCard.getInstance(), new ReadCardResultX() {

View File

@ -254,7 +254,6 @@ public class ProcessingFragment extends DataBindingFragment {
case FAIL: case FAIL:
case SUCCESS: case SUCCESS:
case OFFLINE_SUCCESS: case OFFLINE_SUCCESS:
LogUtil.d(TAG,"This was called!");
if(SystemParamsOperation.getInstance().getDemoStatus()) { if(SystemParamsOperation.getInstance().getDemoStatus()) {
delayFunctionCall(()->{ delayFunctionCall(()->{
// updateData(); // updateData();

View File

@ -42,12 +42,13 @@ import java.io.IOException;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.HttpException; import retrofit2.HttpException;
import com.utsmyanmar.paylibs.utils.LogUtil; import com.utsmyanmar.paylibs.utils.LogUtil;
// Temporarily disabled Hilt // Temporarily disabled Hilt
// @AndroidEntryPoint @AndroidEntryPoint
public class TransactionResultFragment extends DataBindingFragment implements DataBindingFragment.BackPressCallback { public class TransactionResultFragment extends DataBindingFragment implements DataBindingFragment.BackPressCallback {
private static final String TAG = TransactionResultFragment.class.getSimpleName(); private static final String TAG = TransactionResultFragment.class.getSimpleName();
@ -165,7 +166,8 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
} else if (transactionType == TransactionsType.SETTLEMENT) { } else if (transactionType == TransactionsType.SETTLEMENT) {
handleSettlementTransaction(siriusReq); handleSettlementTransaction(siriusReq);
} else if (isWavePayNonSuccessTransaction(transactionType, payDetail)) { } else if (isWavePayNonSuccessTransaction(transactionType, payDetail)) {
startPrintProcess(false); // startPrintProcess(false);
showDeclineDialog("QR Payment Failed!");
navigateToMainScreen(); navigateToMainScreen();
} else if (isWavePaySuccessTransaction(transactionType, payDetail)) { } else if (isWavePaySuccessTransaction(transactionType, payDetail)) {
handleWavePaySuccessTransaction(payDetail, siriusReq); handleWavePaySuccessTransaction(payDetail, siriusReq);

View File

@ -25,6 +25,8 @@ import java.util.HashSet;
import javax.inject.Inject; import javax.inject.Inject;
import io.reactivex.rxjava3.core.Single;
public class CardReadViewModel extends ViewModel { public class CardReadViewModel extends ViewModel {
@ -46,7 +48,6 @@ public class CardReadViewModel extends ViewModel {
public SingleLiveEvent<Integer> cardTypeData = new SingleLiveEvent<>(); public SingleLiveEvent<Integer> cardTypeData = new SingleLiveEvent<>();
public SingleLiveEvent<PayDetail> payDetail = new SingleLiveEvent<>(); public SingleLiveEvent<PayDetail> payDetail = new SingleLiveEvent<>();
public SingleLiveEvent<CardReadStatus> readStatus = new SingleLiveEvent<>();
public SingleLiveEvent<String> checkCardAlertMsg = new SingleLiveEvent<>(); public SingleLiveEvent<String> checkCardAlertMsg = new SingleLiveEvent<>();
@ -95,96 +96,6 @@ public class CardReadViewModel extends ViewModel {
checkCardAlertMsg.setValue(null); checkCardAlertMsg.setValue(null);
} }
// public void initCardReadProcess(int allType, int timeOutInSec, ReadCardResult readCardResult) {
// ReadCardProcess.getInstance().startReadCard(allType,"",timeOutInSec,false).onStartReadCardProcess(new CheckCardListener() {
// @Override
// public void onCheckCardSuccess(int cardType, MPUCardData mpuCardData, CardScheme cardScheme) {
//
// cardData.postValue(mpuCardData);
// cardSchemeData.postValue(cardScheme);
// cardTypeData.postValue(cardType);
// PayDetail payDetailData = new PayDetail();
// payDetailData.setCardNo(mpuCardData.getPan());
// payDetailData.setEXPDate(mpuCardData.getExp());
// payDetailData.setAccountType(cardScheme.name());
// payDetailData.setCardType(cardType);
// payDetailData.setCardHolderName(mpuCardData.getCardHolderName());
// CardInfo cardInfo = new CardInfo();
// MAGCardInfo magCardInfo = new MAGCardInfo();
// LogUtil.d(TAG,"track 2 : "+mpuCardData.getTrack2());
// magCardInfo.setTrack2Cipher(mpuCardData.getTrack2());
// cardInfo.setMAGCardInfo(magCardInfo);
// payDetailData.setCardInfo(cardInfo);
//
// payDetail.postValue(payDetailData);
//
// mainThreadHandler.post(readCardResult::onSuccess);
//// readCardResult.onSuccess();
//
// }
//
// @Override
// public void onCheckCardFail(int code, String message) {
//
// if(code == -2801) {
// mainThreadHandler.post(readCardResult::onCommunicationError);
//// readCardResult.onCommunicationError();
// } else {
// mainThreadHandler.post(() -> readCardResult.onError(code,message));
//// readCardResult.onError(code,message);
// }
//
// }
// });
// }
//
// public SingleLiveEvent<CardReadStatus> startReadProcess(int allType,int timeOutInSec){
//
// ReadCardProcess.getInstance().startReadCard(allType,"",timeOutInSec,false).onStartReadCardProcess(new CheckCardListener() {
// @Override
// public void onCheckCardSuccess(int cardType, MPUCardData mpuCardData, CardScheme cardScheme) {
// flag.postValue(true);
// cardData.postValue(mpuCardData);
// cardSchemeData.postValue(cardScheme);
// cardTypeData.postValue(cardType);
// PayDetail payDetailData = new PayDetail();
// payDetailData.setCardNo(mpuCardData.getPan());
// payDetailData.setEXPDate(mpuCardData.getExp());
// payDetailData.setAccountType(cardScheme.name());
// payDetailData.setCardType(cardType);
// payDetailData.setCardHolderName(mpuCardData.getCardHolderName());
// CardInfo cardInfo = new CardInfo();
// MAGCardInfo magCardInfo = new MAGCardInfo();
// LogUtil.d(TAG,"track 2 : "+mpuCardData.getTrack2());
// magCardInfo.setTrack2Cipher(mpuCardData.getTrack2());
// cardInfo.setMAGCardInfo(magCardInfo);
// payDetailData.setCardInfo(cardInfo);
//
// payDetail.postValue(payDetailData);
//
// readStatus.postValue(CardReadStatus.SUCCESS);
//
//
// }
//
// @Override
// public void onCheckCardFail(int code, String message) {
//
// readStatus.postValue(CardReadStatus.FAIL);
// // this action just need to do once because there's too many reason to get fail
// if(!oneTimeFlag){
// errorCode.postValue(code+":"+message);
// flag.postValue(false);
// cardSchemeData.postValue(CardScheme.UNK);
// oneTimeFlag = true;
// }
//
// }
// });
//
// return readStatus;
// }
public void resetOneTimeFlag(){ public void resetOneTimeFlag(){
oneTimeFlag = false; oneTimeFlag = false;
} }
@ -193,19 +104,4 @@ public class CardReadViewModel extends ViewModel {
NexGoSDK.getInstance().cancelCheckCard(); NexGoSDK.getInstance().cancelCheckCard();
} }
// public void checkCard(){
// int allType = AidlConstants.CardType.NFC.getValue() | AidlConstants.CardType.IC.getValue() | AidlConstants.CardType.MAGNETIC.getValue();
// ReadCardProcess.getInstance().startReadCard(allType,"",60,false).onStartReadCardProcess(new CheckCardListener() {
// @Override
// public void onCheckCardSuccess(int cardType, MPUCardData mpuCardData, CardScheme cardScheme) {
// System.out.println("Success");
// }
//
// @Override
// public void onCheckCardFail(int code, String message) {
//
// System.out.println("Failed");
// }
// });
// }
} }

View File

@ -492,9 +492,9 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
private void handleTransactionProcess() { private void handleTransactionProcess() {
String cardNo = mPayDetail.getCardNo(); String cardNo = mPayDetail.getCardNo();
if (cardNo == null || TextUtils.equals(cardNo, "")) { // if (cardNo == null || TextUtils.equals(cardNo, "")) {
getCardInfo(); getCardInfo();
} // }
getPayWaveData(); getPayWaveData();
getF055Data(); getF055Data();
if(transType.getValue() == TransactionsType.PRE_AUTH_COMPLETE || transType.getValue() == TransactionsType.PRE_AUTH_VOID || transType.getValue() == TransactionsType.REFUND ) { if(transType.getValue() == TransactionsType.PRE_AUTH_COMPLETE || transType.getValue() == TransactionsType.PRE_AUTH_VOID || transType.getValue() == TransactionsType.REFUND ) {

View File

@ -1,12 +1,15 @@
package com.utsmm.kbz.ui.core_viewmodel; package com.utsmm.kbz.ui.core_viewmodel;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.util.Log;
import dagger.hilt.android.lifecycle.HiltViewModel; import dagger.hilt.android.lifecycle.HiltViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import com.google.gson.Gson;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest; import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse; import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.baselib.repo.Repository; import com.utsmyanmar.baselib.repo.Repository;
@ -26,9 +29,15 @@ import com.utsmyanmar.paylibs.utils.enums.HostType;
import java.util.List; import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import retrofit2.HttpException;
import com.utsmyanmar.paylibs.utils.LogUtil; import com.utsmyanmar.paylibs.utils.LogUtil;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject; import javax.inject.Inject;
@HiltViewModel @HiltViewModel
@ -185,6 +194,15 @@ public class SharedViewModel extends ViewModel {
printReceiptButtons.postValue(value); printReceiptButtons.postValue(value);
} }
public void setTransactionsType(TransactionsType transactionsType) {
this.transactionsType.setValue(transactionsType);
}
public SingleLiveEvent<TransactionsType> getTransactionsType() {
return transactionsType;
}
public void setPrintReceiptMsg(String msg) { this.printReceiptMsg.setValue(msg);}
public void postPrintReceiptMsg(String msg) { this.printReceiptMsg.postValue(msg);} public void postPrintReceiptMsg(String msg) { this.printReceiptMsg.postValue(msg);}
public void setPrintStatus(PrintStatus printStatus) { this.printStatus.setValue(printStatus); } public void setPrintStatus(PrintStatus printStatus) { this.printStatus.setValue(printStatus); }
public void postPrintStatus(PrintStatus printStatus) { this.printStatus.postValue(printStatus); } public void postPrintStatus(PrintStatus printStatus) { this.printStatus.postValue(printStatus); }
@ -406,4 +424,33 @@ public class SharedViewModel extends ViewModel {
} }
public void pushReceipt(Object body){
repository.sendReceipt(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> Log.d("E-Receipt", "Success =>"+ response.getMessage()),
error -> {
if (error instanceof HttpException) {
HttpException httpEx = (HttpException) error;
try {
String errorJson = httpEx.response().errorBody().string();
Log.e("E-Receipt", "Raw error JSON => " + errorJson);
// Parse JSON to model
EReceiptResponse res = new Gson().fromJson(errorJson, EReceiptResponse.class);
Log.e("E-Receipt", "Parsed Error => " + res.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
} else {
Log.e("E-Receipt", "Unexpected error => " + error.getMessage());
}
}
);
}
} }

View File

@ -135,7 +135,7 @@ public class DashboardTransFragment extends DataBindingFragment {
} else if (checkTid()) { } else if (checkTid()) {
showDeclineDialog("Please Download Config!"); showDeclineDialog("Please Download Config!");
} else { } else {
sharedViewModel.transactionsType.setValue(TransactionsType.VOID); sharedViewModel.setTransactionsType(TransactionsType.VOID);
// sharedViewModel.transMenu.postValue(TransMenu.TRANSACTIONS); // sharedViewModel.transMenu.postValue(TransMenu.TRANSACTIONS);
sharedViewModel.setTransMenu(TransMenu.TRANSACTIONS); sharedViewModel.setTransMenu(TransMenu.TRANSACTIONS);
@ -147,16 +147,15 @@ public class DashboardTransFragment extends DataBindingFragment {
public void onClickSettlement() { public void onClickSettlement() {
sharedViewModel.settlementType.setValue(SettlementType.NORMAL); sharedViewModel.settlementType.setValue(SettlementType.NORMAL);
sharedViewModel.transactionsType.setValue(TransactionsType.SETTLEMENT);
sharedViewModel.setTransMenu(TransMenu.SETTLEMENT); sharedViewModel.setTransMenu(TransMenu.SETTLEMENT);
sharedViewModel.setTransactionsType(TransactionsType.SETTLEMENT);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment; routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId); safeRouteTo(currentId,routeId,hostId);
} }
public void onClickRefund() { public void onClickRefund() {
sharedViewModel.transactionsType.setValue(TransactionsType.REFUND); sharedViewModel.setTransactionsType(TransactionsType.REFUND);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment; routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId); safeRouteTo(currentId,routeId,hostId);
} }
@ -167,8 +166,7 @@ public class DashboardTransFragment extends DataBindingFragment {
} else if (checkTid()) { } else if (checkTid()) {
showDeclineDialog("Please Download Config!"); showDeclineDialog("Please Download Config!");
} else { } else {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_SALE); sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_SALE);
routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment; routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment;
safeRouteTo(currentId,routeId,hostId); safeRouteTo(currentId,routeId,hostId);
} }
@ -176,7 +174,7 @@ public class DashboardTransFragment extends DataBindingFragment {
} }
public void onClickPreAuthCancel () { public void onClickPreAuthCancel () {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_VOID); sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_FULL_VOID); sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_FULL_VOID);
sharedViewModel.amount.postValue(null); sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment; routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
@ -185,14 +183,14 @@ public class DashboardTransFragment extends DataBindingFragment {
} }
public void onClickPreAuthComp () { public void onClickPreAuthComp () {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_COMPLETE); sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_COMPLETE);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment; routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId); safeRouteTo(currentId,routeId,hostId);
} }
public void onClickPreAuthCompCancel () { public void onClickPreAuthCompCancel () {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_COMPLETE_VOID); sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_COMPLETE_VOID);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment; routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId); safeRouteTo(currentId,routeId,hostId);
@ -200,7 +198,8 @@ public class DashboardTransFragment extends DataBindingFragment {
public void onClickCashAdvance() { public void onClickCashAdvance() {
sharedViewModel.transactionsType.setValue(TransactionsType.CASH_OUT); sharedViewModel.setTransactionsType(TransactionsType.CASH_OUT);
sharedViewModel.setTransMenu(TransMenu.CASH_OUT);
routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment; routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment;
safeRouteTo(currentId,routeId,hostId); safeRouteTo(currentId,routeId,hostId);

View File

@ -13,6 +13,7 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import com.kizzy.xpay.util.Sign; import com.kizzy.xpay.util.Sign;
import com.utsmm.kbz.ui.qr_pay.QRRefund;
import com.utsmyanmar.baselib.network.model.DemoQRRequest; import com.utsmyanmar.baselib.network.model.DemoQRRequest;
import com.utsmyanmar.baselib.network.model.DemoQRResponse; import com.utsmyanmar.baselib.network.model.DemoQRResponse;
import com.utsmyanmar.baselib.network.model.DemoQRReturnRequest; import com.utsmyanmar.baselib.network.model.DemoQRReturnRequest;
@ -36,6 +37,7 @@ import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.system.SingleLiveEvent; import com.utsmyanmar.paylibs.system.SingleLiveEvent;
import com.utsmyanmar.paylibs.utils.LogUtil; import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation; import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.HashMap; import java.util.HashMap;
@ -52,7 +54,11 @@ public class KPayViewModel extends ViewModel {
private final Repository repository; private final Repository repository;
private final String appKey = "UTSMMuat@2025"; private final String appKey = SystemParamsOperation.getInstance().getAppKey();
// private final String appKey = "UTSMMuat@2025";
private final String appId = SystemParamsOperation.getInstance().getAppId();
// private final String appId = "kp9b0794b349ae85b00c51e0677484c7";
private final ExecutorService executor = Executors.newSingleThreadExecutor(); private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Handler mainHandler = new Handler(Looper.getMainLooper()); private final Handler mainHandler = new Handler(Looper.getMainLooper());
@ -114,7 +120,7 @@ public class KPayViewModel extends ViewModel {
bizContent.put("refund_request_no", refundOrderId); bizContent.put("refund_request_no", refundOrderId);
bizContent.put("merch_order_id", origOrderId); bizContent.put("merch_order_id", origOrderId);
bizContent.put("merch_code", mid); bizContent.put("merch_code", mid);
bizContent.put("appid", "kp9b0794b349ae85b00c51e0677484c7"); bizContent.put("appid", appId);
if(!refundAmount.equals("0")) { if(!refundAmount.equals("0")) {
bizContent.put("refund_amount", refundAmount); bizContent.put("refund_amount", refundAmount);
} }
@ -138,7 +144,7 @@ public class KPayViewModel extends ViewModel {
KPayRefund.RefundRequest.RequestBody.BizContent biz = KPayRefund.RefundRequest.RequestBody.BizContent biz =
new KPayRefund.RefundRequest.RequestBody.BizContent( new KPayRefund.RefundRequest.RequestBody.BizContent(
"kp9b0794b349ae85b00c51e0677484c7", appId,
mid, mid,
origOrderId, origOrderId,
refundAmount, // This can be null for original refunds refundAmount, // This can be null for original refunds
@ -160,7 +166,7 @@ public class KPayViewModel extends ViewModel {
} else { } else {
KPayRefund.RefundRequest.RequestBody.BizContent biz = KPayRefund.RefundRequest.RequestBody.BizContent biz =
new KPayRefund.RefundRequest.RequestBody.BizContent( new KPayRefund.RefundRequest.RequestBody.BizContent(
"kp9b0794b349ae85b00c51e0677484c7", appId,
mid, mid,
origOrderId, origOrderId,
refundOrderId, refundOrderId,
@ -195,7 +201,7 @@ public class KPayViewModel extends ViewModel {
Map<String, Object> bizContent = new HashMap<>(); Map<String, Object> bizContent = new HashMap<>();
bizContent.put("merch_order_id", merchOrderId); bizContent.put("merch_order_id", merchOrderId);
bizContent.put("merch_code", mid); bizContent.put("merch_code", mid);
bizContent.put("appid", "kp9b0794b349ae85b00c51e0677484c7"); bizContent.put("appid", appId);
bizContent.put("trade_type", "PAY_BY_QRCODE"); bizContent.put("trade_type", "PAY_BY_QRCODE");
bizContent.put("total_amount", amount); bizContent.put("total_amount", amount);
bizContent.put("title", "testing"); bizContent.put("title", "testing");
@ -216,7 +222,7 @@ public class KPayViewModel extends ViewModel {
String sign = Sign.INSTANCE.generateSign(requestMap, appKey); String sign = Sign.INSTANCE.generateSign(requestMap, appKey);
KPayQRRequest.QrRequest.RequestBody.BizContent biz = new KPayQRRequest.QrRequest.RequestBody.BizContent( KPayQRRequest.QrRequest.RequestBody.BizContent biz = new KPayQRRequest.QrRequest.RequestBody.BizContent(
"kp9b0794b349ae85b00c51e0677484c7", appId,
mid, mid,
merchOrderId, merchOrderId,
"PAY_BY_QRCODE", "PAY_BY_QRCODE",
@ -252,7 +258,7 @@ public class KPayViewModel extends ViewModel {
Map<String, Object> bizContentMap = new HashMap<>(); Map<String, Object> bizContentMap = new HashMap<>();
bizContentMap.put("merch_order_id", merchOrderId); bizContentMap.put("merch_order_id", merchOrderId);
bizContentMap.put("merch_code", mid); bizContentMap.put("merch_code", mid);
bizContentMap.put("appid", "kp9b0794b349ae85b00c51e0677484c7"); bizContentMap.put("appid", appId);
Map<String, Object> requestMap = new HashMap<>(); Map<String, Object> requestMap = new HashMap<>();
requestMap.put("timestamp", currentTime); requestMap.put("timestamp", currentTime);
@ -264,7 +270,7 @@ public class KPayViewModel extends ViewModel {
String sign = Sign.INSTANCE.generateSign(requestMap, appKey); String sign = Sign.INSTANCE.generateSign(requestMap, appKey);
KPayQRQueryRequest.QRQueryRequest.Request.BizContent bizContent = new KPayQRQueryRequest.QRQueryRequest.Request.BizContent( KPayQRQueryRequest.QRQueryRequest.Request.BizContent bizContent = new KPayQRQueryRequest.QRQueryRequest.Request.BizContent(
"kp9b0794b349ae85b00c51e0677484c7", appId,
mid, mid,
merchOrderId merchOrderId
); );
@ -291,6 +297,7 @@ public class KPayViewModel extends ViewModel {
private TradeData tradeData; private TradeData tradeData;
private PayDetail payDetail; private PayDetail payDetail;
private QRRefund qrRefund;
public void setTradeData(TradeData tradeData){ public void setTradeData(TradeData tradeData){
this.tradeData = tradeData; this.tradeData = tradeData;
@ -304,6 +311,12 @@ public class KPayViewModel extends ViewModel {
public void setPayDetail(PayDetail payDetail) { public void setPayDetail(PayDetail payDetail) {
this.payDetail = payDetail; this.payDetail = payDetail;
} }
public void setQrRefund(QRRefund qrRefund) {
this.qrRefund = qrRefund;
}
public QRRefund getQrRefund() {
return qrRefund;
}
public PayDetail getPayDetail() { return payDetail; } public PayDetail getPayDetail() { return payDetail; }

View File

@ -1,6 +1,7 @@
package com.utsmm.kbz.ui.kpay; package com.utsmm.kbz.ui.kpay;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -13,6 +14,7 @@ import androidx.annotation.Nullable;
import com.utsmyanmar.baselib.fragment.DataBindingFragment; import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.DemoQRStatusRequest; import com.utsmyanmar.baselib.network.model.DemoQRStatusRequest;
import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest; import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptQRRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig; import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TimeoutCallback; import com.utsmyanmar.baselib.util.TimeoutCallback;
import com.utsmyanmar.ecr.ECRHelper; import com.utsmyanmar.ecr.ECRHelper;
@ -27,6 +29,10 @@ import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.TransactionUtil; import com.utsmm.kbz.util.TransactionUtil;
import com.utsmm.kbz.util.ecr.CoreUtils; import com.utsmm.kbz.util.ecr.CoreUtils;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -308,6 +314,30 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
retrievedUpdatePayDetail(refLabel, payDetail,false); 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);
return; return;
} }
@ -317,6 +347,26 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
payDetail.setQrTransStatus(2); 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.payDetail.postValue(payDetail); sharedViewModel.payDetail.postValue(payDetail);
safeNavigateToRouteId(); safeNavigateToRouteId();
@ -325,6 +375,26 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
} catch (Exception e) { } catch (Exception e) {
LogUtil.d(TAG, "On Exception::"); LogUtil.d(TAG, "On Exception::");
e.printStackTrace(); 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 (count == totalCount) {
if(payDetail.getQrTransStatus() != -1) { if(payDetail.getQrTransStatus() != -1) {
payDetail.setQrTransStatus(2); payDetail.setQrTransStatus(2);
@ -332,8 +402,6 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
sharedViewModel.payDetail.postValue(payDetail); sharedViewModel.payDetail.postValue(payDetail);
safeNavigateToRouteId(); safeNavigateToRouteId();
} }
} }

View File

@ -13,6 +13,7 @@ import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.pinpad.AlgorithmModeEnum; import com.nexgo.oaf.apiv3.device.pinpad.AlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.OnPinPadInputListener; import com.nexgo.oaf.apiv3.device.pinpad.OnPinPadInputListener;
import com.nexgo.oaf.apiv3.device.pinpad.PinAlgorithmModeEnum; import com.nexgo.oaf.apiv3.device.pinpad.PinAlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinKeyboardModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinKeyboardViewModeEnum; import com.nexgo.oaf.apiv3.device.pinpad.PinKeyboardViewModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinPad; import com.nexgo.oaf.apiv3.device.pinpad.PinPad;
import com.nexgo.oaf.apiv3.device.pinpad.PinPadKeyCode; import com.nexgo.oaf.apiv3.device.pinpad.PinPadKeyCode;
@ -119,7 +120,12 @@ public class PinPadViewModel extends ViewModel {
case ON_CONFIRM_CLICK: case ON_CONFIRM_CLICK:
LogUtil.d(TAG, "ON CLICK CONFIRM"); LogUtil.d(TAG, "ON CLICK CONFIRM");
// increasedKSN(); // increasedKSN();
pinStatus.postValue(PinPadStatus.ON_CONFIRM); if(transType.getValue() == TransactionsType.PRE_AUTH_COMPLETE || transType.getValue() == TransactionsType.PRE_AUTH_VOID || transType.getValue() == TransactionsType.REFUND ) {
pinStatus.postValue(PinPadStatus.ON_NEXT_SCREEN);
} else {
pinStatus.postValue(PinPadStatus.ON_CONFIRM);
}
break; break;
case ON_CANCEL_CLICK: case ON_CANCEL_CLICK:
LogUtil.d(TAG, "ON CLICK CANCEL"); LogUtil.d(TAG, "ON CLICK CANCEL");
@ -317,6 +323,8 @@ public class PinPadViewModel extends ViewModel {
r.bottom = customPinPadKeyboard.getKey_ok().getHeight() + r.top; r.bottom = customPinPadKeyboard.getKey_ok().getHeight() + r.top;
pinpadLayout.setKeyConfirm(r); pinpadLayout.setKeyConfirm(r);
pinPad.setPinKeyboardMode(PinKeyboardModeEnum.FIXED);
byte[] number = pinPad.setPinpadLayout(pinpadLayout); byte[] number = pinPad.setPinpadLayout(pinpadLayout);
if(number != null) { if(number != null) {
@ -331,6 +339,8 @@ public class PinPadViewModel extends ViewModel {
byte[] panBytes = pan.substring(length - 13).getBytes(StandardCharsets.US_ASCII); byte[] panBytes = pan.substring(length - 13).getBytes(StandardCharsets.US_ASCII);
// byte[] panBytes = pan.substring(length - 13, length - 1).getBytes(StandardCharsets.US_ASCII); // byte[] panBytes = pan.substring(length - 13, length - 1).getBytes(StandardCharsets.US_ASCII);
pinPad.setPinKeyboardViewMode(PinKeyboardViewModeEnum.DEFAULT); pinPad.setPinKeyboardViewMode(PinKeyboardViewModeEnum.DEFAULT);
pinPad.setPinKeyboardMode(PinKeyboardModeEnum.FIXED);
// pinPad.setAlgorithmMode(AlgorithmModeEnum.DES); // pinPad.setAlgorithmMode(AlgorithmModeEnum.DES);

View File

@ -8,6 +8,9 @@ import com.utsmyanmar.paylibs.model.PayDetail;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class PreAuthVoidViewModel extends ViewModel { public class PreAuthVoidViewModel extends ViewModel {
private Repository repository; private Repository repository;

View File

@ -0,0 +1,61 @@
package com.utsmm.kbz.ui.qr_pay;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemQrHistoryBinding;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.ArrayList;
import java.util.List;
public class QRHistoryAdapter extends RecyclerView.Adapter<QRHistoryAdapter.ViewHolder> {
private List<PayDetail> items = new ArrayList<>();
public void setItems(List<PayDetail> data){
items = data;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemQrHistoryBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_qr_history,
parent,
false
);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final ItemQrHistoryBinding binding;
public ViewHolder(@NonNull ItemQrHistoryBinding binding){
super(binding.getRoot());
this.binding = binding;
}
public void bind(PayDetail item){
binding.setPayDetail(item);
binding.executePendingBindings();
}
}
}

View File

@ -0,0 +1,78 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class QRHistoryFragment extends DataBindingFragment {
private QRHistoryAdapter adapter;
private QRHistoryViewModel viewModel;
@Override
protected void initViewModel() {
viewModel = new ViewModelProvider(this).get(QRHistoryViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
DataBindingConfig bindingConfig = new DataBindingConfig(R.layout.fragment_qr_history, BR.viewModel, viewModel);
bindingConfig.addBindingParam(BR.adapter, adapter);
return bindingConfig;
}
@Override
protected int currentId() {
return 0;
}
@Override
protected int hostId() {
return 0;
}
@Override
protected int routeId() {
return 0;
}
@Override
public void onResume(){
super.onResume();
setToolBarTitleWithBackIcon("History");
observeData();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstance){
super.onViewCreated(view, savedInstance);
RecyclerView rvHistory = view.findViewById(R.id.qrRvHistory);
rvHistory.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new QRHistoryAdapter();
rvHistory.setAdapter(adapter);
observeData();
}
private void observeData(){
viewModel.getAllQrHistory().observe(getViewLifecycleOwner(), list -> {
adapter.setItems(list);
});
}
}

View File

@ -0,0 +1,35 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class QRHistoryViewModel extends ViewModel {
private final Repository repository;
private final LiveData<List<PayDetail>> history;
private final LiveData<List<PayDetail>> refundList;
@Inject
public QRHistoryViewModel(Repository repository){
this.repository = repository;
this.history = repository.getAllQRHistory();
this.refundList = repository.getRefundableQRHistory();
}
public LiveData<List<PayDetail>> getAllQrHistory(){
return history;
}
public LiveData<List<PayDetail>> getRefundableQrHistory(){
return refundList;
}
}

View File

@ -0,0 +1,400 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.MainViewModel;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.adapters.QRPayAdapter;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.management.ManagementViewModel;
import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import com.utsmm.kbz.util.Connectivity;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
import com.utsmyanmar.paylibs.utils.enums.HostType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import java.util.Date;
import java.util.List;
public class QRPayFragment extends DataBindingFragment {
private MainViewModel mainViewModel;
private SharedViewModel sharedViewModel;
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
private static final int currentId = R.id.qrFragment;
private Observer<PayDetail> observeLastTrans;
private PayDetail lastPay;
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithBackIcon("MMQR PAY");
}
@Override
protected void initViewModel() {
mainViewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
// sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
List<QRPayItem> features = List.of(
new QRPayItem("Sale", R.drawable.ic_qr_pay),
new QRPayItem("Refund", R.drawable.ic_refund),
new QRPayItem("History", R.drawable.ic_history)
);
QRPayAdapter adapter = new QRPayAdapter(features, position -> {
QRPayItem selectedBtn = features.get(position);
switch (selectedBtn.title){
case "Sale":
onClickQRPay();
break;
case "Refund":
onClickRefund();
break;
case "History":
onClickHistory();
break;
}
});
DataBindingConfig config = new DataBindingConfig(R.layout.fragment_qr_pay, BR.mainViewModel, mainViewModel);
config.addBindingParam(BR.sharedViewModel,sharedViewModel);
config.addBindingParam(BR.adapter, adapter);
return config;
}
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {
return hostId;
}
@Override
protected int routeId() {
return routeId;
}
public void onClickQRPay() {
String mmqrIp = SystemParamsOperation.getInstance().getSecHostIpAddress();
if (mainViewModel.payDetailSingle.getValue() != null) {
mainViewModel.startReversal(mainViewModel.payDetailSingle.getValue());
}
// else if (TMSUtil.getInstance().checkSecHostParams().isStatus() == ValidityStatus.FAILURE) {
// showDeclineDialog(getResourceString(R.string.txt_please_download_config)+"\n"+TMSUtil.getInstance().checkSecHostParams().getMessage());
// }
else if (!Connectivity.isConnectedWifi(getContext()) && !Connectivity.isConnectedMobile(getContext())) {
showSingleInfoDialog(getResourceString(R.string.txt_please_enable_internet));
}
// else if (!isValidDomain(mmqrIp)) {
// showDeclineDialog(getResourceString(R.string.txt_please_check_mmqr_ip));
// }
else {
CurrencyType currencyType = TMSUtil.getInstance().currencyTextToCurrencyType(SystemParamsOperation.getInstance().getSecHostCurrency());
sharedViewModel.set_currencyText(currencyType.name);
processBatch();
sharedViewModel.transactionsType.setValue(TransactionsType.MMQR);
navigateToAmount();
}
}
private void onClickHistory(){
sharedViewModel.hostType.setValue(HostType.QR);
routeId = R.id.action_qrFragment_to_qrHistory;
safeNavigateToRouteId();
}
private void onClickRefund(){
// sharedViewModel.setTransactionsType(TransactionsType.MMQR_REFUND);
routeId = R.id.action_qrFragment_to_qr_refund_list;
// routeId = R.id.action_qrFragment_to_inputPasswordFragment;
safeNavigateToRouteId();
}
private void navigateToAmount() {
routeId = R.id.action_qrFragment_to_inputAmountFragment;
safeNavigateToRouteId();
}
private void processBatch() {
calculateLastTransaction();
mainViewModel.observeSettlementPOS();
observeLastTrans = payDetail -> {
if(payDetail == null) {
return;
}
// SystemParamsOperation.getInstance().saveSerialNumber(payDetail.getVoucherNo());
// SystemParamsOperation.getInstance().saveInvoiceNumber(payDetail.getInvoiceNo());
if(lastPay != null) {
Date lastPayDate = POSUtil.getInstance().getDateByString(lastPay.getTransDate());
Date observePayDate = POSUtil.getInstance().getDateByString(payDetail.getTransDate());
if(lastPayDate.compareTo(observePayDate) < 0) {
payDetail = lastPay;
}
}
// LogUtil.d(TAG,"TransDetail :"+payDetail.getTransType() + "- Trans Time :"+payDetail.getTransDate());
if(payDetail.getTransactionType() == TransactionsType.SETTLEMENT.value){
return;
}
/*
* saving for api post request, to send in next time.
* */
SystemParamsOperation.getInstance().setLastTransTime(String.valueOf(payDetail.getTransNum()));
SystemParamsOperation.getInstance().setLastTransName(payDetail.getTransType());
String lastTransDate = payDetail.getTransDate();
String lastTransTime = payDetail.getTransTime();
Date today = POSUtil.getInstance().getCurrentDate();
Date transDate;
Date current = new Date(System.currentTimeMillis());
String configTimer = SystemParamsOperation.getInstance().getClearBatchTime();
// LogUtil.d(TAG,"Config Time :" + configTimer);
String configTime = "23:00";
int configDay = 10;
String configRawDay = SystemParamsOperation.getInstance().getClearBatchDay();
if(configRawDay != null && !configRawDay.trim().isEmpty()) {
try {
configDay = Integer.parseInt(configRawDay);
} catch (Exception e) {
e.printStackTrace();
}
}
if(configTimer != null && !configTimer.trim().isEmpty() && checkValidTime(configTimer.trim())) {
configTime = configTimer.trim();
}
Date configDateTime = POSUtil.getInstance().getDateTime(configTime);
Date yesterdayConfigDateTime = POSUtil.getInstance().getYesterdayConfigDateTime(configTime);
transDate = POSUtil.getInstance().getDateByString(lastTransDate);
Date yesterdayDate = POSUtil.getInstance().getYesterdayDate();
if(today.compareTo(transDate) == 0) {
// LogUtil.d(TAG,"Last Trans is Today!");
// LogUtil.d(TAG,"date current :!"+current);
// LogUtil.d(TAG,"date config :!"+configDateTime);
// Today
if(current.compareTo(configDateTime) > 0) {
// clear batch and force update param
// LogUtil.d(TAG,"Last Trans is Today! Config time is passed!");
// if (!SystemParamsOperation.getInstance().isClearBatch()) {
if (checkSyncTrans() || !SystemParamsOperation.getInstance().isClearBatch() ) {
// LogUtil.d(TAG,"Clearing....");
if(!SystemParamsOperation.getInstance().getSettlementStatus()) {
clearBatchAndDownload(configDay,payDetail);
}
}
} else {
// LogUtil.d(TAG,"Last Trans is Today! Config time is not passed yet.....");
SystemParamsOperation.getInstance().setClearBatch(false);
}
} else {
// LogUtil.d(TAG,"Last Trans is Before Today!");
Date yest;
if(configTime.equals("00:00")) {
yest = configDateTime;
} else {
yest = yesterdayConfigDateTime;
}
Date lastTransDateTime = POSUtil.getInstance().getDateByTransDateTime(lastTransDate,lastTransTime);
// LogUtil.d(TAG,"Compare Result : "+lastTransDateTime.compareTo(yest));
if(yest.compareTo(lastTransDateTime) < 0) {
// LogUtil.d(TAG,"Trans is yesterday, but after config time, no action!");
} else {
// LogUtil.d(TAG,"Trans is yesterday, before config time,");
// if(!checkSyncTrans()) {
// LogUtil.d(TAG,"Clearing....");
if(!SystemParamsOperation.getInstance().getSettlementStatus()) {
clearBatchAndDownload(configDay,payDetail);
}
// }
}
}
};
if(mainViewModel.lastTrans.hasActiveObservers()) {
mainViewModel.lastTrans.removeObserver(observeLastTrans);
}
mainViewModel.lastTrans.observe(getViewLifecycleOwner(),observeLastTrans);
}
private void calculateLastTransaction() {
mainViewModel.allTrans.observe(getViewLifecycleOwner(), new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
if(payDetails == null || payDetails.isEmpty()) {
return;
}
PayDetail tempPay = payDetails.get(0);
for (int i = 0; i < payDetails.size() ; i++) {
Date tempDate = POSUtil.getInstance().getDateByString(tempPay.getTransDate());
Date indexDate = POSUtil.getInstance().getDateByString(payDetails.get(i).getTransDate());
if(tempDate.compareTo(indexDate) > 0) {
// if before
tempPay = payDetails.get(i);
}
}
lastPay = tempPay;
// LogUtil.d(TAG,"Transaction Name : "+tempPay.getTransType());
// LogUtil.d(TAG,"Transaction Type : "+tempPay.getTransactionType());
// LogUtil.d(TAG,"Transaction Amount : "+tempPay.getAmount());
// LogUtil.d(TAG,"Transaction Date/Time : "+tempPay.getTransDate()+"/"+tempPay.getTransTime());
// LogUtil.d(TAG,"RRN/Trace : "+tempPay.getReferNo()+"/"+tempPay.getVoucherNo());
// LogUtil.d(TAG,"Transaction is canceled : "+tempPay.isCanceled());
// LogUtil.d(TAG,"Transaction settlement config : "+tempPay.isSettlementEnabled());
// LogUtil.d(TAG,"----------------------------------------------------------");
}
});
}
private boolean checkSyncTrans() {
String transDateString = SystemParamsOperation.getInstance().getSyncTransDate();
Date today = POSUtil.getInstance().getCurrentDate();
if(transDateString == null || transDateString.isEmpty()) {
return true;
}
Date transDate = POSUtil.getInstance().getDateByString(transDateString);
return today.compareTo(transDate) == 0;
}
private void clearBatchAndDownload(int configDay,PayDetail payDetail) {
Date yesterday = POSUtil.getInstance().getYesterdayDate();
Date lastInputDay = POSUtil.getInstance().getLastInputDay(configDay);
mainViewModel.settlementPOS.observe(getViewLifecycleOwner(), list -> {
Date transDate;
if(list == null || list.size() == 0) {
return;
}
for (PayDetail pay:list) {
/*
* To Hold One day transaction!
* */
transDate = POSUtil.getInstance().getDateByString(pay.getTransDate());
if (pay.getTransactionType() == TransactionsType.SALE.value) {
if(lastInputDay.compareTo(transDate) > 0) {
mainViewModel.deletePayDetail(pay);
}
} else {
if(yesterday.compareTo(transDate) > 0 ) {
mainViewModel.deletePayDetail(pay);
}
}
}
});
mainViewModel.deleteTrans.observe(getViewLifecycleOwner(), lists -> {
Date transDate;
if(lists == null || lists.size() == 0) {
return;
}
for (PayDetail pay:lists) {
/*
* To Hold One day transaction!
* */
transDate = POSUtil.getInstance().getDateByString(pay.getTransDate());
// LogUtil.d(TAG,"Trans Type : "+pay.getTransType()+"<=====> Trans Date :"+pay.getTransDate());
// LogUtil.d(TAG,"Yesterday Date: "+yesterday.toString());
// LogUtil.d(TAG,"Compare result :"+yesterday.compareTo(transDate));
if(yesterday.compareTo(transDate) > 0) {
mainViewModel.deletePayDetail(pay);
}
}
});
mainViewModel.preAuthTrans.observe(getViewLifecycleOwner(), lists -> {
Date transDate;
if(lists == null || lists.size() == 0) {
return;
}
for (PayDetail pay:lists) {
transDate = POSUtil.getInstance().getDateByString(pay.getTransDate());
if (lastInputDay.compareTo(transDate) > 0) {
mainViewModel.deletePayDetail(pay);
}
}
});
/*
*
* Need to think about his problem .... Nov 7, 2024
* */
if(!SystemParamsOperation.getInstance().isClearBatch()) {
// downloadParams(payDetail.getTransType(),String.valueOf(payDetail.getTransNum()),TMSUpdate.UPDATE,false);
}
}
}

View File

@ -0,0 +1,11 @@
package com.utsmm.kbz.ui.qr_pay;
public class QRPayItem {
public String title;
public int icon;
public QRPayItem(String title, int icon) {
this.title = title;
this.icon = icon;
}
}

View File

@ -0,0 +1,42 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class QRPayViewModel extends ViewModel {
private final Repository repository;
private final LiveData<List<PayDetail>> refundableHistory;
private final MutableLiveData<PayDetail> _payDetail = new MutableLiveData<>();
public LiveData<PayDetail> payDetail = _payDetail;
@Inject
public QRPayViewModel(Repository repository){
this.repository = repository;
this.refundableHistory = repository.getRefundableQRHistory();
}
public LiveData<List<PayDetail>> getRefundableQrHistory(){
return refundableHistory;
}
public void setPayDetail(PayDetail payDetail){
_payDetail.setValue(payDetail);
}
public Runnable onCancel;
public Runnable onConfirm;
}

View File

@ -0,0 +1,49 @@
package com.utsmm.kbz.ui.qr_pay;
public class QRRefund {
private String referenceNo;
private String refundAmount;
private String originalAmount;
private String reason;
public QRRefund(String referenceNo, String refundAmount, String originalAmount, String reason) {
this.referenceNo = referenceNo;
this.refundAmount = refundAmount;
this.originalAmount = originalAmount;
this.reason = reason;
}
public String getReferenceNo() {
return referenceNo;
}
public void setReferenceNo(String referenceNo) {
this.referenceNo = referenceNo;
}
public String getRefundAmount() {
return refundAmount;
}
public void setRefundAmount(String refundAmount) {
this.refundAmount = refundAmount;
}
public String getOriginalAmount() {
return originalAmount;
}
public void setOriginalAmount(String originalAmount) {
this.originalAmount = originalAmount;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}

View File

@ -0,0 +1,221 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.text.InputFilter;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.DecimalDigitsInputFilter;
import com.utsmm.kbz.util.TransactionUtil;
public class QRRefundDetailFragment extends DataBindingFragment {
private SharedViewModel sharedViewModel;
private KPayViewModel kPayViewModel;
private int routeId;
private TradeData tradeData;
private PayDetail payDetail;
// UI Elements
private RadioGroup radioGroupRefundType;
private RadioButton radioOriginal, radioPartial;
private EditText etReferenceNo, etOriginalAmount, etRefundAmount, etRefundReason;
private LinearLayout originalAmountLayout, refundAmountLayout;
private boolean isPartialRefund = false;
private static final String TAG = com.utsmm.kbz.ui.kpay.QRRefundFragment.class.getSimpleName();
private static final int hostId = Constants.NAV_HOST_ID;
private static final int currentId = R.id.qrRefundDetail;
@Override
protected void initViewModel() {
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
kPayViewModel = getFragmentScopeViewModel(KPayViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_qr_refund_detail, BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.kPayViewModel, kPayViewModel)
.addBindingParam(BR.click, new ClickEvent());
}
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {
return hostId;
}
@Override
protected int routeId() {
return routeId;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(getArguments() != null){
payDetail = (PayDetail) getArguments().getSerializable("payDetail");
}
}
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithBackIcon("QR Refund");
kPayViewModel.invalidAmountMsg.setValue("");
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initViews();
initData();
setupRadioGroupListener();
}
private void initViews() {
radioGroupRefundType = mBinding.getRoot().findViewById(R.id.radio_group_refund_type);
radioOriginal = mBinding.getRoot().findViewById(R.id.radio_original);
radioPartial = mBinding.getRoot().findViewById(R.id.radio_partial);
etReferenceNo = mBinding.getRoot().findViewById(R.id.et_reference_no);
etOriginalAmount = mBinding.getRoot().findViewById(R.id.et_original_amount);
etRefundAmount = mBinding.getRoot().findViewById(R.id.et_refund_amount);
etRefundReason = mBinding.getRoot().findViewById(R.id.et_refund_reason);
originalAmountLayout = mBinding.getRoot().findViewById(R.id.original_amount_layout);
refundAmountLayout = mBinding.getRoot().findViewById(R.id.refund_amount_layout);
// Set input filters for amount fields
etOriginalAmount.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(11, 2)});
etRefundAmount.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(11, 2)});
}
private void initData() {
payDetail = TransactionUtil.getInstance().initWalletTransaction(TransactionsType.MMQR_REFUND);
payDetail.setInvoiceNo(SystemParamsOperation.getInstance().getIncrementInvoiceNum());
if(getArguments() != null){
PayDetail passData = (PayDetail) getArguments().getSerializable("payDetail");
if(passData != null && passData.getReferNo() != null){
etReferenceNo.setText(passData.getReferNo());
etReferenceNo.setEnabled(false);
}
}
}
private void setupRadioGroupListener() {
radioGroupRefundType.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.radio_partial) {
// Show both amount fields for partial refund
isPartialRefund = true;
originalAmountLayout.setVisibility(View.VISIBLE);
refundAmountLayout.setVisibility(View.VISIBLE);
} else {
// Hide amount fields for original refund
isPartialRefund = false;
originalAmountLayout.setVisibility(View.GONE);
refundAmountLayout.setVisibility(View.GONE);
}
});
}
public class ClickEvent {
public void onCancel() {
safePopBackStack();
}
public void onConfirm() {
kPayViewModel.invalidAmountMsg.setValue("");
String referenceNo = etReferenceNo.getText().toString().trim();
String refundReason = etRefundReason.getText().toString().trim();
// Validate reference number
if (referenceNo.isEmpty()) {
kPayViewModel.invalidAmountMsg.setValue("Enter reference number");
return;
}
if (isPartialRefund) {
// Partial refund validation
String originalAmountStr = etOriginalAmount.getText().toString().trim();
String refundAmountStr = etRefundAmount.getText().toString().trim();
if (originalAmountStr.isEmpty()) {
kPayViewModel.invalidAmountMsg.setValue("Enter original amount");
return;
}
if (refundAmountStr.isEmpty()) {
kPayViewModel.invalidAmountMsg.setValue("Enter refund amount");
return;
}
double originalAmount = Double.parseDouble(originalAmountStr);
double refundAmount = Double.parseDouble(refundAmountStr);
if (originalAmount <= 0) {
kPayViewModel.invalidAmountMsg.setValue("Enter valid original amount");
return;
}
if (refundAmount <= 0) {
kPayViewModel.invalidAmountMsg.setValue("Enter valid refund amount");
return;
}
if (refundAmount > originalAmount) {
kPayViewModel.invalidAmountMsg.setValue("Refund amount cannot exceed original amount");
return;
}
QRRefund qrRefund = new QRRefund(referenceNo, refundAmountStr, originalAmountStr, refundReason);
kPayViewModel.setQrRefund(qrRefund);
} else {
QRRefund qrRefund = new QRRefund(referenceNo, "0", etOriginalAmount.getText().toString().trim(), refundReason);
kPayViewModel.setQrRefund(qrRefund);
}
sharedViewModel.transactionsType.setValue(TransactionsType.MMQR_REFUND);
kPayViewModel.setPayDetail(payDetail);
routeId = R.id.action_qrRefundDetail_inputPasswordFragment;
safeNavigateToRouteId();
}
}
}

View File

@ -0,0 +1,142 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.ArrayList;
import java.util.List;
public class QRRefundFragment extends DataBindingFragment {
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
private static final int currentId = R.id.qrRefundList;
private QRPayViewModel qrPayViewModel;
private SharedViewModel sharedViewModel;
private QRRefundViewAdapter adapter;
private LinearLayout emptyStateView;
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {return hostId;}
@Override
protected int routeId() {return routeId;}
@Override
protected void initViewModel() {
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
qrPayViewModel = new ViewModelProvider(requireActivity()).get(QRPayViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
adapter = new QRRefundViewAdapter(this::onRefundItemClicked);
return new DataBindingConfig(R.layout.fragment_qr_refund, BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.adapter, adapter)
.addBindingParam(BR.click, new ClickEvent());
}
@Override
public void onResume(){
super.onResume();
setToolBarTitleWithBackIcon("QR Refund");
}
@Override
public void onCreate(@Nullable Bundle savedInstance){
super.onCreate(savedInstance);
adapter = new QRRefundViewAdapter(this::onRefundItemClicked);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
RecyclerView recyclerView = view.findViewById(R.id.qrRefundHistory);
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
recyclerView.setAdapter(adapter);
emptyStateView = view.findViewById(R.id.emptyStateSectionRefund);
observeData();
}
private void observeData(){
qrPayViewModel.getRefundableQrHistory().observe(getViewLifecycleOwner(), this::updateList);
}
private void updateList(List<PayDetail> list){
if (adapter == null) {
return;
}
if(list == null || list.isEmpty()){
emptyStateView.setVisibility(View.VISIBLE);
getView().findViewById(R.id.qrRefundHistory).setVisibility(View.GONE);
}else{
emptyStateView.setVisibility(View.GONE);
getView().findViewById(R.id.qrRefundHistory).setVisibility(View.VISIBLE);
adapter.setData(list);
}
}
public class ClickEvent {
public void onCancel(){
safeNavigateToRouteId();
}
public void onSearch(){
searchById();
}
}
private void onRefundItemClicked(PayDetail payDetail) {
Bundle bundle = new Bundle();
bundle.putSerializable("payDetail", payDetail);
routeId = R.id.action_qrRefundList_to_qrRefundDetail;
NavHostFragment.findNavController(this).navigate(routeId, bundle);
}
private void searchById(){
EditText editText = getView().findViewById(R.id.rnn_trace_id);
String keyword = editText.getText().toString().trim();
if(keyword.isEmpty()){
updateList(qrPayViewModel.getRefundableQrHistory().getValue());
return;
}
List<PayDetail> originalList = qrPayViewModel.getRefundableQrHistory().getValue();
if(originalList == null) return;
List<PayDetail> filteredList = new ArrayList<>();
for(PayDetail item: originalList){
if(item.getReferNo() != null && item.getReferNo().contains(keyword)){
filteredList.add(item);
}
}
updateList(filteredList);
}
}

View File

@ -0,0 +1,215 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
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.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.KPayRefund;
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 io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class QRRefundProcessFragment extends DataBindingFragment {
private static final String TAG = QRRefundProcessFragment.class.getSimpleName();
private SharedViewModel sharedViewModel;
private KPayViewModel kPayViewModel;
private PayDetail payDetail;
private QRRefund qrRefund;
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
private static final int currentId = R.id.QRRefundProcessFragment;
CompositeDisposable retrieveUpdateDisposable = new CompositeDisposable();
CompositeDisposable refundDisposable = new CompositeDisposable();
@Override
protected void initViewModel() {
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
kPayViewModel = getFragmentScopeViewModel(KPayViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_qr_refund_process, BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.kPayViewModel, kPayViewModel);
}
@Override
public void onDestroyView() {
super.onDestroyView();
retrieveUpdateDisposable.dispose();
refundDisposable.dispose();
}
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {
return hostId;
}
@Override
protected int routeId() {
return routeId;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initData();
processKPayRefund(qrRefund.getReferenceNo(), qrRefund.getRefundAmount(), qrRefund.getOriginalAmount(), qrRefund.getReason());
}
private void initData() {
payDetail = kPayViewModel.getPayDetail();
qrRefund = kPayViewModel.getQrRefund();
}
private void processKPayRefund(String referenceNo, String refundAmount, String originalAmount, String reason) {
String merchantId = TransactionUtil.getInstance().getQRMerchantId();
// Generate unique refund request ID
String refundRequestId = referenceNo + "R";
// Create KPay refund request
KPayRefund.RefundRequest refundRequest = kPayViewModel.createRefundRequest(
refundRequestId,
referenceNo,
merchantId,
refundAmount,
reason != null ? reason : "Refund request"
);
Disposable refundDi = kPayViewModel.kPayRefund(refundRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
handleRefundResponse(response, referenceNo);
},
throwable -> {
LogUtil.e(TAG, "Refund error: " + throwable.getMessage());
showDeclineDialog("Refund failed!\nCommunication Error!");
ecrActionCancel("Refund failed");
navigateToMain();
},
() -> LogUtil.d(TAG, "Refund request completed")
);
refundDisposable.add(refundDi);
}
private void handleRefundResponse(KPayRefund.RefundResponse response, String referenceNo) {
if (response != null && response.getResponse() != null && "REFUND_SUCCESS".equalsIgnoreCase(response.getResponse().getRefundStatus())) {
LogUtil.d(TAG, "Refund successful!");
String refundAmount = response.getResponse().getRefundAmount();
String dateTime = SystemDateTime.getTodayDateFormat() + " " + SystemDateTime.getTodayTimeFormat();
payDetail.setAmount(refundAmount == null ? 0 : POSUtil.getInstance().convertAmount(refundAmount));
payDetail.setOriginalTransDate(dateTime);
payDetail.setQrTransStatus(1);
payDetail.setQrReferNo(referenceNo);
payDetail.setReferNo(referenceNo);
payDetail.setIsCanceled(true);
retrievedUpdatePayDetail(referenceNo);
} else {
LogUtil.d(TAG, "Refund failed!");
payDetail.setQrTransStatus(-1);
payDetail.setQrReferNo(referenceNo);
payDetail.setReferNo(referenceNo);
String errorMsg = "Refund failed";
if (response != null && response.getResponse() != null && response.getResponse().getMsg() != null) {
errorMsg = response.getResponse().getMsg();
}
payDetail.setTradeResultDes(errorMsg);
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
}
}
private void retrievedUpdatePayDetail(String refNum) {
LogUtil.d(TAG, "Trying to update Database!");
retrieveUpdateDisposable.add(kPayViewModel.searchPayByRefNum(refNum)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(oldPay -> {
LogUtil.d(TAG, "Inside the subscribe!");
if (oldPay != null) {
oldPay.setIsCanceled(true);
payDetail.setQrTransId(oldPay.getQrTransId());
payDetail.setCustomerMobile(oldPay.getCustomerMobile());
sharedViewModel.updatePayDetail(oldPay);
}
updateData();
navigateToNext();
},
onError -> {
LogUtil.d(TAG, "On error Unable to retrieve PayDetail");
updateData();
navigateToNext();
},
() -> {
LogUtil.d(TAG, "No data found! navigating to Result Page!");
updateData();
navigateToNext();
}
));
}
private void updateData() {
kPayViewModel.insertPayDetail(payDetail);
sharedViewModel.payDetail.postValue(payDetail);
}
private void navigateToNext() {
routeId = R.id.action_QRRefundProcessFragment_to_transactionResultFragment;
safeNavigateToRouteId();
}
private void navigateToMain() {
routeId = R.id.action_QRRefundProcessFragment_to_nav_main;
safeNavigateToRouteId();
}
private void ecrActionCancel(String msg) {
if (sharedViewModel.isEcr.getValue() != null) {
if (sharedViewModel.isEcr.getValue()) {
sharedViewModel.isEcr.postValue(false);
CoreUtils.getInstance(sharedViewModel).responseRejectMsg(msg);
sharedViewModel.isEcrFinished.postValue(true);
}
}
}
}

View File

@ -0,0 +1,84 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemQrRefundBinding;
import com.utsmm.kbz.ui.adapters.QRPayAdapter;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.ArrayList;
import java.util.List;
public class QRRefundViewAdapter extends RecyclerView.Adapter<QRRefundViewAdapter.ViewHolder> {
private List<PayDetail> payDetailList = new ArrayList<>();
private final OnRefundItemClick listener;
public interface OnRefundItemClick {
void onClick(PayDetail payDetail);
}
public QRRefundViewAdapter(OnRefundItemClick listener){
this.listener = listener;
}
// public QRRefundViewAdapter(List<PayDetail> payDetailList, OnRefundItemClick listener){
// this.payDetailList = payDetailList;
// this.listener = listener;
// }
public void setData(List<PayDetail> data){
this.payDetailList = data != null ? data : new ArrayList<>();
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemQrRefundBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_qr_refund,
parent,
false
);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
PayDetail payDetail = payDetailList.get(position);
holder.bind(payDetail, listener);
}
@Override
public int getItemCount() {
return payDetailList == null ? 0 : payDetailList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final ItemQrRefundBinding binding;
public ViewHolder(ItemQrRefundBinding binding){
super(binding.getRoot());
this.binding = binding;
}
public void bind(PayDetail payDetail, OnRefundItemClick listener){
binding.setVariable(BR.payDetail, payDetail);
binding.executePendingBindings();
binding.getRoot().setOnClickListener( v -> {
if(listener != null) listener.onClick(payDetail);
});
}
}
}

View File

@ -1,6 +1,7 @@
package com.utsmm.kbz.util; package com.utsmm.kbz.util;
import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2; import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
import com.utsmyanmar.checkxread.model.CardDataX;
import com.utsmyanmar.paylibs.model.CardInfo; import com.utsmyanmar.paylibs.model.CardInfo;
import com.utsmyanmar.paylibs.model.ICCardInfo; import com.utsmyanmar.paylibs.model.ICCardInfo;
import com.utsmyanmar.paylibs.model.MAGCardInfo; import com.utsmyanmar.paylibs.model.MAGCardInfo;
@ -42,15 +43,42 @@ public class MockData {
// .cardScheme("VISA") // .cardScheme("VISA")
// .cardHolderName("U AYE") // .cardHolderName("U AYE")
// .iccData("5F21BLAHBLAH") // .iccData("5F21BLAHBLAH")
// .build();
// mockCardData = new MockCardData.Builder()
// .cardNo("9503051034047056")
// .expDate("0230")
// .cardScheme("MPU")
// .cardHolderName("KBZ Debit")
// .iccData("9503051034047056=30021015930000000000")
// .build();
// mockCardData = new MockCardData.Builder()
// .cardNo("9505050161133125")
// .expDate("0127")
// .cardScheme("MPU")
// .cardHolderName("KBZ Credit")
// .iccData("9505050161133125=27011017250000000000")
// .build(); // .build();
mockCardData = new MockCardData.Builder() mockCardData = new MockCardData.Builder()
.cardNo("9503190006079422") .cardNo("9503712156912514")
.expDate("0725") .expDate("1229")
.cardScheme("MPU") .cardScheme("MPU")
.cardHolderName("YOMA VALUED CUSTOMER") .cardHolderName("Htin Kyaw Win")
.iccData("9503712156912514=29121010000000000000")
.build(); .build();
// mockCardData = new MockCardData.Builder()
// .cardNo("9503742975107251")
// .expDate("0629")
// .cardScheme("MPU")
// .cardHolderName("Bank Q")
// .iccData("9503742975107251=22081010000000000000")
// .build();
// mockCardData = new MockCardData.Builder() // mockCardData = new MockCardData.Builder()
// .phoneNo("9794452506") // .phoneNo("9794452506")
// .expDate("0425") // .expDate("0425")
@ -74,6 +102,15 @@ public class MockData {
return String.valueOf(randomNumber); return String.valueOf(randomNumber);
} }
public CardDataX generateMPUCard() {
CardDataX cardDataX = new CardDataX();
cardDataX.setPan(mockCardData.getCardNo());
cardDataX.setExp(mockCardData.getExpDate());
cardDataX.setCardHolderName(mockCardData.getCardHolderName());
cardDataX.setTrack2(mockCardData.getIccData());
return cardDataX;
}
public TradeData generateMockDataWithTime(TransactionsType transType,int cardInputType,String transDate,String transTime) { public TradeData generateMockDataWithTime(TransactionsType transType,int cardInputType,String transDate,String transTime) {
String strDate = SystemDateTime.getMMDD(); String strDate = SystemDateTime.getMMDD();
String strTime = SystemDateTime.getHHmmss(); String strTime = SystemDateTime.getHHmmss();

View File

@ -10,6 +10,7 @@ import com.utsmyanmar.baselib.network.model.sirius.SiriusMerchant;
import com.utsmyanmar.baselib.network.model.sirius.SiriusProperty; import com.utsmyanmar.baselib.network.model.sirius.SiriusProperty;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse; import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation; import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType; import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
import com.utsmm.kbz.BuildConfig; import com.utsmm.kbz.BuildConfig;
@ -170,7 +171,7 @@ public class TMSSetupsImpl implements TMSSetups{
for (SiriusHost siriusHost: siriusHosts) { for (SiriusHost siriusHost: siriusHosts) {
if( siriusHost.getName().toLowerCase().contains("mmqr") || siriusHost.getName().toLowerCase().contains("kbzpay") || siriusHost.getDescription().toLowerCase().contains("mmqr") || siriusHost.getDescription().toLowerCase().contains("qr")) { if( siriusHost.getName().toLowerCase().contains("mmqr") || siriusHost.getName().toLowerCase().contains("kbzpay") || siriusHost.getName().toLowerCase().contains("mmqr") || siriusHost.getName().toLowerCase().contains("qr")) {
SystemParamsOperation.getInstance().setSecHostName(siriusHost.getName()); SystemParamsOperation.getInstance().setSecHostName(siriusHost.getName());
SystemParamsOperation.getInstance().setSecHostTerminalId(siriusHost.getTid()); SystemParamsOperation.getInstance().setSecHostTerminalId(siriusHost.getTid());
@ -412,15 +413,18 @@ public class TMSSetupsImpl implements TMSSetups{
} else if (TextUtils.equals(name,"ssl_enable")) { } else if (TextUtils.equals(name,"ssl_enable")) {
SystemParamsOperation.getInstance().setSslSwitchStatus(parseBoolean(data)); SystemParamsOperation.getInstance().setSslSwitchStatus(parseBoolean(data));
} else if (TextUtils.equals(name,"wave_pay_inquiry_status_enable")) { } else if (TextUtils.equals(name,"qrpay_inquiry_status_enable")) {
SystemParamsOperation.getInstance().setWavePayInquiryStatus(parseBoolean(data)); SystemParamsOperation.getInstance().setQRPayInquiryStatus(parseBoolean(data));
} else if (TextUtils.equals(name,"tips_adjustment_enable")) { } else if (TextUtils.equals(name,"tips_adjustment_enable")) {
SystemParamsOperation.getInstance().setTipsAdjustmentStatus(parseBoolean(data)); SystemParamsOperation.getInstance().setTipsAdjustmentStatus(parseBoolean(data));
} else if (TextUtils.equals(name,"wave_enable")) { } else if (TextUtils.equals(name,"qrpay_enable")) {
SystemParamsOperation.getInstance().setWavePayStatus(parseBoolean(data)); SystemParamsOperation.getInstance().setQRPayStatus(parseBoolean(data));
} else if(TextUtils.equals(name, "mmqr_interval_waiting_time")){
SystemParamsOperation.getInstance().setWaveIntervalTime(data);
} else if (TextUtils.equals(name,"print_iso_enable")) { } else if (TextUtils.equals(name,"print_iso_enable")) {
SystemParamsOperation.getInstance().setPrintISOStatus(parseBoolean(data)); SystemParamsOperation.getInstance().setPrintISOStatus(parseBoolean(data));
@ -582,11 +586,14 @@ public class TMSSetupsImpl implements TMSSetups{
} }
} else if (TextUtils.equals(name,"speedup_contactless_enable")) { } else if (TextUtils.equals(name,"speedup_contactless_enable")) {
SystemParamsOperation.getInstance().setSpeedUpContactless(parseBoolean(data)); SystemParamsOperation.getInstance().setSpeedUpContactless(parseBoolean(data));
} } else if (TextUtils.equals(name,"manual_entry_pin_enable")) {
else if (TextUtils.equals(name,"manual_entry_pin_enable")) {
SystemParamsOperation.getInstance().setManualEntryPinEnable(parseBoolean(data)); SystemParamsOperation.getInstance().setManualEntryPinEnable(parseBoolean(data));
} else if (TextUtils.equals(name,"decimal_enable")) { } else if (TextUtils.equals(name,"decimal_enable")) {
SystemParamsOperation.getInstance().setDecimalEnable(parseBoolean(data)); SystemParamsOperation.getInstance().setDecimalEnable(parseBoolean(data));
} else if (TextUtils.equals(name,"app_key")) {
SystemParamsOperation.getInstance().setAppKey(data);
} else if (TextUtils.equals(name,"app_id")) {
SystemParamsOperation.getInstance().setAppId(data);
} }
} }
@ -684,7 +691,7 @@ public class TMSSetupsImpl implements TMSSetups{
return value == 1; return value == 1;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return false; return Boolean.parseBoolean(data);
} }
} }
} }

View File

@ -147,7 +147,8 @@ public class TMSUtil {
siriusRequest.setApplicationVersion(BuildConfig.VERSION_NAME); siriusRequest.setApplicationVersion(BuildConfig.VERSION_NAME);
siriusRequest.setCurrentNetwork(getNetworkType(context)); siriusRequest.setCurrentNetwork(getNetworkType(context));
siriusRequest.setLastTransaction(lastTransName); siriusRequest.setLastTransaction(lastTransName);
siriusRequest.setLastTranTime(lastTransTime); siriusRequest.setLastTranTime(Long.parseLong(lastTransTime));
siriusRequest.setValue("YourValueHere");
return siriusRequest; return siriusRequest;
} }
@ -206,7 +207,7 @@ public class TMSUtil {
voidStatus = SystemParamsOperation.getInstance().getVoidStatus(); voidStatus = SystemParamsOperation.getInstance().getVoidStatus();
cashAdvanceStatus = SystemParamsOperation.getInstance().getCashAdvanceStatus(); cashAdvanceStatus = SystemParamsOperation.getInstance().getCashAdvanceStatus();
refundStatus = SystemParamsOperation.getInstance().getRefundStatus(); refundStatus = SystemParamsOperation.getInstance().getRefundStatus();
wavePayInquiryStatus = SystemParamsOperation.getInstance().getWavePayInquiryStatus(); wavePayInquiryStatus = SystemParamsOperation.getInstance().getQRPayInquiryStatus();
tipAdjustmentStatus = SystemParamsOperation.getInstance().getTipsAdjustmentStatus(); tipAdjustmentStatus = SystemParamsOperation.getInstance().getTipsAdjustmentStatus();
settlementStatus = SystemParamsOperation.getInstance().getSettlementStatus(); settlementStatus = SystemParamsOperation.getInstance().getSettlementStatus();

View File

@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="200"
android:viewportHeight="200">
<path
android:fillColor="@color/colorPrimary"
android:strokeColor="@color/colorPrimary"
android:strokeWidth="2"
android:pathData="
M 24 0
H 140
A 60 60 0 0 0 200 60
V 176
Q 200 200 176 200
H 24
Q 0 200 0 176
V 24
Q 0 0 24 0
Z" />
</vector>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="200"
android:viewportHeight="200">
<path
android:fillColor="@color/colorPrimary"
android:strokeColor="@color/colorPrimary"
android:strokeWidth="2"
android:pathData="
M 60 0
H 176
Q 200 0 200 24
V 176
Q 200 200 176 200
H 24
Q 0 200 0 176
V 60
A 60 60 0 0 1 60 0
Z" />
</vector>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="200"
android:viewportHeight="200">
<path
android:fillColor="@color/colorPrimary"
android:strokeColor="@color/colorPrimary"
android:strokeWidth="2"
android:pathData="
M 24 0
H 176
Q 200 0 200 24
V 176
Q 200 200 176 200
H 60
A 60 60 0 0 0 0 140
V 24
Q 0 0 24 0
Z" />
</vector>

View File

@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="200"
android:viewportHeight="200">
<path
android:fillColor="@color/colorPrimary"
android:strokeColor="@color/colorPrimary"
android:strokeWidth="2"
android:pathData="
M 24 0
H 176
Q 200 0 200 24
V 140
A 60 60 0 0 0 140 200
H 24
Q 0 200 0 176
V 24
Q 0 0 24 0
Z" />
</vector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/colorPrimary"
android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6z" />
</vector>

View File

@ -149,6 +149,7 @@
android:layout_weight="1" android:layout_weight="1"
app:cardCornerRadius="12dp" app:cardCornerRadius="12dp"
app:cardElevation="2dp" app:cardElevation="2dp"
android:onClick="@{()->click.onClickTap()}"
android:backgroundTint="#fff" android:backgroundTint="#fff"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
android:focusable="true" android:focusable="true"

View File

@ -8,326 +8,283 @@
<import type="android.view.View"/> <import type="android.view.View"/>
<import type="com.utsmm.kbz.R"/> <import type="com.utsmm.kbz.R"/>
<import type="com.utsmm.kbz.util.layout.LayoutDataUtil"/> <import type="com.utsmm.kbz.util.layout.LayoutDataUtil"/>
<variable
name="shareViewModel" <variable name="shareViewModel" type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel"/>
type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel" /> <variable name="mainViewModel" type="com.utsmm.kbz.MainViewModel"/>
<variable <variable name="click" type="com.utsmm.kbz.MainFragment.ClickEvent"/>
name="mainViewModel" <variable name="carouselAdapter" type="androidx.recyclerview.widget.RecyclerView.Adapter"/>
type="com.utsmm.kbz.MainViewModel" /> <variable name="myAdapter" type="com.utsmm.kbz.ui.adapters.MainAdapter"/>
<variable
name="click"
type="com.utsmm.kbz.MainFragment.ClickEvent" />
<variable
name="carouselAdapter"
type="androidx.recyclerview.widget.RecyclerView.Adapter" />
<variable
name="myAdapter"
type="com.utsmm.kbz.ui.adapters.MainAdapter" />
</data> </data>
<!-- ======== ROOT ======== -->
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/colorBackground"> android:background="@color/colorPrimary"
android:padding="16dp">
<!-- ====================== BANNER ====================== -->
<!-- Banner Section (25% of screen) -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/bannerCard" android:id="@+id/bannerCard"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_margin="16dp" android:layout_margin="4dp"
app:cardElevation="6dp"
app:cardCornerRadius="16dp" app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.25"> app:layout_constraintDimensionRatio="16:6">
<RelativeLayout <com.denzcoskun.imageslider.ImageSlider
android:id="@+id/image_slider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
app:iss_auto_cycle="true"
<com.denzcoskun.imageslider.ImageSlider app:iss_period="5000"
android:id="@+id/image_slider" app:iss_delay="5000"
android:layout_width="match_parent" app:iss_no_dots="true"
android:layout_height="match_parent" app:iss_title_background="@color/transparent" />
app:iss_auto_cycle="true"
app:iss_period="5000"
app:iss_delay="5000"
app:iss_no_dots="true"
app:iss_title_background="@color/transparent" />
<!-- Gradient overlay for better text visibility -->
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_overlay" />
</RelativeLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<!-- Main Menu Grid (3+2 layout for 5 functions) -->
<!-- ====================== GRID AREA ====================== -->
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/menuGrid"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_margin="16dp" app:layout_constraintTop_toBottomOf="@id/bannerCard"
app:layout_constraintTop_toBottomOf="@+id/bannerCard" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"> android:paddingTop="12dp">
<!-- First Row - 3 cards -->
<!-- Sale Card --> <!-- ===== FULL WIDTH SALE BUTTON ===== -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/cardMenuCard" android:id="@+id/cardMenuCard"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginEnd="4dp" app:cardCornerRadius="18dp"
android:layout_marginBottom="8dp" app:cardElevation="4dp"
app:cardCornerRadius="16dp" android:layout_margin="6dp"
app:cardElevation="6dp" android:foreground="?attr/selectableItemBackground"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickCard()}" android:onClick="@{() -> click.onClickCard()}"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/signOnMenuCard" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/qrPayMenuCard" app:layout_constraintDimensionRatio="1:0.4">
app:layout_constraintWidth_percent="0.32"
app:layout_constraintHeight_percent="0.48">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center" android:gravity="center"
android:background="@drawable/primary_card_bg" android:padding="20dp"
android:padding="12dp"> android:background="@color/white">
<ImageView <ImageView
android:layout_width="40dp" android:layout_width="56dp"
android:layout_height="40dp" android:layout_height="56dp"
android:src="@drawable/ic_sale" android:src="@drawable/ic_sale"
android:layout_marginBottom="6dp" app:tint="@color/colorPrimary"
app:tint="@color/white" /> android:layout_marginBottom="6dp" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/menu_sale" android:text="@string/menu_sale"
android:textColor="@color/white" android:textColor="@color/colorPrimary"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold"
android:fontFamily="@font/rubik_medium" android:fontFamily="@font/rubik_medium"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<!-- Sign On Card -->
<androidx.cardview.widget.CardView
android:id="@+id/signOnMenuCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickSignOn()}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/cardMenuCard"
app:layout_constraintEnd_toStartOf="@+id/settlementMenuCard"
app:layout_constraintBottom_toTopOf="@+id/otherFeaturesCard"
app:layout_constraintWidth_percent="0.32"
app:layout_constraintHeight_percent="0.48">
<LinearLayout <!-- ===== ROW 1 ===== -->
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="12dp">
<ImageView <!-- Settlement -->
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_signon"
android:layout_marginBottom="6dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_sign_on"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Settlement Card -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/settlementMenuCard" android:id="@+id/settlementMenuCard"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="4dp" app:cardCornerRadius="18dp"
android:layout_marginBottom="8dp" app:cardElevation="4dp"
app:cardCornerRadius="16dp" android:layout_margin="6dp"
app:cardElevation="6dp" android:foreground="?attr/selectableItemBackground"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickSettlement()}" android:onClick="@{() -> click.onClickSettlement()}"
app:disableBtn="@{mainViewModel.settlementStatus}" app:disableBtn="@{mainViewModel.settlementStatus}"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@id/cardMenuCard"
app:layout_constraintStart_toEndOf="@+id/signOnMenuCard" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/signOnMenuCard"
app:layout_constraintBottom_toTopOf="@+id/otherFeaturesCard" app:layout_constraintDimensionRatio="1:0.8">
app:layout_constraintWidth_percent="0.32"
app:layout_constraintHeight_percent="0.48">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:background="@color/white"
android:gravity="center" android:gravity="center"
android:background="@drawable/primary_card_bg" android:orientation="vertical"
android:padding="12dp"> android:padding="18dp">
<ImageView <ImageView
android:layout_width="40dp" android:layout_width="46dp"
android:layout_height="40dp" android:layout_height="46dp"
android:src="@drawable/ic_settlement"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
app:tint="@color/white" /> android:src="@drawable/ic_settlement"
app:tint="@color/colorPrimary" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/menu_settlement" android:text="@string/menu_settlement"
android:textColor="@color/white" android:textColor="@color/colorPrimary"
android:textSize="14sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold"
android:fontFamily="@font/rubik_medium" android:fontFamily="@font/rubik_medium"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<!-- Second Row - 2 cards centered -->
<!-- QR Pay Card --> <!-- Sign On -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/qrPayMenuCard" android:id="@+id/signOnMenuCard"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginEnd="8dp" app:cardCornerRadius="18dp"
android:layout_marginTop="8dp" app:cardElevation="4dp"
app:cardCornerRadius="16dp" android:layout_margin="6dp"
app:cardElevation="6dp" android:foreground="?attr/selectableItemBackground"
android:foreground="?android:attr/selectableItemBackground" android:onClick="@{() -> click.onClickSignOn()}"
android:clickable="true" app:layout_constraintTop_toBottomOf="@id/cardMenuCard"
android:focusable="true" app:layout_constraintStart_toEndOf="@id/settlementMenuCard"
android:onClick="@{() -> click.onClickQRPay()}" app:layout_constraintEnd_toEndOf="parent"
app:disableBtn="@{mainViewModel.kPayStatus}" app:layout_constraintDimensionRatio="1:0.8">
app:layout_constraintTop_toBottomOf="@+id/cardMenuCard"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/otherFeaturesCard"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent="0.48"
app:layout_constraintHeight_percent="0.48">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:background="@color/white"
android:gravity="center" android:gravity="center"
android:background="@drawable/primary_card_bg" android:orientation="vertical"
android:padding="16dp"> android:padding="18dp">
<ImageView <ImageView
android:layout_width="48dp" android:layout_width="46dp"
android:layout_height="48dp" android:layout_height="46dp"
android:src="@drawable/ic_qr_pay" android:src="@drawable/ic_signon"
android:layout_marginBottom="8dp" app:tint="@color/colorPrimary"
app:tint="@color/white" /> android:layout_marginBottom="6dp" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/menu_qrpay" android:text="@string/menu_sign_on"
android:textColor="@color/white" android:textColor="@color/colorPrimary"
android:textSize="16sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold"
android:fontFamily="@font/rubik_medium" android:fontFamily="@font/rubik_medium"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<!-- Transactions Card -->
<!-- ===== ROW 2 ===== -->
<!-- Transactions -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/otherFeaturesCard" android:id="@+id/otherFeaturesCard"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="8dp" app:cardCornerRadius="18dp"
android:layout_marginTop="8dp" app:cardElevation="4dp"
app:cardCornerRadius="16dp" android:layout_margin="6dp"
app:cardElevation="6dp" android:foreground="?attr/selectableItemBackground"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickTrans()}" android:onClick="@{() -> click.onClickTrans()}"
app:layout_constraintTop_toBottomOf="@+id/signOnMenuCard" app:layout_constraintTop_toBottomOf="@id/settlementMenuCard"
app:layout_constraintStart_toEndOf="@+id/qrPayMenuCard" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@id/qrPayMenuCard"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="1:0.8">
app:layout_constraintWidth_percent="0.48"
app:layout_constraintHeight_percent="0.48">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center" android:gravity="center"
android:background="@drawable/primary_card_bg" android:background="@color/white"
android:padding="16dp"> android:orientation="vertical"
android:padding="18dp">
<ImageView <ImageView
android:layout_width="48dp" android:layout_width="46dp"
android:layout_height="48dp" android:layout_height="46dp"
android:src="@drawable/ic_other_features" android:src="@drawable/ic_other_features"
android:layout_marginBottom="8dp" app:tint="@color/colorPrimary"
app:tint="@color/white" /> android:layout_marginBottom="6dp" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/menu_transactions" android:text="@string/menu_transactions"
android:textColor="@color/white" android:textColor="@color/colorPrimary"
android:textSize="16sp" android:textSize="14sp"
android:textStyle="bold" android:textStyle="bold"
android:fontFamily="@font/rubik_medium" android:fontFamily="@font/rubik_medium"
android:textAlignment="center" /> android:textAlignment="center" />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView>
<!-- QR Pay -->
<androidx.cardview.widget.CardView
android:id="@+id/qrPayMenuCard"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="18dp"
app:cardElevation="4dp"
android:layout_margin="6dp"
android:foreground="?attr/selectableItemBackground"
android:onClick="@{() -> click.onClickQR()}"
app:disableBtn="@{mainViewModel.kPayStatus}"
app:layout_constraintTop_toBottomOf="@id/signOnMenuCard"
app:layout_constraintStart_toEndOf="@id/otherFeaturesCard"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="1:0.8">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="center"
android:orientation="vertical"
android:padding="18dp">
<ImageView
android:layout_width="46dp"
android:layout_height="46dp"
android:src="@drawable/ic_qr_pay"
app:tint="@color/colorPrimary"
android:layout_marginBottom="6dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_qrpay"
android:textColor="@color/colorPrimary"
android:textSize="14sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
</layout>

View File

@ -0,0 +1,333 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.utsmm.kbz.util.ecr.ECRConnectionStatus"/>
<import type="android.view.View"/>
<import type="com.utsmm.kbz.R"/>
<import type="com.utsmm.kbz.util.layout.LayoutDataUtil"/>
<variable
name="shareViewModel"
type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel" />
<variable
name="mainViewModel"
type="com.utsmm.kbz.MainViewModel" />
<variable
name="click"
type="com.utsmm.kbz.MainFragment.ClickEvent" />
<variable
name="carouselAdapter"
type="androidx.recyclerview.widget.RecyclerView.Adapter" />
<variable
name="myAdapter"
type="com.utsmm.kbz.ui.adapters.MainAdapter" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground">
<!-- Banner Section (25% of screen) -->
<androidx.cardview.widget.CardView
android:id="@+id/bannerCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.25">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.denzcoskun.imageslider.ImageSlider
android:id="@+id/image_slider"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:iss_auto_cycle="true"
app:iss_period="5000"
app:iss_delay="5000"
app:iss_no_dots="true"
app:iss_title_background="@color/transparent" />
<!-- Gradient overlay for better text visibility -->
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
<!-- Main Menu Grid (3+2 layout for 5 functions) -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
app:layout_constraintTop_toBottomOf="@+id/bannerCard"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<!-- First Row - 3 cards -->
<!-- Sale Card -->
<androidx.cardview.widget.CardView
android:id="@+id/cardMenuCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickCard()}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/signOnMenuCard"
app:layout_constraintBottom_toTopOf="@+id/qrPayMenuCard"
app:layout_constraintWidth_percent="0.32"
app:layout_constraintHeight_percent="0.48">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="12dp">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_sale"
android:layout_marginBottom="6dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_sale"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Sign On Card -->
<androidx.cardview.widget.CardView
android:id="@+id/signOnMenuCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickSignOn()}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/cardMenuCard"
app:layout_constraintEnd_toStartOf="@+id/settlementMenuCard"
app:layout_constraintBottom_toTopOf="@+id/otherFeaturesCard"
app:layout_constraintWidth_percent="0.32"
app:layout_constraintHeight_percent="0.48">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="12dp">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_signon"
android:layout_marginBottom="6dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_sign_on"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Settlement Card -->
<androidx.cardview.widget.CardView
android:id="@+id/settlementMenuCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="4dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickSettlement()}"
app:disableBtn="@{mainViewModel.settlementStatus}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/signOnMenuCard"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/otherFeaturesCard"
app:layout_constraintWidth_percent="0.32"
app:layout_constraintHeight_percent="0.48">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="12dp">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_settlement"
android:layout_marginBottom="6dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_settlement"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Second Row - 2 cards centered -->
<!-- QR Pay Card -->
<androidx.cardview.widget.CardView
android:id="@+id/qrPayMenuCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickQR()}"
app:disableBtn="@{mainViewModel.kPayStatus}"
app:layout_constraintTop_toBottomOf="@+id/cardMenuCard"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/otherFeaturesCard"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent="0.48"
app:layout_constraintHeight_percent="0.48">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="16dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_qr_pay"
android:layout_marginBottom="8dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_qrpay"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Transactions Card -->
<androidx.cardview.widget.CardView
android:id="@+id/otherFeaturesCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:onClick="@{() -> click.onClickTrans()}"
app:layout_constraintTop_toBottomOf="@+id/signOnMenuCard"
app:layout_constraintStart_toEndOf="@+id/qrPayMenuCard"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent="0.48"
app:layout_constraintHeight_percent="0.48">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="16dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_other_features"
android:layout_marginBottom="8dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_transactions"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="@font/rubik_medium"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -41,7 +41,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:spanCount="2" app:spanCount="3"
bind:setAdapter="@{adapter}" bind:setAdapter="@{adapter}"
tools:itemCount="6" tools:itemCount="6"
tools:listitem="@layout/item_view_dashboard" /> tools:listitem="@layout/item_view_dashboard" />

View File

@ -32,21 +32,21 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp" android:padding="10dp"
android:paddingTop="24dp" android:paddingTop="14dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<!-- Trace Number Icon --> <!-- Trace Number Icon -->
<ImageView <ImageView
android:layout_width="48dp" android:layout_width="36dp"
android:layout_height="48dp" android:layout_height="36dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginBottom="12dp" android:layout_marginBottom="8dp"
android:src="@drawable/ic_txn_history" android:src="@drawable/ic_txn_history"
app:tint="@color/colorPrimary" app:tint="@color/colorPrimary"
android:alpha="0.9" android:alpha="0.88"
android:padding="8dp" android:padding="6dp"
android:background="@drawable/bg_oval" /> android:background="@drawable/bg_oval" />
<!-- Compact Title --> <!-- Compact Title -->
@ -56,10 +56,10 @@
android:text="@string/enter_trace_text" android:text="@string/enter_trace_text"
android:textAlignment="center" android:textAlignment="center"
android:textColor="@color/colorPrimary" android:textColor="@color/colorPrimary"
android:textSize="22sp" android:textSize="17sp"
android:textStyle="bold" android:textStyle="bold"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:layout_marginBottom="4dp" /> android:layout_marginBottom="2dp" />
<!-- Compact Subtitle --> <!-- Compact Subtitle -->
<TextView <TextView
@ -68,18 +68,18 @@
android:text="Enter the transaction trace number" android:text="Enter the transaction trace number"
android:textAlignment="center" android:textAlignment="center"
android:textColor="@color/colorPrimary" android:textColor="@color/colorPrimary"
android:textSize="14sp" android:textSize="12sp"
android:alpha="0.75" android:alpha="0.7"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:layout_marginBottom="16dp" /> android:layout_marginBottom="10dp" />
<!-- Compact Trace Number Display Card --> <!-- Compact Trace Number Display Card -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/traceCard" android:id="@+id/traceCard"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="70dp" android:layout_height="48dp"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="4dp"
app:cardCornerRadius="16dp" app:cardCornerRadius="12dp"
app:cardElevation="0dp" app:cardElevation="0dp"
app:cardBackgroundColor="@color/white"> app:cardBackgroundColor="@color/white">
@ -87,11 +87,11 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="2dp" android:layout_margin="1dp"
android:background="@drawable/bg_edittext_primary_border" android:background="@drawable/bg_edittext_primary_border"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center" android:gravity="center"
android:padding="16dp"> android:padding="8dp">
<!-- Trace number display --> <!-- Trace number display -->
<TextView <TextView
@ -102,22 +102,22 @@
android:gravity="center" android:gravity="center"
android:text="@{inputTraceViewModel.invoiceNo}" android:text="@{inputTraceViewModel.invoiceNo}"
android:textColor="@color/colorPrimary" android:textColor="@color/colorPrimary"
android:textSize="28sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
android:letterSpacing="0.1" android:letterSpacing="0.07"
android:fontFamily="monospace" android:fontFamily="monospace"
android:hint="000000" android:hint="000000"
android:textColorHint="@color/colorPrimary" android:textColorHint="@color/colorPrimary"
android:alpha="0.6" android:alpha="0.5"
tools:text="123456" /> tools:text="123456" />
<!-- Transaction indicator --> <!-- Transaction indicator -->
<View <View
android:layout_width="6dp" android:layout_width="5dp"
android:layout_height="6dp" android:layout_height="5dp"
android:background="@drawable/bg_oval" android:background="@drawable/bg_oval"
android:backgroundTint="@color/colorPrimary" android:backgroundTint="@color/colorPrimary"
android:alpha="0.6" /> android:alpha="0.5" />
</LinearLayout> </LinearLayout>
@ -125,14 +125,14 @@
</LinearLayout> </LinearLayout>
<!-- Number Keyboard - guaranteed space --> <!-- Number Keyboard - guaranteed space, more compact -->
<com.utsmyanmar.baselib.ui.NumberKeyboard <com.utsmyanmar.baselib.ui.NumberKeyboard
android:id="@+id/passwordKeyboard" android:id="@+id/passwordKeyboard"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="4dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="4dp"
app:KeyClickCallback="@{inputTraceViewModel.onKeyClick}" app:KeyClickCallback="@{inputTraceViewModel.onKeyClick}"
app:layout_constraintBottom_toTopOf="@+id/actionButtonsContainer" app:layout_constraintBottom_toTopOf="@+id/actionButtonsContainer"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -146,8 +146,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:padding="16dp" android:padding="8dp"
android:paddingBottom="24dp" android:paddingBottom="12dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintStart_toStartOf="parent">
@ -155,34 +155,34 @@
<Button <Button
android:id="@+id/btnCancel" android:id="@+id/btnCancel"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="52dp" android:layout_height="42dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginEnd="8dp" android:layout_marginEnd="6dp"
android:text="@string/layout_cancel" android:text="@string/layout_cancel"
android:textColor="@color/colorPrimary" android:textColor="@color/colorPrimary"
android:textSize="15sp" android:textSize="12sp"
android:textStyle="bold" android:textStyle="bold"
android:background="@drawable/bg_rounded_btn_cancel_cv" android:background="@drawable/bg_rounded_btn_cancel_cv"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:onClick="@{()->click.onCancel()}" android:onClick="@{()->click.onCancel()}"
android:elevation="2dp" android:elevation="1dp"
android:textAllCaps="false" /> android:textAllCaps="false" />
<!-- Confirm Button --> <!-- Confirm Button -->
<Button <Button
android:id="@+id/btnConfirm" android:id="@+id/btnConfirm"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="52dp" android:layout_height="42dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_marginStart="8dp" android:layout_marginStart="6dp"
android:text="@string/layout_confirm" android:text="@string/layout_confirm"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="15sp" android:textSize="12sp"
android:textStyle="bold" android:textStyle="bold"
android:background="@drawable/bg_rounded_btn_cv" android:background="@drawable/bg_rounded_btn_cv"
android:fontFamily="sans-serif-medium" android:fontFamily="sans-serif-medium"
android:onClick="@{()->click.onConfirm()}" android:onClick="@{()->click.onConfirm()}"
android:elevation="8dp" android:elevation="3dp"
android:textAllCaps="false" /> android:textAllCaps="false" />
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="vm"
type="com.utsmm.kbz.ui.qr_pay.QRHistoryViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/qrRvHistory"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- No Data Message -->
<!-- <TextView-->
<!-- android:id="@+id/tvNoData"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:gravity="center"-->
<!-- android:text="No history available"-->
<!-- android:textSize="18sp"-->
<!-- android:padding="16dp"-->
<!-- android:visibility="@{vm.refundList.size() == 0 ? View.VISIBLE : View.GONE}" />-->
</FrameLayout>
</layout>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="adapter"
type="androidx.recyclerview.widget.RecyclerView.Adapter" />
<variable
name="sharedViewModel"
type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewFeatures"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:background="@color/transparent"
android:padding="16dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="3"
bind:setAdapter="@{adapter}"
tools:itemCount="3"
tools:listitem="@layout/item_qr_pay_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="sharedViewModel"
type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel" />
<variable
name="adapter"
type="androidx.recyclerview.widget.RecyclerView.Adapter" />
<variable
name="manageViewModel"
type="com.utsmm.kbz.ui.management.ManagementViewModel" />
<variable
name="click"
type="com.utsmm.kbz.ui.qr_pay.QRRefundFragment.ClickEvent" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<!-- SEARCH SECTION -->
<LinearLayout
android:id="@+id/refundSearchSection"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center_vertical"
android:visibility="@{manageViewModel.reprintLayoutTopVisibility}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- Search Card -->
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_weight="1"
android:layout_marginEnd="12dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:cardBackgroundColor="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="2dp"
android:background="@drawable/bg_edittext_primary_border"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_txn_history"
app:tint="@color/colorPrimary"
android:alpha="0.6"/>
<EditText
android:id="@+id/rnn_trace_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:hint="@string/txt_search_rrn_trace"
android:text="@={manageViewModel.txtRRNTrace}"
android:textColor="@color/colorPrimary"
android:textColorHint="@color/colorPrimary"
android:alpha="0.9"
android:textSize="14sp"
android:inputType="number"
android:singleLine="true"
android:imeOptions="actionSearch" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- SEARCH BUTTON -->
<androidx.cardview.widget.CardView
android:id="@+id/btnSearch"
android:onClick="@{() -> click.onSearch()}"
android:layout_width="80dp"
android:layout_height="52dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="8dp"
app:cardElevation="3dp"
android:foreground="?android:attr/selectableItemBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search"
android:textSize="14sp"
android:textColor="@color/white"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
<!-- RECYCLER VIEW -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/qrRefundHistory"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingTop="4dp"
app:adapter="@{adapter}"
app:layout_constraintTop_toBottomOf="@id/refundSearchSection"
app:layout_constraintBottom_toBottomOf="parent"
tools:listitem="@layout/item_qr_refund" />
<!-- Empty State Section -->
<LinearLayout
android:id="@+id/emptyStateSectionRefund"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:visibility="@{manageViewModel.reprintLayoutBtmVisibility}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lav_no_trans"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginBottom="24dp"
app:lottie_autoPlay="true"
app:lottie_fileName="lottie_no_trans.json"
app:lottie_loop="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_history"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/colorPrimary"
android:layout_marginBottom="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No transactions found for the selected criteria"
android:textSize="14sp"
android:textColor="@color/colorPrimary"
android:alpha="0.7"
android:paddingHorizontal="32dp"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,349 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="sharedViewModel"
type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel" />
<variable
name="kPayViewModel"
type="com.utsmm.kbz.ui.kpay.KPayViewModel" />
<variable
name="click"
type="com.utsmm.kbz.ui.qr_pay.QRRefundDetailFragment.ClickEvent" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<!-- Refund Type Selection -->
<LinearLayout
android:id="@+id/refund_type_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="Refund Type"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<RadioGroup
android:id="@+id/radio_group_refund_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/radio_original"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="Original Amount"
android:textSize="16sp" />
<!-- <RadioButton-->
<!-- android:id="@+id/radio_partial"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:text="Partial Amount"-->
<!-- android:textSize="16sp" />-->
</RadioGroup>
</LinearLayout>
<!-- Reference Number Input -->
<LinearLayout
android:id="@+id/reference_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/refund_type_layout">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="Reference Number *"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<EditText
android:id="@+id/et_reference_no"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="Enter reference number"
android:inputType="text"
android:maxLength="20"
android:padding="16dp"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="18sp"
tools:text="NEX000011" />
</androidx.cardview.widget.CardView>
</LinearLayout>
<!-- Original Amount Input (for partial refunds) -->
<LinearLayout
android:id="@+id/original_amount_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reference_layout">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="Original Amount *"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_amount_title"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mmk"
android:textSize="18sp" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<EditText
android:id="@+id/et_original_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="0.00"
android:inputType="numberDecimal"
android:padding="16dp"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
tools:text="1000.00" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
<!-- Refund Amount Input (for partial refunds) -->
<LinearLayout
android:id="@+id/refund_amount_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/original_amount_layout">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="Refund Amount *"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_amount_title"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mmk"
android:textSize="18sp" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<EditText
android:id="@+id/et_refund_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="0.00"
android:inputType="numberDecimal"
android:padding="16dp"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
tools:text="400.00" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
<!-- Refund Reason Input -->
<LinearLayout
android:id="@+id/refund_reason_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/refund_amount_layout">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:text="Refund Reason"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<EditText
android:id="@+id/et_refund_reason"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="Enter refund reason (optional)"
android:inputType="textMultiLine"
android:lines="3"
android:padding="16dp"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="Customer requested refund" />
</androidx.cardview.widget.CardView>
</LinearLayout>
<!-- Error Message -->
<TextView
android:id="@+id/error_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@{kPayViewModel.invalidAmountMsg}"
android:textAlignment="center"
android:textColor="@color/red"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/refund_reason_layout"
tools:text="Invalid amount entered" />
<!-- Action Buttons -->
<LinearLayout
android:id="@+id/button_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/error_message">
<Button
android:id="@+id/btn_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_btn_cancel_cv"
android:onClick="@{()->click.onCancel()}"
android:text="@string/layout_cancel"
android:textColor="@color/colorPrimary"
android:textSize="16sp" />
<Button
android:id="@+id/btn_confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_btn_cv"
android:onClick="@{()->click.onConfirm()}"
android:text="Process Refund"
android:textColor="@color/white"
android:textSize="16sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="sharedViewModel"
type="com.utsmm.kbz.ui.core_viewmodel.SharedViewModel" />
<variable
name="kPayViewModel"
type="com.utsmm.kbz.ui.kpay.KPayViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/rubik_regular"
android:text="@string/txt_processing_refund"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="@+id/lav_thumbUp"
app:layout_constraintStart_toStartOf="@+id/lav_thumbUp"
app:layout_constraintTop_toBottomOf="@+id/lav_thumbUp" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lav_thumbUp"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_margin="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_autoPlay="true"
app:lottie_fileName="lottie_processing_card.json"
app:lottie_loop="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -91,7 +91,7 @@
app:layout_constraintBottom_toTopOf="@+id/actionButtonsContainer"> app:layout_constraintBottom_toTopOf="@+id/actionButtonsContainer">
<!-- Card with border --> <!-- Card with border -->
<LinearLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="2dp" android:layout_margin="2dp"
@ -477,13 +477,18 @@
tools:text="12/12/2024 12:12:12" /> tools:text="12/12/2024 12:12:12" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
</LinearLayout> </FrameLayout>
<ImageView
android:id="@+id/scroll_hint"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="8dp"
android:src="@drawable/ic_down_arrow"
android:alpha="0.6" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<!-- Action Buttons Container --> <!-- Action Buttons Container -->

View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<data>
<import type="com.utsmyanmar.paylibs.utils.POSUtil"/>
<variable
name="payDetail"
type="com.utsmyanmar.paylibs.model.PayDetail" />
<variable
name="onItemClick"
type="com.utsmm.kbz.ui.management.ItemClickListener" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_marginVertical="6dp"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:cardBackgroundColor="@color/colorPrimary"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> onItemClick.onClickCard(payDetail)}">
<!-- Card with border -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:background="@drawable/bg_edittext_primary_border"
android:orientation="horizontal"
android:padding="16dp">
<!-- Transaction Icon -->
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_custom_pos"
app:tint="@color/white"
android:alpha="0.8"
android:layout_marginEnd="12dp"
android:layout_gravity="top" />
<!-- Transaction Details -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<!-- Transaction Type and Amount Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<!-- Transaction Type -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{POSUtil.getInstance().getTransName(payDetail)}"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="sans-serif-medium"
tools:text="SALE" />
<!-- Transaction Amount -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{POSUtil.getInstance().checkMinusSign(payDetail.transactionType)+POSUtil.getInstance().getDecimalAmountSeparatorFormat(payDetail.amount) + ` `+POSUtil.getInstance().currencyCodeToText(payDetail.currencyCode)}"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="sans-serif-medium"
tools:text="1,000.00 MMK" />
</LinearLayout>
<!-- Transaction Date -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{POSUtil.getInstance().convertDateFormatByDateString(payDetail.transDate)}"
android:textColor="@color/white"
android:textSize="14sp"
android:alpha="0.8"
android:fontFamily="sans-serif"
android:layout_marginBottom="6dp"
tools:text="2022-11-26" />
<!-- Card Number -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{POSUtil.getInstance().getCardNumMasking(payDetail.cardNo)}"
android:textColor="@color/white"
android:textSize="14sp"
android:alpha="0.8"
android:fontFamily="monospace"
android:layout_marginBottom="8dp"
tools:text="468785******9564" />
<!-- Trace and RRN Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- Trace Number -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:isTrace="@{payDetail}"
android:textColor="@color/white"
android:textSize="13sp"
android:alpha="0.7"
android:fontFamily="monospace"
tools:text="TRC:000050" />
<!-- RRN -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{`RRN:`+payDetail.referNo}"
android:textColor="@color/white"
android:textSize="13sp"
android:alpha="0.7"
android:fontFamily="monospace"
android:textAlignment="textEnd"
tools:text="RRN:233001786242" />
</LinearLayout>
</LinearLayout>
<!-- Arrow Icon -->
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_right_arrow"
app:tint="@color/white"
android:alpha="0.6"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.utsmm.kbz.R"/>
<import type="com.utsmm.kbz.util.enums.FeaturesType" />
<variable
name="text"
type="String" />
<variable
name="icon"
type="Integer" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
>
<androidx.cardview.widget.CardView
android:id="@+id/btnItem"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="20dp"
app:cardElevation="6dp"
android:layout_margin="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="1:1"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp">
<ImageView
android:id="@+id/btnIcon"
android:layout_width="38dp"
android:layout_height="38dp"
android:layout_marginBottom="8dp"
app:srcUrl="@{icon}"
app:tint="@color/colorPrimary"
tools:src="@drawable/ic_sale"
tools:tint="@color/colorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="@font/rubik_medium"
android:gravity="center"
android:maxLines="2"
android:text="@{text}"
tools:text="button title"
android:textAlignment="center"
android:textColor="@color/colorPrimary"
android:textSize="12sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<data>
<import type="com.utsmyanmar.paylibs.utils.POSUtil"/>
<variable
name="payDetail"
type="com.utsmyanmar.paylibs.model.PayDetail" />
<variable
name="onItemClick"
type="com.utsmm.kbz.ui.qr_pay.QRRefundFragment.ClickEvent" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_margin="8dp"
android:layout_marginVertical="6dp"
app:cardCornerRadius="12dp"
app:cardElevation="0dp"
app:cardBackgroundColor="@color/white"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> onItemClick.onCancel()}">
<!-- Card with border -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:orientation="horizontal"
android:background="@drawable/bg_edittext_primary_border"
android:padding="16dp">
<!-- Transaction Icon -->
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_custom_pos"
app:tint="@color/colorPrimary"
android:alpha="0.8"
android:layout_marginEnd="12dp"
android:layout_gravity="top" />
<!-- Transaction Details -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<!-- Transaction Type and Amount Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<!-- Transaction Type -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{POSUtil.getInstance().getTransName(payDetail)}"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="sans-serif-medium"
tools:text="SALE" />
<!-- Transaction Amount -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{POSUtil.getInstance().checkMinusSign(payDetail.transactionType)+POSUtil.getInstance().getDecimalAmountSeparatorFormat(payDetail.amount) + ` `+POSUtil.getInstance().currencyCodeToText(payDetail.currencyCode)}"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="sans-serif-medium"
tools:text="1,000.00 MMK" />
</LinearLayout>
<!-- Transaction Date -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{POSUtil.getInstance().convertDateFormatByDateString(payDetail.transDate)}"
android:textColor="@color/colorPrimary"
android:textSize="14sp"
android:alpha="0.8"
android:fontFamily="sans-serif"
android:layout_marginBottom="6dp"
tools:text="2022-11-26" />
<!-- Card Number -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{POSUtil.getInstance().getCardNumMasking(payDetail.cardNo)}"
android:textColor="@color/colorPrimary"
android:textSize="14sp"
android:alpha="0.8"
android:fontFamily="monospace"
android:layout_marginBottom="8dp"
tools:text="468785******9564" />
<!-- Trace and RRN Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- Trace Number -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:isTrace="@{payDetail}"
android:textColor="@color/colorPrimary"
android:textSize="13sp"
android:alpha="0.7"
android:fontFamily="monospace"
tools:text="TRC:000050" />
<!-- RRN -->
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{`RRN:`+payDetail.referNo}"
android:textColor="@color/colorPrimary"
android:textSize="13sp"
android:alpha="0.7"
android:fontFamily="monospace"
android:textAlignment="textEnd"
tools:text="RRN:233001786242" />
</LinearLayout>
</LinearLayout>
<!-- Arrow Icon -->
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_right_arrow"
app:tint="@color/colorPrimary"
android:alpha="0.6"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>

View File

@ -5,58 +5,61 @@
<data> <data>
<import type="com.utsmm.kbz.R"/> <import type="com.utsmm.kbz.R"/>
<import type="com.utsmm.kbz.util.enums.FeaturesType" />
<variable <variable
name="item" name="item"
type="com.utsmm.kbz.config.data.model.Features" /> type="com.utsmm.kbz.config.data.model.Features" />
</data> </data>
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/trans_card" android:id="@+id/trans_card"
android:layout_width="match_parent" android:layout_width="100dp"
android:layout_height="180dp" android:layout_height="100dp"
android:layout_margin="8dp" android:layout_margin="6dp"
app:cardCornerRadius="20dp" android:foreground="?android:attr/selectableItemBackground"
app:cardElevation="6dp" android:clickable="true"
android:foreground="?android:attr/selectableItemBackground" android:focusable="true"
android:clickable="true" app:cardCornerRadius="20dp"
app:disableBtn="@{item.isActive}" app:cardElevation="6dp"
android:focusable="true"> app:disableBtn="@{item.isActive}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="1:1">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="16dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
app:srcUrl="@{item.drawable}"
app:tint="@color/white"
tools:tint="@color/white"
tools:src="@drawable/ic_sale" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/rubik_medium"
android:textSize="16sp"
android:textStyle="bold"
android:textAlignment="center"
android:textColor="@color/white"
android:text="@{item.name}"
tools:text="SALE"
android:gravity="center" android:gravity="center"
android:maxLines="2" android:orientation="vertical"
android:ellipsize="end" /> android:padding="16dp"
android:background="@color/white">
</LinearLayout> <ImageView
android:id="@+id/btnIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginBottom="8dp"
app:srcUrl="@{item.drawable}"
app:tint="@color/colorPrimary"
tools:src="@drawable/ic_sale"
tools:tint="@color/colorPrimary" />
</androidx.cardview.widget.CardView> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:gravity="center"
android:text="@{item.name}"
tools:text="button title"
android:textAlignment="center"
android:textColor="@color/colorPrimary"
android:textSize="12sp"
android:textStyle="bold" />
</layout> </LinearLayout>
</androidx.cardview.widget.CardView>
</layout>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.utsmm.kbz.R"/>
<import type="com.utsmm.kbz.util.enums.FeaturesType" />
<variable
name="item"
type="com.utsmm.kbz.config.data.model.Features" />
</data>
<androidx.cardview.widget.CardView
android:id="@+id/trans_card"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_margin="8dp"
app:cardCornerRadius="20dp"
app:cardElevation="6dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
app:disableBtn="@{item.isActive}"
android:focusable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/primary_card_bg"
android:padding="16dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
app:srcUrl="@{item.drawable}"
app:tint="@color/white"
tools:tint="@color/white"
tools:src="@drawable/ic_sale" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/rubik_medium"
android:textSize="16sp"
android:textStyle="bold"
android:textAlignment="center"
android:textColor="@color/white"
android:text="@{item.name}"
tools:text="SALE"
android:gravity="center"
android:maxLines="2"
android:ellipsize="end" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>

View File

@ -141,6 +141,9 @@
<action <action
android:id="@+id/action_nav_main_to_QRConnectingFragment" android:id="@+id/action_nav_main_to_QRConnectingFragment"
app:destination="@id/QRConnectingFragment" /> app:destination="@id/QRConnectingFragment" />
<action
android:id="@+id/action_nav_main_to_qrFragment"
app:destination="@id/qrFragment" />
<action <action
app:launchSingleTop="true" app:launchSingleTop="true"
app:popUpTo="@+id/nav_main" app:popUpTo="@+id/nav_main"
@ -186,6 +189,12 @@
app:popUpToInclusive="true" app:popUpToInclusive="true"
android:id="@+id/action_cardWaitingFragment_to_nav_main" android:id="@+id/action_cardWaitingFragment_to_nav_main"
app:destination="@id/nav_main" /> app:destination="@id/nav_main" />
<action
android:id="@+id/action_cardWaitingFragment_to_pinPadFragment"
app:launchSingleTop="true"
app:popUpTo="@+id/cardWaitingFragment"
app:popUpToInclusive="true"
app:destination="@id/pinPadFragment" />
</fragment> </fragment>
<fragment <fragment
tools:layout="@layout/fragment_emv_input_pin" tools:layout="@layout/fragment_emv_input_pin"
@ -332,6 +341,18 @@
app:popUpToInclusive="true" app:popUpToInclusive="true"
android:id="@+id/action_inputPasswordFragment_to_inputTraceFragment" android:id="@+id/action_inputPasswordFragment_to_inputTraceFragment"
app:destination="@id/inputTraceFragment" /> app:destination="@id/inputTraceFragment" />
<action
app:launchSingleTop="true"
app:popUpTo="@+id/inputPasswordFragment"
app:popUpToInclusive="true"
android:id="@+id/action_inputPasswordFragment_to_qrRefundList"
app:destination="@id/qrRefundList" />
<action
app:launchSingleTop="true"
app:popUpTo="@+id/inputPasswordFragment"
app:popUpToInclusive="true"
android:id="@+id/action_inputPasswordFragment_to_QRRefundProcessFragment"
app:destination="@id/QRRefundProcessFragment" />
</fragment> </fragment>
@ -1070,6 +1091,72 @@
android:id="@+id/action_receiptFragment_to_nav_main" android:id="@+id/action_receiptFragment_to_nav_main"
app:destination="@id/nav_main" /> app:destination="@id/nav_main" />
</fragment> </fragment>
<!-- qr menu-->
<fragment
android:id="@+id/qrFragment"
tools:layout="@layout/fragment_qr_pay"
android:name="com.utsmm.kbz.ui.qr_pay.QRPayFragment"
android:label="QRPayFragment" >
<action
android:id="@+id/action_qrFragment_to_inputAmountFragment"
app:destination="@id/inputAmountFragment" />
<action
android:id="@+id/action_qrFragment_to_qrHistory"
app:destination="@id/reprintAnyTransactionFragment"
/>
<action
android:id="@+id/action_qrFragment_to_qr_refund_list"
app:destination="@id/qrRefundList"
/>
</fragment>
<fragment
android:id="@+id/qrRefundList"
tools:layout="@layout/fragment_qr_refund"
android:name="com.utsmm.kbz.ui.qr_pay.QRRefundFragment"
>
<action
android:id="@+id/action_qrRefundList_to_qrRefundDetail"
app:destination="@id/qrRefundDetail"
/>
</fragment>
<fragment
android:id="@+id/qrRefundDetail"
tools:layout="@layout/fragment_qr_refund_detail"
android:name="com.utsmm.kbz.ui.qr_pay.QRRefundDetailFragment"
>
<action
app:launchSingleTop="true"
app:popUpToInclusive="true"
app:popUpTo="@id/mobile_navigation"
android:id="@+id/action_qrRefundDetail_to_nav_main"
app:destination="@id/nav_main" />
<action
android:id="@+id/action_qrRefundDetail_inputPasswordFragment"
app:destination="@id/inputPasswordFragment"
/>
<!-- <action-->
<!-- app:launchSingleTop="true"-->
<!-- app:popUpTo="@id/qrRefundList"-->
<!-- app:popUpToInclusive="false"-->
<!-- android:id="@+id/action_qrRefundDetail_to_password"-->
<!-- app:destination="@id/QRRefundProcessFragment" />-->
</fragment>
<fragment
tools:layout="@layout/fragment_qr_refund_process"
android:id="@+id/QRRefundProcessFragment"
android:name="com.utsmm.kbz.ui.qr_pay.QRRefundProcessFragment"
android:label="QRRefundProcessFragment" >
<action
app:launchSingleTop="true"
app:popUpTo="@+id/QRRefundProcessFragment"
app:popUpToInclusive="true"
android:id="@+id/action_QRRefundProcessFragment_to_transactionResultFragment"
app:destination="@id/transactionResultFragment" />
<action
app:launchSingleTop="true"
app:popUpTo="@+id/QRRefundProcessFragment"
app:popUpToInclusive="true"
android:id="@+id/action_QRRefundProcessFragment_to_nav_main"
app:destination="@id/nav_main" />
</fragment>
</navigation> </navigation>

View File

@ -308,7 +308,7 @@
<string name="title_processing">Processing</string> <string name="title_processing">Processing</string>
<string name="enter_trans_id_text">Enter Transaction Id</string> <string name="enter_trans_id_text">Enter Transaction Id</string>
<string name="title_trans_id">Transaction Id</string> <string name="title_trans_id">Transaction Id</string>
<string name="title_wave_pay">Wave Pay</string> <string name="title_wave_pay">MMQR Pay</string>
<string name="title_qr_scan">QR Scan</string> <string name="title_qr_scan">QR Scan</string>
<string name="txt_wave_pay_description">Please scan here to complete your payment.</string> <string name="txt_wave_pay_description">Please scan here to complete your payment.</string>
<string name="txt_title_papaer_roll">Paper roll not found!</string> <string name="txt_title_papaer_roll">Paper roll not found!</string>
@ -362,6 +362,7 @@
<string name="alert_sound_title">Alert Sound</string> <string name="alert_sound_title">Alert Sound</string>
<string name="title_echo">Echo Test</string> <string name="title_echo">Echo Test</string>
<string name="txt_processing_card">Processing card, please wait...</string> <string name="txt_processing_card">Processing card, please wait...</string>
<string name="txt_processing_refund">Processing refund, please wait...</string>
<string name="title_processing_card">Processing Card</string> <string name="title_processing_card">Processing Card</string>
<string name="title_log_on">Log-On</string> <string name="title_log_on">Log-On</string>
<string name="title_log_off">Log-Off</string> <string name="title_log_off">Log-Off</string>
@ -515,6 +516,7 @@
<string name="txt_error_occurred">Error Occured</string> <string name="txt_error_occurred">Error Occured</string>
<string name="error_occurred">Error Occured</string> <string name="error_occurred">Error Occured</string>
<string name="nav_app_bar_open_drawer_description">Open navigation drawer</string> <string name="nav_app_bar_open_drawer_description">Open navigation drawer</string>
<string name="no_history">No History yet</string>
<string-array name="pref_bank_name"> <string-array name="pref_bank_name">
<item>UTS</item> <item>UTS</item>
<item>YOMA Bank</item> <item>YOMA Bank</item>

View File

@ -6,5 +6,9 @@
<domain includeSubdomains="true">128.199.170.203</domain> <domain includeSubdomains="true">128.199.170.203</domain>
<domain includeSubdomains="true">152.42.199.193</domain> <domain includeSubdomains="true">152.42.199.193</domain>
<domain includeSubdomains="true">api.kbzpay.com</domain> <domain includeSubdomains="true">api.kbzpay.com</domain>
<domain-config>
<domain includeSubdomains="true">receipt-nest.utsmyanmar.com</domain>
</domain-config>
<domain includeSubdomains="true">sirius-nest.utsmyanmar.com</domain>
</domain-config> </domain-config>
</network-security-config> </network-security-config>

View File

@ -14,4 +14,7 @@ public class ExampleUnitTest {
public void addition_isCorrect() { public void addition_isCorrect() {
assertEquals(4, 2 + 2); assertEquals(4, 2 + 2);
} }
} }

View File

@ -11,6 +11,7 @@ import com.sunmi.pay.hardware.aidl.AidlConstants;
import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2; import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
import com.sunmi.pay.hardware.aidlv2.security.SecurityOptV2; import com.sunmi.pay.hardware.aidlv2.security.SecurityOptV2;
import com.utsmyanmar.paylibs.Constant; import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.PayLibNex;
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil; import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation; import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.secure.TriDes; import com.utsmyanmar.paylibs.utils.secure.TriDes;
@ -24,8 +25,11 @@ public final class TerminalKeyUtil {
public static final String TAG = TerminalKeyUtil.class.getSimpleName(); public static final String TAG = TerminalKeyUtil.class.getSimpleName();
private static final byte[] main_key_data = ByteUtil.hexStr2Bytes("875f63741753d18811a3449090d1777b".toUpperCase()); // private static final byte[] main_key_data = ByteUtil.hexStr2Bytes("875f63741753d18811a3449090d1777b".toUpperCase()); // KBZ UAT
private static final byte[] main_key_data = ByteUtil.hexStr2Bytes("e121249099a677e8b7d4f6a9d49fe8d1".toUpperCase()); // MPU
private static final byte[] work_key_data = new byte[16]; private static final byte[] work_key_data = new byte[16];
public static void initNexKey() { public static void initNexKey() {
byte[] IPEKByte = ByteUtil.hexStr2Bytes("5A4CFF6DE93F0488926166F3317EFD2F"); // IPEK byte[] IPEKByte = ByteUtil.hexStr2Bytes("5A4CFF6DE93F0488926166F3317EFD2F"); // IPEK
byte[] IPEKKCVByte = ByteUtil.hexStr2Bytes("1F0C18"); byte[] IPEKKCVByte = ByteUtil.hexStr2Bytes("1F0C18");
@ -36,6 +40,8 @@ public final class TerminalKeyUtil {
Arrays.fill(work_key_data, (byte) 0x31); Arrays.fill(work_key_data, (byte) 0x31);
PinPad pinPad = BaseApplication.getInstance().deviceEngine.getPinPad(); PinPad pinPad = BaseApplication.getInstance().deviceEngine.getPinPad();
int result = pinPad.writeMKey(9, main_key_data, main_key_data.length); int result = pinPad.writeMKey(9, main_key_data, main_key_data.length);
// //
// int resultPINKEY = pinPad.writeWKey(0, WorkKeyTypeEnum.PINKEY, work_key_data, work_key_data.length); // int resultPINKEY = pinPad.writeWKey(0, WorkKeyTypeEnum.PINKEY, work_key_data, work_key_data.length);
// int resultTDKEY = pinPad.writeWKey(0, WorkKeyTypeEnum.TDKEY, work_key_data, work_key_data.length); // int resultTDKEY = pinPad.writeWKey(0, WorkKeyTypeEnum.TDKEY, work_key_data, work_key_data.length);
@ -47,6 +53,12 @@ public final class TerminalKeyUtil {
// int result = pinPad.dukptKeyInject(9, DukptKeyTypeEnum.BDK,IPEKByte,16,ksn); // int result = pinPad.dukptKeyInject(9, DukptKeyTypeEnum.BDK,IPEKByte,16,ksn);
LogUtil.d(TAG, "save tmk key result: "+result); LogUtil.d(TAG, "save tmk key result: "+result);
byte[] encryptedPIK = SystemParamsOperation.getInstance().getKeyPIK().get(0);
result = pinPad.writeWKey(9, WorkKeyTypeEnum.PINKEY,encryptedPIK,encryptedPIK.length);
LogUtil.d(TAG, "save PIK key result: "+result);
// LogUtil.d(TAG, "save pin key result: "+resultPINKEY); // LogUtil.d(TAG, "save pin key result: "+resultPINKEY);
// LogUtil.d(TAG, "save tdk key result: "+resultTDKEY); // LogUtil.d(TAG, "save tdk key result: "+resultTDKEY);
// LogUtil.d(TAG, "save tek key result: "+resultTEK); // LogUtil.d(TAG, "save tek key result: "+resultTEK);

View File

@ -149,4 +149,10 @@ public interface PayDetailDao {
@Query("SELECT * FROM paydetail ORDER BY PID DESC") @Query("SELECT * FROM paydetail ORDER BY PID DESC")
LiveData<List<PayDetail>> getAllTrans(); LiveData<List<PayDetail>> getAllTrans();
@Query("SELECT * FROM paydetail WHERE transactionType = 20 ORDER BY PID DESC")
LiveData<List<PayDetail>> getAllQRHistory();
@Query("SELECT * FROM paydetail WHERE transactionType = 20 AND qrTransStatus = 1 AND isCanceled = 0 ORDER BY PID DESC")
LiveData<List<PayDetail>> getRefundableQRHistory();
} }

View File

@ -23,6 +23,7 @@ import com.utsmyanmar.baselib.db.dao.QuickPassDao;
import com.utsmyanmar.baselib.db.dao.RidDao; import com.utsmyanmar.baselib.db.dao.RidDao;
import com.utsmyanmar.baselib.db.model.EmvDetail; import com.utsmyanmar.baselib.db.model.EmvDetail;
import com.utsmyanmar.baselib.network.DemoQRApiService; import com.utsmyanmar.baselib.network.DemoQRApiService;
import com.utsmyanmar.baselib.network.EReceiptApiService;
import com.utsmyanmar.baselib.network.KPayApiService; import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService; import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService; import com.utsmyanmar.baselib.network.SiriusApiService;
@ -447,10 +448,12 @@ public class DatabaseModule {
com.utsmyanmar.baselib.repo.local.PayWaveRepository payWaveRepository, com.utsmyanmar.baselib.repo.local.PayWaveRepository payWaveRepository,
DemoQRApiService demoQRApiService, DemoQRApiService demoQRApiService,
KPayApiService kPayApiService, KPayApiService kPayApiService,
KPayRefundApiService kPayRefundApiService) { KPayRefundApiService kPayRefundApiService,
EReceiptApiService eReceiptApiService
) {
return new com.utsmyanmar.baselib.repo.Repository(payDetailDao, return new com.utsmyanmar.baselib.repo.Repository(payDetailDao,
siriusApiService, emvDetailDao, siriusApiService, emvDetailDao,
payWaveRepository, demoQRApiService, kPayApiService, kPayRefundApiService); payWaveRepository, demoQRApiService, kPayApiService, kPayRefundApiService, eReceiptApiService);
} }
@Provides @Provides

View File

@ -9,6 +9,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
//import com.utsmyanmar.baselib.BuildConfig; //import com.utsmyanmar.baselib.BuildConfig;
import com.utsmyanmar.baselib.network.DemoQRApiService; import com.utsmyanmar.baselib.network.DemoQRApiService;
import com.utsmyanmar.baselib.network.EReceiptApiService;
import com.utsmyanmar.baselib.network.KPayApiService; import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService; import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService; import com.utsmyanmar.baselib.network.SiriusApiService;
@ -386,5 +387,25 @@ public class NetworkModule {
return retrofit.create(KPayRefundApiService.class); return retrofit.create(KPayRefundApiService.class);
} }
@Provides
@Singleton
public EReceiptApiService provideEReceiptApiService(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
return new Retrofit.Builder()
.baseUrl("http://receipt-nest.utsmyanmar.com/") // base URL
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(EReceiptApiService.class);
}
} }

View File

@ -0,0 +1,20 @@
package com.utsmyanmar.baselib.network;
import com.google.gson.JsonElement;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import io.reactivex.rxjava3.core.Observable;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.POST;
public interface EReceiptApiService {
@POST("transaction")
Observable<EReceiptResponse> sendReceipt(
@Header("x-timestamp") String timestamp,
@Header("x-api-key") String apiKey,
@Body Object body
);
}

View File

@ -29,15 +29,18 @@ public class SiriusInterceptor implements Interceptor {
@Override @Override
public Response intercept(@NonNull Chain chain) throws IOException { public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request(); Request request = chain.request();
Request newRequest; Request newRequest;
String hashed = ""; String hashed = "";
String nonce = TerminalUtil.getInstance().generateRandomNumbers(); String nonce = TerminalUtil.getInstance().generateRandomNumbers();
// String nonce = "1234567";
try { try {
hashed = TerminalUtil.getInstance().generateHashedString(nonce); // hashed = TerminalUtil.getInstance().generateHashedString(nonce); // old tms
hashed = TerminalUtil.getInstance().generateHashedString(nonce).toLowerCase(); // new tms
// LogUtil.d(TAG,"hashed :"+ hashed); LogUtil.d(TAG,"hashed :"+ hashed);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -47,6 +50,7 @@ public class SiriusInterceptor implements Interceptor {
// .addHeader("content-type", "application/json") // .addHeader("content-type", "application/json")
.addHeader("request-id", hashed) .addHeader("request-id", hashed)
.addHeader("request-code",nonce) .addHeader("request-code",nonce)
// .addHeader("x-api-key","8f4df38d1001bcc4620b5c736c66a03eef4653eb3ba31105faa2f2ee294c4a46")
.build(); .build();
return chain.proceed(newRequest); return chain.proceed(newRequest);

View File

@ -0,0 +1,123 @@
package com.utsmyanmar.baselib.network.model.e_receipt;
public class EReceiptQRRequest {
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;
public EReceiptQRRequest() {}
// 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;
}
// 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;
}
}

View File

@ -0,0 +1,14 @@
package com.utsmyanmar.baselib.network.model.e_receipt;
public class EReceiptResponse {
private String message;
private String code;
public String getMessage(){
return message;
}
public String getCode(){
return code;
}
}

View File

@ -15,10 +15,12 @@ public class SiriusRequest {
private String lastTransaction; private String lastTransaction;
private String lastTranTime; private Long lastTranTime;
private String value;
public SiriusRequest() {} public SiriusRequest() {}
public SiriusRequest(String serial, String appPackage, String androidVersion, String firmwareVersion, String applicationVersion, String currentNetwork, String lastTransaction, String lastTranTime) { public SiriusRequest(String serial, String appPackage, String androidVersion, String firmwareVersion, String applicationVersion, String currentNetwork, String lastTransaction, Long lastTranTime, String value) {
this.serial = serial; this.serial = serial;
this.appPackage = appPackage; this.appPackage = appPackage;
this.androidVersion = androidVersion; this.androidVersion = androidVersion;
@ -27,6 +29,7 @@ public class SiriusRequest {
this.currentNetwork = currentNetwork; this.currentNetwork = currentNetwork;
this.lastTransaction = lastTransaction; this.lastTransaction = lastTransaction;
this.lastTranTime = lastTranTime; this.lastTranTime = lastTranTime;
this.value = value;
} }
public void setSerial(String serial) { public void setSerial(String serial) {
@ -57,7 +60,7 @@ public class SiriusRequest {
this.lastTransaction = lastTransaction; this.lastTransaction = lastTransaction;
} }
public void setLastTranTime(String lastTranTime) { public void setLastTranTime(Long lastTranTime) {
this.lastTranTime = lastTranTime; this.lastTranTime = lastTranTime;
} }
@ -89,7 +92,15 @@ public class SiriusRequest {
return lastTransaction; return lastTransaction;
} }
public String getLastTranTime() { public Long getLastTranTime() {
return lastTranTime; return lastTranTime;
} }
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
} }

View File

@ -4,11 +4,14 @@ package com.utsmyanmar.baselib.repo;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.utsmyanmar.baselib.db.dao.EmvDetailDao; import com.utsmyanmar.baselib.db.dao.EmvDetailDao;
import com.utsmyanmar.baselib.db.dao.PayDetailDao; import com.utsmyanmar.baselib.db.dao.PayDetailDao;
import com.utsmyanmar.baselib.db.model.EmvDetail; import com.utsmyanmar.baselib.db.model.EmvDetail;
import com.utsmyanmar.baselib.db.model.PayWaveEntity; import com.utsmyanmar.baselib.db.model.PayWaveEntity;
import com.utsmyanmar.baselib.network.DemoQRApiService; import com.utsmyanmar.baselib.network.DemoQRApiService;
import com.utsmyanmar.baselib.network.EReceiptApiService;
import com.utsmyanmar.baselib.network.KPayApiService; import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService; import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService; import com.utsmyanmar.baselib.network.SiriusApiService;
@ -29,10 +32,12 @@ import com.utsmyanmar.baselib.network.model.WaveQRRequest;
import com.utsmyanmar.baselib.network.model.WaveQRResponse; import com.utsmyanmar.baselib.network.model.WaveQRResponse;
import com.utsmyanmar.baselib.network.model.WaveStatusRequest; import com.utsmyanmar.baselib.network.model.WaveStatusRequest;
import com.utsmyanmar.baselib.network.model.WaveTokenResponse; import com.utsmyanmar.baselib.network.model.WaveTokenResponse;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest; import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse; import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.baselib.network.model.KPayRefund; import com.utsmyanmar.baselib.network.model.KPayRefund;
import com.utsmyanmar.baselib.repo.local.PayWaveRepository; import com.utsmyanmar.baselib.repo.local.PayWaveRepository;
import com.utsmyanmar.baselib.util.EReceiptHelper;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.List; import java.util.List;
@ -57,8 +62,9 @@ public class Repository {
private DemoQRApiService demoQRApiService; private DemoQRApiService demoQRApiService;
private KPayRefundApiService kPayRefundApiService; private KPayRefundApiService kPayRefundApiService;
private EReceiptApiService eReceiptApiService;
@Inject @Inject
public Repository(PayDetailDao payDetailDao, SiriusApiService siriusApiService, EmvDetailDao emvDetailDao, PayWaveRepository payWaveRepository, DemoQRApiService demoQRApiService, KPayApiService kPayApiService, KPayRefundApiService kPayRefundApiService) { public Repository(PayDetailDao payDetailDao, SiriusApiService siriusApiService, EmvDetailDao emvDetailDao, PayWaveRepository payWaveRepository, DemoQRApiService demoQRApiService, KPayApiService kPayApiService, KPayRefundApiService kPayRefundApiService, EReceiptApiService eReceiptApiService) {
this.payDetailDao = payDetailDao; this.payDetailDao = payDetailDao;
this.siriusApiService = siriusApiService; this.siriusApiService = siriusApiService;
this.emvDetailDao = emvDetailDao; this.emvDetailDao = emvDetailDao;
@ -66,6 +72,7 @@ public class Repository {
this.demoQRApiService = demoQRApiService; this.demoQRApiService = demoQRApiService;
this.kPayApiService = kPayApiService; this.kPayApiService = kPayApiService;
this.kPayRefundApiService = kPayRefundApiService; this.kPayRefundApiService = kPayRefundApiService;
this.eReceiptApiService = eReceiptApiService;
} }
public Observable<KPayQRResponse.QrResponse> qrGenerate(KPayQRRequest.QrRequest request) { public Observable<KPayQRResponse.QrResponse> qrGenerate(KPayQRRequest.QrRequest request) {
@ -98,9 +105,14 @@ public class Repository {
public Observable<SiriusResponse> getParams(SiriusRequest siriusRequest) { public Observable<SiriusResponse> getParams(SiriusRequest siriusRequest) {
return siriusApiService.getParams(siriusRequest); return siriusApiService.getParams(siriusRequest);
} }
public Observable<EReceiptResponse> sendReceipt(Object body){
String apiSecret = "8f4df38d1001bcc4620b5c736c66a03eef4653eb3ba31105faa2f2ee294c4a46";
String timestamp = String.valueOf(System.currentTimeMillis());
String bodyString = new Gson().toJson(body);
String dataToHash = bodyString + apiSecret + timestamp;
String signature = EReceiptHelper.sha256(dataToHash);
return eReceiptApiService.sendReceipt(timestamp, signature, body);
}
public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);} public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);}
@ -226,4 +238,8 @@ public class Repository {
} }
public LiveData<List<PayDetail>> getAllTrans() { return payDetailDao.getAllTrans(); } public LiveData<List<PayDetail>> getAllTrans() { return payDetailDao.getAllTrans(); }
public LiveData<List<PayDetail>> getAllQRHistory(){ return payDetailDao.getAllQRHistory();}
public LiveData<List<PayDetail>> getRefundableQRHistory(){ return payDetailDao.getRefundableQRHistory(); }
} }

View File

@ -0,0 +1,48 @@
package com.utsmyanmar.baselib.util;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class EReceiptHelper {
public static String hmacSha256(String key, String data) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
mac.init(secretKey);
byte[] hash = mac.doFinal(data.getBytes());
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b));
}
return hex.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String sha256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -56,9 +56,15 @@ public class TerminalUtilsImpl implements TerminalUtils{
@Override @Override
public String generateRandom() { public String generateRandom() {
Random rnd = new Random(); Random rnd = new Random();
StringBuilder sb = new StringBuilder((1000000 + rnd.nextInt(9000000))); StringBuilder sb = new StringBuilder();
sb.append(1000000 + rnd.nextInt(9000000));
return sb.toString(); return sb.toString();
// Random rnd = new Random();
// StringBuilder sb = new StringBuilder((1000000 + rnd.nextInt(9000000)));
// return sb.toString();
} }
@Override @Override

View File

@ -564,6 +564,8 @@ public abstract class EmvBaseViewModel extends BaseViewModel {
mHandler.obtainMessage(PIN_TIME_OUT,-7009).sendToTarget(); mHandler.obtainMessage(PIN_TIME_OUT,-7009).sendToTarget();
} else if (retCode == SdkResult.PinPad_No_Pin_Input) { } else if (retCode == SdkResult.PinPad_No_Pin_Input) {
mHandler.obtainMessage(PIN_CLICK_CONFIRM).sendToTarget(); mHandler.obtainMessage(PIN_CLICK_CONFIRM).sendToTarget();
} else {
mHandler.obtainMessage(PIN_ERROR).sendToTarget();
} }
LogUtil.d(TAG, "RetCode:" + retCode); LogUtil.d(TAG, "RetCode:" + retCode);
@ -631,24 +633,6 @@ public abstract class EmvBaseViewModel extends BaseViewModel {
++pinEnterCount; ++pinEnterCount;
} }
private void keyMap(String str, PinPadDataV2 data) {
data.keyMap = new byte[64];
for (int i = 0, j = 0; i < 15; i++, j++) {
if (i == 9 || i == 12) { // || i == 12
data.keyMap[i] = 0x1B;//cancel
j--;
} else if (i == 13) { //10
data.keyMap[i] = 0x0C;//clear
j--;
} else if (i == 11 || i == 14) { // || i == 14
data.keyMap[i] = 0x0D;//confirm
j--;
} else {
data.keyMap[i] = (byte) str.charAt(j);
}
}
}
private OnEmvProcessListener2 emvProcessListener = new OnEmvProcessListener2() { private OnEmvProcessListener2 emvProcessListener = new OnEmvProcessListener2() {
@Override @Override

View File

@ -4,6 +4,13 @@ import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
/** /**
* Example local unit test, which will execute on the development machine (host). * Example local unit test, which will execute on the development machine (host).
* *
@ -20,10 +27,63 @@ public class ExampleUnitTest {
@Test @Test
public void testUpperCase() { public void testUpperCase() {
String keys = "875f63741753d18811a3449090d1777b".toUpperCase(); // String keys = "875f63741753d18811a3449090d1777b".toUpperCase();
String keys = "e121249099a677e8b7d4f6a9d49fe8d1".toUpperCase();
System.out.println("Key: "+keys); System.out.println("Key: "+keys);
String pan = "4216862078830572"; String pan = "4216862078830572";
System.out.println("sub pan: "+pan.substring(pan.length()-13)); System.out.println("sub pan: "+pan.substring(pan.length()-13));
} }
@Test
public void hashString() {
String sn = "N86PW400005";
String snPN = "com.utsmm.kbz";
String nonce = "1234567";
String text = sn + snPN + nonce;
// LogUtil.d(TAG,"Plain Text: "+text);
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String hashStr = ByteUtil.bytes2HexStr(hash);
System.out.println("Hash: "+hashStr.toLowerCase());
}
@Test
public void generateRandomDigit() {
Random rnd = new Random();
StringBuilder sb = new StringBuilder();
sb.append(1000000 + rnd.nextInt(9000000));
String random = sb.toString();
System.out.println("Random: " + random);
}
@Test
public void hashTestString() {
String sn = "SN3123141";
String snPN = "default-package-1";
String nonce = "1234567";
String text = sn + snPN + nonce;
// LogUtil.d(TAG,"Plain Text: "+text);
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String hashStr = ByteUtil.bytes2HexStr(hash);
System.out.println("Hash: "+hashStr.toLowerCase());
}
} }

View File

@ -24,9 +24,208 @@ public class DecodePackage {
} }
public static HashMap<String, MsgField> unAssembly(byte[] body, HostName hostName,boolean printLog) { public static HashMap<String, MsgField> unAssembly(byte[] body, HostName hostName,boolean printLog) {
return unAssembly(body, FieldConfig.FieldTypeConfig128,hostName,printLog); return unAssembly(body,hostName == HostName.BPC? FieldConfig.FieldTypeConfig128 : FieldConfig.FieldTypeConfigV1,hostName,printLog);
} }
public synchronized static HashMap<String, MsgField> unAssembly(byte[] body, int[][] fieldConfig) {
HashMap<String, MsgField> resMap = new HashMap<>();
// Request header length 15
// Len[2] + tPDU[5] + Header[6] + Type[2]
int head = 7 + FieldConfig.MESSAGE_HEADER_LENGTH;
// Calculate the length of the message type
byte[] msgData = null;
int typeLength = FieldConfig.FieldTypeConfig128[0][2];
switch (FieldConfig.FieldTypeConfig128[0][3]) {
case FieldConfig.SDK_8583_DATA_BCD:
head += (typeLength + 1) / 2;
msgData = new byte[(typeLength + 1) / 2];
break;
case FieldConfig.SDK_8583_DATA_ASC:
case FieldConfig.SDK_8583_DATA_BIT:
head += typeLength;
msgData = new byte[typeLength];
break;
default:
break;
}
// Fetch header
byte[] headerData = new byte[FieldConfig.MESSAGE_HEADER_LENGTH];
System.arraycopy(body, 7, headerData, 0, FieldConfig.MESSAGE_HEADER_LENGTH);
MsgField headerFiled = new MsgField();
headerFiled.setFieldPos(-1);
headerFiled.setLengthType(fieldConfig[0][0]);
headerFiled.setLengthTypeEncode(fieldConfig[0][1]);
headerFiled.setDataType(fieldConfig[0][3]);
headerFiled.setAlignType(fieldConfig[0][4]);
headerFiled.setFillChar(fieldConfig[0][5]);
headerFiled.setDataLength(fieldConfig[0][2]);
headerFiled.setDataBytes(headerData);
headerFiled.setDataStr(Utils.byte2HexStr(headerData));
resMap.put("header", headerFiled);
String header = Utils.byte2HexStr(headerData);
LogUtil.d(TAG, "header" + header);
// Get message type
System.arraycopy(body, 7 + FieldConfig.MESSAGE_HEADER_LENGTH, msgData, 0, msgData.length);
String msgType = "";
switch (FieldConfig.FieldTypeConfig128[0][3]) {
case FieldConfig.SDK_8583_DATA_BCD:
case FieldConfig.SDK_8583_DATA_BIT:
msgType = Utils.byte2HexStr(msgData);
break;
case FieldConfig.SDK_8583_DATA_ASC:
try {
msgType = new String(msgData, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
break;
default:
break;
}
MsgField msgFiled = new MsgField();
msgFiled.setFieldPos(0);
msgFiled.setLengthType(fieldConfig[0][0]);
msgFiled.setLengthTypeEncode(fieldConfig[0][1]);
msgFiled.setDataType(fieldConfig[0][3]);
msgFiled.setAlignType(fieldConfig[0][4]);
msgFiled.setFillChar(fieldConfig[0][5]);
msgFiled.setDataLength(fieldConfig[0][2]);
msgFiled.setDataBytes(msgData);
msgFiled.setDataStr(msgType);
resMap.put("type", msgFiled);
LogUtil.d(TAG, "Message type" + msgType);
// Get bitmap
boolean[] boolBitmap;
byte[] bitmap = new byte[8];
System.arraycopy(body, head, bitmap, 0, 8);
boolBitmap = Utils.getBinaryFromByte(bitmap);
if (boolBitmap[1]) {
//If the first bit is 1, it is an expandable bitmap, set to 16 bytes in length.
byte[] b = new byte[16];
System.arraycopy(body, head, b, 0, 16);
boolBitmap = Utils.getBinaryFromByte(b);
head += 16;
} else {
head += 8;
}
int size = boolBitmap.length;
// Total length of request header and bitmap
int tmpLen = head;
// 0 1 Domain not used
for (int i = 2; i < size; i++) {
if (boolBitmap[i]) {
MsgField outField = new MsgField();
outField.setFieldPos(i);
outField.setLengthType(fieldConfig[i][0]);
outField.setLengthTypeEncode(fieldConfig[i][1]);
outField.setDataType(fieldConfig[i][3]);
outField.setAlignType(fieldConfig[i][4]);
outField.setFillChar(fieldConfig[i][5]);
// len is useless for variable length
outField.setDataLength(fieldConfig[i][2]);
byte[] nextData;
//53 templen - 54
// Get the value of the variable length part
if (outField.getLengthType() > 0) {
// Get the length of the byte array
int varLen = (outField.getLengthType() + 1) / 2;
byte[] varValue = new byte[varLen];
if(i==62){
tmpLen++;
}
System.arraycopy(body, tmpLen, varValue, 0, varValue.length);
// Length of data length
tmpLen += varLen; //tmp len 54
// Number of bytes of variable length data
int datLen;
int bcdLength = Utils.bcdToint(varValue); //31
// Determine the data type (binary type data is not processed)
if (outField.getDataType() == FieldConfig.SDK_8583_DATA_BCD) {
datLen = (bcdLength / 2) + (bcdLength % 2);
outField.setDataLength(bcdLength); // Fill the variable length
} else if (outField.getDataType() == FieldConfig.SDK_8583_DATA_BIT) {
datLen = bcdLength;
outField.setDataLength(2 * bcdLength); // Fill the variable length
} else {
//asc
datLen = bcdLength;
outField.setDataLength(bcdLength); // Fill the variable length
}
// Take the value of the variable length part
nextData = new byte[datLen];
//tmp len 55
System.arraycopy(body, tmpLen, nextData, 0, datLen);
//updated
/* if(body.length>tmpLen+datLen){
System.arraycopy(body, tmpLen, nextData, 0, datLen);
}else {
return null;
}*/
// Data length
tmpLen += datLen;
} else {
// Fixed-length data length
int datLen;
// Determine the data type (binary type data is not processed)
if (outField.getDataType() == FieldConfig.SDK_8583_DATA_BCD) {
datLen = (outField.getDataLength() / 2) + (outField.getDataLength() % 2);
} else if (outField.getDataType() == FieldConfig.SDK_8583_DATA_ASC) {
datLen = outField.getDataLength();
} else {
datLen = outField.getDataLength();
}
nextData = new byte[datLen];
System.arraycopy(body, tmpLen, nextData, 0, nextData.length);
tmpLen += nextData.length;
}
// Data input
if (outField.getDataType() == FieldConfig.SDK_8583_DATA_ASC) {
String s = null;
try {
s = new String(nextData, "gbk");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (!TextUtils.isEmpty(s) && Utils.isMessyCode(s)) {
LogUtil.d(TAG, "field pos:" + outField.getFieldPos() + " = " + Utils.byte2HexStr(nextData));
} else {
LogUtil.d(TAG, "field pos:" + outField.getFieldPos() + " = " + s);
}
// Determine whether it is GBK encoding or garbled
outField.setDataStr(new String(nextData));
} else if (outField.getDataType() == FieldConfig.SDK_8583_DATA_BIT) {
LogUtil.d(TAG, "field pos:" + outField.getFieldPos() + " = " + Utils.byte2HexStr(nextData));
outField.setDataStr(Utils.byte2HexStr(nextData));
} else if (outField.getDataType() == FieldConfig.SDK_8583_DATA_BCD) {
LogUtil.d(TAG, "field pos:" + outField.getFieldPos() + " = " + Utils.Bcd2String(nextData));
outField.setDataStr(Utils.Bcd2String(nextData));
}
outField.setDataBytes(nextData);
resMap.put("F" + String.format(Locale.getDefault(), "%03d", i), outField);
}
}
return resMap;
}
public synchronized static HashMap<String, MsgField> unAssembly(byte[] body, int[][] fieldConfig,HostName hostName,boolean printLog) { public synchronized static HashMap<String, MsgField> unAssembly(byte[] body, int[][] fieldConfig,HostName hostName,boolean printLog) {
HashMap<String, MsgField> resMap = new HashMap<>(); HashMap<String, MsgField> resMap = new HashMap<>();
@ -152,9 +351,17 @@ public class DecodePackage {
// Get the value of the variable length part // Get the value of the variable length part
if (outField.getLengthType() > 0) { if (outField.getLengthType() > 0) {
int varLen;
if( hostName == HostName.BPC) {
varLen = outField.getLengthType();
} else {
varLen = (outField.getLengthType() + 1) / 2;
}
// Get the length of the byte array // Get the length of the byte array
// int varLen = (outField.getLengthType() + 1) / 2; // int varLen = (outField.getLengthType() + 1) / 2;
int varLen = outField.getLengthType(); // varLen = outField.getLengthType();
byte[] varValue = new byte[varLen]; byte[] varValue = new byte[varLen];
if(i==62){ if(i==62){
tmpLen++; tmpLen++;
@ -172,6 +379,7 @@ public class DecodePackage {
bcdLength = Integer.parseInt(new String(varValue)); //31 bcdLength = Integer.parseInt(new String(varValue)); //31
} else { } else {
bcdLength = Utils.bcdToint(varValue); //31 bcdLength = Utils.bcdToint(varValue); //31
// bcdLength = Integer.parseInt(new String(varValue)); //31
} }
// Determine the data type (binary type data is not processed) // Determine the data type (binary type data is not processed)
@ -197,8 +405,13 @@ public class DecodePackage {
//tmp len 55 //tmp len 55
try { try {
System.arraycopy(body, tmpLen, nextData, 0, datLen); if(hostName == HostName.BPC) {
// System.arraycopy(body, tmpLen, nextData, 0, body.length-tmpLen); System.arraycopy(body, tmpLen, nextData, 0, datLen);
} else {
System.arraycopy(body, tmpLen, nextData, 0, body.length-tmpLen);
}
//
} catch (IndexOutOfBoundsException e){ } catch (IndexOutOfBoundsException e){
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -19,10 +19,12 @@ import java.util.Map;
import com.utsmyanmar.paylibs.utils.LogUtil; import com.utsmyanmar.paylibs.utils.LogUtil;
/** /**
* Package * Package with MPU certification ready.
*/ */
public class EncodePackage { public class EncodePackage {
public static Map<String, MsgField> preAssemblyBase(String bitmapHexString,HostName hostName) { public static Map<String, MsgField> preAssemblyBase(String bitmapHexString,HostName hostName) {
int [][] fieldTypeViaHost = hostName == HostName.BPC ? FieldConfig.FieldTypeConfig128 : FieldConfig.FieldTypeConfigV1; int [][] fieldTypeViaHost = hostName == HostName.BPC ? FieldConfig.FieldTypeConfig128 : FieldConfig.FieldTypeConfigV1;
return preAssembly(bitmapHexString, fieldTypeViaHost ); return preAssembly(bitmapHexString, fieldTypeViaHost );
@ -137,7 +139,12 @@ public class EncodePackage {
if (fieldDataType == FieldConfig.SDK_8583_DATA_BCD) { if (fieldDataType == FieldConfig.SDK_8583_DATA_BCD) {
if (msgField.getLengthType() > 0) { if (msgField.getLengthType() > 0) {
// F035 enter into this step // F035 enter into this step
len += msgField.getLengthType() + ( msgField.getDataLength() / 2); // len += msgField.getLengthType() + ( msgField.getDataLength() / 2);
if(hostName == HostName.BPC) {
len += msgField.getLengthType() + ( msgField.getDataLength() / 2);
} else {
len += (msgField.getLengthType() + 1) / 2 + (msgField.getDataLength() / 2) + (msgField.getDataLength() % 2);
}
// len += (msgField.getLengthType() + 1) / 2 + (msgField.getDataLength() / 2) + (msgField.getDataLength() % 2); // len += (msgField.getLengthType() + 1) / 2 + (msgField.getDataLength() / 2) + (msgField.getDataLength() % 2);
} else { } else {
len += (msgField.getDataLength() / 2) + (msgField.getDataLength() % 2); len += (msgField.getDataLength() / 2) + (msgField.getDataLength() % 2);
@ -146,7 +153,12 @@ public class EncodePackage {
if (msgField.getLengthType() > 0) { if (msgField.getLengthType() > 0) {
// commended at Oct4 , 2023 ( for BPC DE55) // commended at Oct4 , 2023 ( for BPC DE55)
// len += (msgField.getLengthType() + 1) / 2 + (msgField.getDataLength() / 2); // len += (msgField.getLengthType() + 1) / 2 + (msgField.getDataLength() / 2);
len += msgField.getLengthType() + (msgField.getDataLength() / 2); if(hostName == HostName.BPC) {
len += msgField.getLengthType() + (msgField.getDataLength() / 2);
} else {
len += (msgField.getLengthType() + 1) / 2 + (msgField.getDataLength() / 2);
}
} else { } else {
len += msgField.getDataLength(); len += msgField.getDataLength();
@ -156,8 +168,14 @@ public class EncodePackage {
// 12:51pm jan 26 / 21 comment switch cuz of getting error at field 57 // 12:51pm jan 26 / 21 comment switch cuz of getting error at field 57
// fixing bcd ASCII length odd number eg.3 // fixing bcd ASCII length odd number eg.3
// need to fix this one later for field 62 // need to fix this one later for field 62
if(hostName == HostName.BPC) {
len += msgField.getLengthType() + msgField.getDataLength();
} else {
// len += (msgField.getLengthType() + 1) / 2 + msgField.getDataLength(); // Nov19,2025
len += msgField.getDataLength();
}
// len += (msgField.getLengthType() + 1) / 2 + msgField.getDataLength(); // len += (msgField.getLengthType() + 1) / 2 + msgField.getDataLength();
len += msgField.getLengthType() + msgField.getDataLength(); // len += msgField.getLengthType() + msgField.getDataLength();
// msgField.getLengthType() => 2 // msgField.getLengthType() => 2
// len+=msgField.getDataLength()-msgField.getLengthType(); // len+=msgField.getDataLength()-msgField.getLengthType();
@ -220,7 +238,13 @@ public class EncodePackage {
System.arraycopy(varValue, 0, body, index, varValue.length); System.arraycopy(varValue, 0, body, index, varValue.length);
index += varValue.length; index += varValue.length;
} else if (dataType == FieldConfig.SDK_8583_DATA_BCD) { } else if (dataType == FieldConfig.SDK_8583_DATA_BCD) {
byte[] varValue = (String.format("%0" + msgField.getLengthType() + "d", msgField.getDataLength()/2)).getBytes(); byte[] varValue;
if(hostName == HostName.BPC) {
varValue = (String.format("%0" + msgField.getLengthType() + "d", msgField.getDataLength()/2)).getBytes();
} else {
varValue = Utils.StrToBCDBytes(String.format("%0"+msgField.getLengthType() + "d",msgField.getDataLength()));
}
// byte[] varValue = (String.format("%0" + msgField.getLengthType() + "d", msgField.getDataLength()/2)).getBytes();
// byte[] varValue = Utils.StrToBCDBytes(String.format("%0" + msgField.getLengthType() + "d", msgField.getDataLength())); // byte[] varValue = Utils.StrToBCDBytes(String.format("%0" + msgField.getLengthType() + "d", msgField.getDataLength()));
System.arraycopy(varValue, 0, body, index, varValue.length); System.arraycopy(varValue, 0, body, index, varValue.length);
index += varValue.length; index += varValue.length;
@ -246,27 +270,40 @@ public class EncodePackage {
if (msgField.getDataType() == FieldConfig.SDK_8583_DATA_ASC) { if (msgField.getDataType() == FieldConfig.SDK_8583_DATA_ASC) {
if (msgField.getLengthType() == 2) { if (msgField.getLengthType() == 2) {
/* Field 60 issue with TTIP*/ /* Field 60 issue with TTIP*/
/* String fieldHead = msgField.getDataStr().substring(0,4);
int bcdLen = (fieldHead.length() / 2) + (fieldHead.length() % 2);
byte dat[] = new byte[bcdLen];
Utils.BCDEncode(fieldHead, dat, msgField.getAlignType(), msgField.getFillChar());
System.arraycopy(dat, 0, body, index, bcdLen);
index += bcdLen;
String fieldBody=msgField.getDataStr().substring(4,msgField.getDataLength());
*/
String fieldBody = msgField.getDataStr(); if(hostName == HostName.BPC) {
System.arraycopy(fieldBody.getBytes(StandardCharsets.ISO_8859_1), 0, body, index, fieldBody.length()); String fieldBody = msgField.getDataStr();
index += fieldBody.length(); System.arraycopy(fieldBody.getBytes(StandardCharsets.ISO_8859_1), 0, body, index, fieldBody.length());
index += fieldBody.length();
} else {
String fieldHead = msgField.getDataStr().substring(0,4);
int bcdLen = (fieldHead.length() / 2) + (fieldHead.length() % 2);
byte dat[] = new byte[bcdLen];
Utils.BCDEncode(fieldHead, dat, msgField.getAlignType(), msgField.getFillChar());
System.arraycopy(dat, 0, body, index, bcdLen);
index += bcdLen;
String fieldBody=msgField.getDataStr().substring(4,msgField.getDataLength());
System.arraycopy(fieldBody.getBytes(StandardCharsets.ISO_8859_1), 0, body, index, fieldBody.length());
LogUtil.d(Constant.TAG,"fieldBody : "+fieldBody);
index += fieldBody.length();
}
} else { } else {
LogUtil.d(Constant.TAG, " msgField ------ " + entry.getKey()); LogUtil.d(Constant.TAG, " msgField ------ " + entry.getKey());
try {
if(hostName == HostName.BPC) {
try {
System.arraycopy(msgField.getDataStr().getBytes(StandardCharsets.ISO_8859_1), 0, body, index, msgField.getDataLength());
index += msgField.getDataLength();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
} else {
System.arraycopy(msgField.getDataStr().getBytes(StandardCharsets.ISO_8859_1), 0, body, index, msgField.getDataLength()); System.arraycopy(msgField.getDataStr().getBytes(StandardCharsets.ISO_8859_1), 0, body, index, msgField.getDataLength());
index += msgField.getDataLength(); index += msgField.getDataLength();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
} }
} }
// try { // try {
// LogUtil.d(Constant.TAG, " msgField ------ " + entry.getKey()); // LogUtil.d(Constant.TAG, " msgField ------ " + entry.getKey());

View File

@ -17,7 +17,8 @@ public abstract class BaseISOMsgX {
byte[] newSendBytes = EncodePackage.assembly(map, msgIdentifier, 0, "0000000000",hostName); byte[] newSendBytes = EncodePackage.assembly(map, msgIdentifier, 0, "0000000000",hostName);
String BPCTPDU = "6005150000"; // String BPCTPDU = "6005150000"; //UAT
String BPCTPDU = "6005170000"; //PrePro
if(isoMode == ISOMode.BOTH_HEADER_TPDU) { if(isoMode == ISOMode.BOTH_HEADER_TPDU) {
byte[] outBytes = new byte[newSendBytes.length - 6 ]; byte[] outBytes = new byte[newSendBytes.length - 6 ];
@ -121,19 +122,43 @@ public abstract class BaseISOMsgX {
return newBytes; return newBytes;
} else if(isoMode == ISOMode.ONLY_HEADER) { } else if(isoMode == ISOMode.ONLY_HEADER) {
byte[] outBytes = new byte[receiveBytes.length + 5];
byte[] outBytes = new byte[receiveBytes.length + 6]; byte[] fakeTPDU = ByteUtil.hexStr2Bytes("5555555555");
System.arraycopy(fakeTPDU, 0, outBytes, 2, 5);
System.arraycopy(receiveBytes, 2, outBytes, 7, receiveBytes.length-2 );
int hig = (outBytes.length - 2) / 256;
int low = (outBytes.length - 2) % 256;
outBytes[0] = (byte) hig;
outBytes[1] = (byte) low;
byte[] headBytes = ByteUtil.hexStr2Bytes("888888888888");
System.arraycopy(receiveBytes, 0, outBytes, 0, 7);
System.arraycopy(headBytes, 0, outBytes, 7, headBytes.length);
System.arraycopy(receiveBytes, 7, outBytes, 13, receiveBytes.length - 7);
log = "HexString:" + ByteUtil.bytes2HexStr(outBytes); log = "HexString:" + ByteUtil.bytes2HexStr(outBytes);
LogUtil.d(Constant.TAG, log); LogUtil.d(Constant.TAG, log);
return outBytes; return outBytes;
// byte[] newBytes = new byte[outBytes.length + 6];
//
//
// System.arraycopy(outBytes, 0, newBytes, 0, 7);
// System.arraycopy(headBytes, 0, newBytes, 7, headBytes.length);
// System.arraycopy(outBytes, 7, newBytes, 13, outBytes.length - 7);
//
// log = "HexString Packed:" + ByteUtil.bytes2HexStr(newBytes);
// LogUtil.d(Constant.TAG, log);
// return newBytes;
// byte[] outBytes = new byte[receiveBytes.length + 6];
//
// byte[] headBytes = ByteUtil.hexStr2Bytes("888888888888");
//
// System.arraycopy(receiveBytes, 0, outBytes, 0, 6);
// System.arraycopy(headBytes, 0, outBytes, 6, headBytes.length);
// System.arraycopy(receiveBytes, 6, outBytes, 12, receiveBytes.length - 6);
//
// log = "HexString:" + ByteUtil.bytes2HexStr(outBytes);
// LogUtil.d(Constant.TAG, log);
//
// return outBytes;
} else { } else {

View File

@ -26,11 +26,15 @@ import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -65,6 +69,8 @@ import com.utsmyanmar.paylibs.utils.LogUtil;
public class ISOSocket { public class ISOSocket {
private static final int DEFAULT_TIMEOUT = 30 * 1000; private static final int DEFAULT_TIMEOUT = 30 * 1000;
private static final String TAG = ISOSocket.class.getSimpleName();
private String serverIP; private String serverIP;
private int serverPort; private int serverPort;
private int connectTimeout = 20; private int connectTimeout = 20;
@ -148,16 +154,15 @@ public class ISOSocket {
private SSLSocketFactory getSSLSocketFactory() private SSLSocketFactory getSSLSocketFactory()
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException { throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {
CertificateFactory cf = CertificateFactory.getInstance("X.509"); CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getResources().openRawResource(R.raw.smile_uat); // this cert file stored in \app\src\main\res\raw folder path InputStream caInput = context.getResources().openRawResource(R.raw.uat_test); // this cert file stored in \app\src\main\res\raw folder path
Certificate ca = cf.generateCertificate(caInput); Certificate ca = cf.generateCertificate(caInput);
caInput.close(); caInput.close();
KeyStore keyStore = KeyStore.getInstance("BKS"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // BKS
keyStore.load(null, null); keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca); keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore); tmf.init(keyStore);
@ -174,81 +179,118 @@ public class ISOSocket {
private OkHttpClient getClient() private OkHttpClient getClient()
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException { throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {
// CertificateFactory cf = CertificateFactory.getInstance("X.509");
// InputStream caInput = context.getResources().openRawResource(R.raw.uat_test); // this cert file stored in \app\src\main\res\raw folder path
//
//
// Certificate ca = cf.generateCertificate(caInput);
// caInput.close();
//
// KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// keyStore.load(null, null);
// keyStore.setCertificateEntry("ca", ca);
//
//
// TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
// TrustManagerFactory.getDefaultAlgorithm());
// trustManagerFactory.init(keyStore);
// TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
// if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
// throw new IllegalStateException("Unexpected default trust managers:"
// + Arrays.toString(trustManagers));
// }
// X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
// SSLContext sslContext = SSLContext.getInstance("TLS");
// sslContext.init(null, new TrustManager[]{trustManager}, null);
// SSLSocketFactory sslSocketFactory = getSSLSocketFactory();
//
// Interceptor dummyInterceptor = new Interceptor() {
// @NotNull
// @Override
// public Response intercept(@NotNull Chain chain) throws IOException {
// Request request = chain.request();
// Response response = chain.proceed(request);
// System.out.println("Dummy interceptor fired!\n\nRequest: " + request.headers() + "\nResponse: " + response.headers());
// return response;
// }
// };
// Interceptor dummyNetworkInterceptor = new Interceptor() {
// @NotNull
// @Override
// public Response intercept(@NotNull Chain chain) throws IOException {
// Request request = chain.request();
// Response response = chain.proceed(request);
// System.out.println("Dummy interceptor fired!\n\nRequest: " + request.headers() + "\nResponse: " + response.headers());
// return response;
// }
// };
//
// Interceptor tlsIntercept = new Interceptor() {
// @NotNull
// @Override
// public Response intercept(@NotNull Chain chain) throws IOException {
// final Response response = chain.proceed(chain.request());
// Handshake handshake = response.handshake();
// if (handshake != null) {
// final CipherSuite cipherSuite = handshake.cipherSuite();
// final TlsVersion tlsVersion = handshake.tlsVersion();
// LogUtil.d(Constant.TAG, "TLS: " + tlsVersion + ", CipherSuite: " + cipherSuite);
// }
// return response;
// }
// };
//
//
// return new OkHttpClient.Builder()
// .sslSocketFactory(sslSocketFactory, trustManager)
// .hostnameVerifier(new HostnameVerifier() {
// @Override
// public boolean verify(String hostname, SSLSession session) {
// return true;
// }
// })
// .addInterceptor(tlsIntercept)
// .connectTimeout(30000, TimeUnit.MILLISECONDS)
// .readTimeout(30000, TimeUnit.MILLISECONDS)
// .writeTimeout(30000, TimeUnit.MILLISECONDS)
// .retryOnConnectionFailure(true)
// .build();
// 1. Read the DER file into a byte array/InputStream
// (Assuming the file is bundled in your app's resources)
InputStream caInput = context.getResources().openRawResource(R.raw.uat_test); // Replace R.raw.cacert
// 2. Load the certificate from the InputStream
CertificateFactory cf = CertificateFactory.getInstance("X.509"); CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = context.getResources().openRawResource(R.raw.smile_uat); // this cert file stored in \app\src\main\res\raw folder path X509Certificate caCert = (X509Certificate) cf.generateCertificate(caInput);
String keyStoreType = KeyStore.getDefaultType();
Certificate ca = cf.generateCertificate(caInput); KeyStore keyStore = KeyStore.getInstance(keyStoreType);
caInput.close();
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(null, null); keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca); keyStore.setCertificateEntry("ca", caCert); // 'ca' is an alias
// 3. Create a TrustManager that uses your custom KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
TrustManager[] trustAll = new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAll, new SecureRandom());
SSLSocketFactory factory = ctx.getSocketFactory();
try (SSLSocket ssock = (SSLSocket) factory.createSocket()) {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
} }
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
SSLSocketFactory sslSocketFactory = getSSLSocketFactory();
Interceptor dummyInterceptor = new Interceptor() { OkHttpClient client = new OkHttpClient.Builder()
@NotNull .sslSocketFactory(ctx.getSocketFactory())
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
System.out.println("Dummy interceptor fired!\n\nRequest: " + request.headers() + "\nResponse: " + response.headers());
return response;
}
};
Interceptor dummyNetworkInterceptor = new Interceptor() {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
System.out.println("Dummy interceptor fired!\n\nRequest: " + request.headers() + "\nResponse: " + response.headers());
return response;
}
};
Interceptor tlsIntercept = new Interceptor() {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
final Response response = chain.proceed(chain.request());
Handshake handshake = response.handshake();
if (handshake != null) {
final CipherSuite cipherSuite = handshake.cipherSuite();
final TlsVersion tlsVersion = handshake.tlsVersion();
LogUtil.d(Constant.TAG, "TLS: " + tlsVersion + ", CipherSuite: " + cipherSuite);
}
return response;
}
};
return new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.addInterceptor(tlsIntercept)
.connectTimeout(30000, TimeUnit.MILLISECONDS)
.readTimeout(30000, TimeUnit.MILLISECONDS)
.writeTimeout(30000, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.build(); .build();
return client;
} }
public boolean isOnline() { public boolean isOnline() {
@ -333,6 +375,8 @@ public class ISOSocket {
serverPort = getSecondaryPort(); serverPort = getSecondaryPort();
} }
private String formatDate(Date d) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(d); }
public void enqueue(byte[] sendBuff, int sendLen, boolean reversalTrans, ISOCallback listener) { public void enqueue(byte[] sendBuff, int sendLen, boolean reversalTrans, ISOCallback listener) {
this.listener = listener; this.listener = listener;
@ -384,8 +428,54 @@ public class ISOSocket {
if (SystemParamsOperation.getInstance().isSslOn()) { if (SystemParamsOperation.getInstance().isSslOn()) {
try { try {
OkHttpClient client = getClient(); TrustManager[] trustAll = new TrustManager[]{new X509TrustManager() {
sslSocket = (SSLSocket) client.sslSocketFactory().createSocket(); public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAll, new SecureRandom());
SSLSocketFactory factory = ctx.getSocketFactory();
// try (SSLSocket sslsock = (SSLSocket) factory.createSocket()) {
//
// sslsock.setEnabledProtocols(new String[]{"TLSv1.2"});
//// while (!isSSLOnline(timeout,subscriber)) {
//// Thread.sleep(150);
//// subscriber.onError(new Throwable("java.net.SocketTimeoutException: connect timed out"));
//// }
//
// sslsock.setSoTimeout(60000);
// sslsock.connect(new InetSocketAddress(serverIP, serverPort), connectTimeout);
//
// sslsock.startHandshake();
//
// SSLSession session = sslsock.getSession();
//
// LogUtil.d(TAG, "SSL handshake success to " + serverIP + ":" + serverPort + "\n");
// LogUtil.d(TAG, "TLS handshake success to " + serverIP + ":" + serverPort + "\n");
// LogUtil.d(TAG, "TLS version: " + session.getProtocol() + "\n");
// LogUtil.d(TAG,"Cipher suite: " + session.getCipherSuite() + "\n");
//
// try {
// java.security.cert.Certificate[] chain = session.getPeerCertificates();
// if (chain != null && chain.length > 0) {
// LogUtil.d(TAG,"Peer certificates:\n");
// for (int i = 0; i < chain.length; i++) {
// X509Certificate x = (X509Certificate) chain[i];
// LogUtil.d(TAG, " [" + i + "] Subject: " + x.getSubjectX500Principal().getName() + "\n");
// LogUtil.d(TAG," Issuer: " + x.getIssuerX500Principal().getName() + "\n");
// LogUtil.d(TAG," Valid: " + formatDate(x.getNotBefore()) + " - " + formatDate(x.getNotAfter()) + "\n");
// }
// }
// } catch (Exception ignored) { }
// }
// OkHttpClient client = getClient();
sslSocket = (SSLSocket) factory.createSocket();
sslSocket.setEnabledProtocols(new String[]{"TLSv1.2"});
// sslSocket.setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});
// while (!isSSLOnline(timeout,subscriber)) { // while (!isSSLOnline(timeout,subscriber)) {
// Thread.sleep(150); // Thread.sleep(150);
// subscriber.onError(new Throwable("java.net.SocketTimeoutException: connect timed out")); // subscriber.onError(new Throwable("java.net.SocketTimeoutException: connect timed out"));
@ -394,6 +484,28 @@ public class ISOSocket {
sslSocket.setSoTimeout(60000); sslSocket.setSoTimeout(60000);
sslSocket.connect(new InetSocketAddress(serverIP, serverPort), connectTimeout); sslSocket.connect(new InetSocketAddress(serverIP, serverPort), connectTimeout);
sslSocket.startHandshake();
SSLSession session = sslSocket.getSession();
LogUtil.d(TAG, "SSL handshake success to " + serverIP + ":" + serverPort + "\n");
LogUtil.d(TAG, "TLS handshake success to " + serverIP + ":" + serverPort + "\n");
LogUtil.d(TAG, "TLS version: " + session.getProtocol() + "\n");
LogUtil.d(TAG,"Cipher suite: " + session.getCipherSuite() + "\n");
try {
java.security.cert.Certificate[] chain = session.getPeerCertificates();
if (chain != null && chain.length > 0) {
LogUtil.d(TAG,"Peer certificates:\n");
for (int i = 0; i < chain.length; i++) {
X509Certificate x = (X509Certificate) chain[i];
LogUtil.d(TAG, " [" + i + "] Subject: " + x.getSubjectX500Principal().getName() + "\n");
LogUtil.d(TAG," Issuer: " + x.getIssuerX500Principal().getName() + "\n");
LogUtil.d(TAG," Valid: " + formatDate(x.getNotBefore()) + " - " + formatDate(x.getNotAfter()) + "\n");
}
}
} catch (Exception ignored) { }
inputStream = sslSocket.getInputStream(); inputStream = sslSocket.getInputStream();
outputStream = sslSocket.getOutputStream(); outputStream = sslSocket.getOutputStream();
@ -421,7 +533,7 @@ public class ISOSocket {
subscriber.onComplete(); subscriber.onComplete();
} catch (CertificateException | KeyStoreException | IOException | } catch ( IOException |
NoSuchAlgorithmException | KeyManagementException e) { NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace(); e.printStackTrace();
subscriber.onError(e); subscriber.onError(e);
@ -495,7 +607,9 @@ public class ISOSocket {
@Override @Override
public void onNext(byte[] aByte) { public void onNext(byte[] aByte) {
LogUtil.d(Constant.TAG, "onNext"); LogUtil.d(Constant.TAG, "onNext raw:"+ByteUtil.bytes2HexStr(aByte));
//rlen =47 MPU 5:44pm 21/9/2020 //rlen =47 MPU 5:44pm 21/9/2020
listener.onReceive(recBuff, rLen); listener.onReceive(recBuff, rLen);
isReversal = false; isReversal = false;

View File

@ -706,7 +706,7 @@ public abstract class BaseXPrint {
String cvmText = "NO SIGNATURE REQUIRED"; String cvmText = "NO SIGNATURE REQUIRED";
switch (payDetail.getTransCVM()) { switch (payDetail.getTransCVM()) {
case SIGNATURE: case SIGNATURE:
cvmText = "SIGN : -----------------------------"; cvmText = "SIGN : --------------";
break; break;
case ONLINE_PIN: case ONLINE_PIN:
cvmText = "ONLINE PIN VERIFIED"; cvmText = "ONLINE PIN VERIFIED";

View File

@ -40,7 +40,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
SimpleDateFormat dfm = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault()); SimpleDateFormat dfm = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault());
currentTime = dfm.format(new Date()); currentTime = dfm.format(new Date());
BitmapFactory.Options opts = new BitmapFactory.Options(); BitmapFactory.Options opts = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeResource(resources, R.drawable.print_kbz_logo, opts); bitmap = BitmapFactory.decodeResource(resources, R.drawable.print_logo_mpu, opts);
} }
@Override @Override

View File

@ -49,7 +49,7 @@ public class ReversalAction {
} }
private ReversalAction() { private ReversalAction() {
isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.BOTH_HEADER_TPDU,HostName.BPC) isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.ONLY_HEADER,HostName.FINEXUS)
.build(); .build();
} }

View File

@ -47,7 +47,7 @@ public class EchoTestProcess {
private EchoTestProcess() { private EchoTestProcess() {
tradeData = Params.newTrade(true); tradeData = Params.newTrade(true);
payDetail = tradeData.getPayDetail(); payDetail = tradeData.getPayDetail();
isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.BOTH_HEADER_TPDU,HostName.BPC) isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.ONLY_HEADER,HostName.FINEXUS)
.build(); .build();
} }

View File

@ -51,7 +51,7 @@ public class SignOnProcess {
private SignOnProcess() { private SignOnProcess() {
tradeData = Params.newTrade(true); tradeData = Params.newTrade(true);
// payDetail = tradeData.getPayDetail(); // payDetail = tradeData.getPayDetail();
isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.BOTH_HEADER_TPDU,HostName.BPC) isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.ONLY_HEADER,HostName.FINEXUS)
.build(); .build();
} }
@ -84,7 +84,7 @@ public class SignOnProcess {
} }
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, BitmapConfig.BPC_SIGN_ON, MessageType.NETWORK_MANAGEMENT); byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, BitmapConfig.MPU_SIGN_ON, MessageType.NETWORK_MANAGEMENT);
ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length,false, new ISOCallback() { ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length,false, new ISOCallback() {
@Override @Override
public void onReceive(byte[] bytes, int length) { public void onReceive(byte[] bytes, int length) {
@ -107,7 +107,9 @@ public class SignOnProcess {
byte[] field62 = Objects.requireNonNull(responseMap.get("F062")).getDataBytes(); byte[] field62 = Objects.requireNonNull(responseMap.get("F062")).getDataBytes();
byte[] encryptedPIK = new byte[16]; byte[] encryptedPIK = new byte[16];
System.arraycopy(field62, 2, encryptedPIK, 0, 16); System.arraycopy(field62, 0, encryptedPIK, 0, 8);
System.arraycopy(field62, 0, encryptedPIK, 8, 8);
// System.arraycopy(field62, 0, encryptedPIK, 0, 16);
try { try {
byte[] kcv = ByteUtil.hexStr2Bytes(TriDes.getKcv(encryptedPIK)); byte[] kcv = ByteUtil.hexStr2Bytes(TriDes.getKcv(encryptedPIK));
@ -118,6 +120,9 @@ public class SignOnProcess {
tmkIndex = Integer.parseInt(SystemParamsOperation.getInstance().getTMKIndex()); tmkIndex = Integer.parseInt(SystemParamsOperation.getInstance().getTMKIndex());
} }
LogUtil.d(TAG, "TMK Index:" + tmkIndex); LogUtil.d(TAG, "TMK Index:" + tmkIndex);
SystemParamsOperation.getInstance().saveKeyPIK(encryptedPIK, kcv);
int res = PayLibNex.getInstance().deviceEngine.getPinPad().writeWKey(9, WorkKeyTypeEnum.PINKEY,encryptedPIK,encryptedPIK.length); int res = PayLibNex.getInstance().deviceEngine.getPinPad().writeWKey(9, WorkKeyTypeEnum.PINKEY,encryptedPIK,encryptedPIK.length);
// int res = PayLibsUtils.getInstance().securityOptV2.saveCiphertextKey(AidlConstantsV2.Security.KEY_TYPE_PIK, encryptedPIK, null, tmkIndex, AidlConstantsV2.Security.KEY_ALG_TYPE_3DES, 11); // int res = PayLibsUtils.getInstance().securityOptV2.saveCiphertextKey(AidlConstantsV2.Security.KEY_TYPE_PIK, encryptedPIK, null, tmkIndex, AidlConstantsV2.Security.KEY_ALG_TYPE_3DES, 11);
// resultCode = res; // resultCode = res;

View File

@ -82,7 +82,7 @@ public class TransactionsOperation {
TradeData transTradeData; TradeData transTradeData;
PayDetail transPayDetail; PayDetail transPayDetail;
isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.BOTH_HEADER_TPDU, hostName) isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1987, ISOMode.ONLY_HEADER, hostName)
.build(); .build();
if (transactionsType == TransactionsType.SALE || transactionsType == TransactionsType.PRE_AUTH_SALE || transactionsType == TransactionsType.CASH_OUT) { if (transactionsType == TransactionsType.SALE || transactionsType == TransactionsType.PRE_AUTH_SALE || transactionsType == TransactionsType.CASH_OUT) {
@ -102,7 +102,6 @@ public class TransactionsOperation {
return; return;
} }
transPayDetail.setSendBag(sendBytes); transPayDetail.setSendBag(sendBytes);
@ -349,7 +348,13 @@ public class TransactionsOperation {
if (transactionsType == TransactionsType.VOID || transactionsType == TransactionsType.REFUND) { if (transactionsType == TransactionsType.VOID || transactionsType == TransactionsType.REFUND) {
String field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount()); String field60;
if(hostName == HostName.BPC) {
field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount());
} else {
field60 = String.format(Locale.getDefault(), "%012d", oldPay.getAmount());
}
// String field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount());
newPay.setReferNo(oldPay.getReferNo()); newPay.setReferNo(oldPay.getReferNo());
newPay.setTransCVM(TransCVM.SIGNATURE); newPay.setTransCVM(TransCVM.SIGNATURE);
newTrade.setField60(field60); newTrade.setField60(field60);

View File

@ -12,16 +12,17 @@ import com.utsmyanmar.paylibs.model.MAGCardInfo;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData; import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.utils.CardSchemeUtils; import com.utsmyanmar.paylibs.utils.CardSchemeUtils;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.TLV; import com.utsmyanmar.paylibs.utils.TLV;
import com.utsmyanmar.paylibs.utils.enums.CardScheme; import com.utsmyanmar.paylibs.utils.enums.CardScheme;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import com.utsmyanmar.paylibs.utils.LogUtil;
public class KernelDataProcessUtil { public class KernelDataProcessUtil {
@ -32,7 +33,7 @@ public class KernelDataProcessUtil {
Map<String, TLV> tlvMap = TLVUtil.buildTLVMap(data); Map<String, TLV> tlvMap = TLVUtil.buildTLVMap(data);
com.sunmi.pay.hardware.aidl.bean.CardInfo cardInfo = new com.sunmi.pay.hardware.aidl.bean.CardInfo(); com.sunmi.pay.hardware.aidl.bean.CardInfo cardInfo = new com.sunmi.pay.hardware.aidl.bean.CardInfo();
LogUtil.d(TAG,"EMV Track data : "+ByteUtil.bytes2HexStr(data)); LogUtil.d(TAG, "EMV Track data : " + ByteUtil.bytes2HexStr(data));
// Two magnetic // Two magnetic
TLV tlv = tlvMap.get("57"); TLV tlv = tlvMap.get("57");
@ -40,10 +41,10 @@ public class KernelDataProcessUtil {
String value = tlv.getValue().toUpperCase(); String value = tlv.getValue().toUpperCase();
cardInfo = Track2Util.parseTrack2(value); cardInfo = Track2Util.parseTrack2(value);
// cardInfo.track2=value; // cardInfo.track2=value;
LogUtil.d(TAG,"57 track:"+value); LogUtil.d(TAG, "57 track:" + value);
cardInfo.track2 = value.replaceAll("F", ""); // Edited "0" with "" 20,Nov,2024 cardInfo.track2 = value.replaceAll("F", ""); // Edited "0" with "" 20,Nov,2024
LogUtil.d(TAG,"57 track:"+cardInfo.track2); LogUtil.d(TAG, "57 track:" + cardInfo.track2);
} }
// IC card obtains the card number and expiration date through 5A and 5F24 // IC card obtains the card number and expiration date through 5A and 5F24
if (AidlConstants.CardType.IC.getValue() == cardType) { if (AidlConstants.CardType.IC.getValue() == cardType) {
@ -67,7 +68,6 @@ public class KernelDataProcessUtil {
} }
LogUtil.d(Constant.TAG, "The card is valid for" + value); LogUtil.d(Constant.TAG, "The card is valid for" + value);
} }
} }
// Card serial number // Card serial number
@ -94,7 +94,7 @@ public class KernelDataProcessUtil {
cardInfo = Track2Util.parseTrack2(value); cardInfo = Track2Util.parseTrack2(value);
cardInfo.track2 = value.replaceAll("F", "0"); // edited " " with "0" cardInfo.track2 = value.replaceAll("F", "0"); // edited " " with "0"
LogUtil.d(TAG,"9F6B track:"+cardInfo.track2); LogUtil.d(TAG, "9F6B track:" + cardInfo.track2);
} }
cardInfo.cardType = cardType; cardInfo.cardType = cardType;
@ -125,7 +125,11 @@ public class KernelDataProcessUtil {
if (track2 != null && track2.length() > 0) { if (track2 != null && track2.length() > 0) {
magCardInfo.setTrack2(track2); magCardInfo.setTrack2(track2);
String track2Encrypt = TrackEncrypt.trackEncrypt(track2); String track2Encrypt = TrackEncrypt.trackEncrypt(track2);
track2Encrypt = track2Encrypt.replace("=","D"); // /*
// * comment this line cuz mastercard network validation is getting failure
// * */
track2Encrypt = track2Encrypt.replace("=", "D");
magCardInfo.setTrack2Cipher(track2Encrypt); magCardInfo.setTrack2Cipher(track2Encrypt);
} }
@ -231,7 +235,7 @@ public class KernelDataProcessUtil {
// if(value.equals("554E494F4E504159204445424954")){ // if(value.equals("554E494F4E504159204445424954")){
// payDetail.setCardHolderName("UNIONPAY"); // payDetail.setCardHolderName("UNIONPAY");
// }else{ // }else{
LogUtil.d(TAG,"App Label 50 : "+Utils.hexStr2AsciiStr(value)); LogUtil.d(TAG, "App Label 50 : " + Utils.hexStr2AsciiStr(value));
icCardInfo.setAppLabel(Utils.hexStr2AsciiStr(value)); icCardInfo.setAppLabel(Utils.hexStr2AsciiStr(value));
payDetail.setAppLabel(Utils.hexStr2AsciiStr(value)); payDetail.setAppLabel(Utils.hexStr2AsciiStr(value));
@ -246,7 +250,7 @@ public class KernelDataProcessUtil {
String value = tlv.getValue(); String value = tlv.getValue();
icCardInfo.setAppName(value); icCardInfo.setAppName(value);
payDetail.setAppName(value); payDetail.setAppName(value);
LogUtil.d(TAG,"App Name : "+value); LogUtil.d(TAG, "App Name : " + value);
} }
// TVR Terminal verification result // TVR Terminal verification result
@ -530,14 +534,22 @@ public class KernelDataProcessUtil {
// Cardholder verification method result CVM // Cardholder verification method result CVM
if (tlvMap.get("9F34") != null) { if (tlvMap.get("9F34") != null) {
f55DataStr += "9F3403" + tlvMap.get("9F34").getValue(); f55DataStr += "9F3403" + tlvMap.get("9F34").getValue();
if(tlvMap.get("9F34").getValue() != null && !tlvMap.get("9F34").getValue().isEmpty()) { if (tlvMap.get("9F34").getValue() != null && !tlvMap.get("9F34").getValue().isEmpty()) {
f55WaveDataStr += "9F3403" + tlvMap.get("9F34").getValue(); f55WaveDataStr += "9F3403" + tlvMap.get("9F34").getValue();
} }
// f55WaveDataStr += "9F3403020200" ; // f55WaveDataStr += "9F3403020200" ;
} }
// f55DataStr += "9F34031F0302"; // f55DataStr += "9F34031F0302";
// TSI Transaction status information
if (tlvMap.get("9B") != null && !tlvMap.get("9B").getValue().isEmpty()) {
f55DataStr += "9B02" + tlvMap.get("9B").getValue();
f55WaveDataStr += "9B02" + tlvMap.get("9B").getValue();
}
// terminal type // terminal type
if (tlvMap.get("9F35") != null) { if (tlvMap.get("9F35") != null) {
f55DataStr += "9F3501" + tlvMap.get("9F35").getValue(); f55DataStr += "9F3501" + tlvMap.get("9F35").getValue();
@ -563,8 +575,8 @@ public class KernelDataProcessUtil {
// need to keeps or not? // need to keeps or not?
if (tlvMap.get("9F41") != null) { if (tlvMap.get("9F41") != null) {
int length = tlvMap.get("9F41").getLength(); int length = tlvMap.get("9F41").getLength();
f55DataStr += "9F41" + String.format("%02d", length-1) + tlvMap.get("9F41").getValue().substring(2,8); f55DataStr += "9F41" + String.format("%02d", length - 1) + tlvMap.get("9F41").getValue().substring(2, 8);
f55WaveDataStr += "9F41" + String.format("%02d", length-1) + tlvMap.get("9F41").getValue().substring(2,8); f55WaveDataStr += "9F41" + String.format("%02d", length - 1) + tlvMap.get("9F41").getValue().substring(2, 8);
} else { } else {
String batchNumStart = SystemParamsOperation.getInstance().getSystemParamsSettings().getBatchNumStart(); String batchNumStart = SystemParamsOperation.getInstance().getSystemParamsSettings().getBatchNumStart();
try { try {
@ -576,29 +588,15 @@ public class KernelDataProcessUtil {
} }
} }
/* if (tlvMap.get("4F") != null) {
tlv = tlvMap.get("4F");
String value = tlv.getValue();
int length = tlv.getLength();
String len = getHexLen(length);
f55DataStr += "4F" + len + value;
}
if (tlvMap.get("9F08") != null) {
tlv = tlvMap.get("9F08");
String value = tlv.getValue();
int length = tlv.getLength();
String len = getHexLen(length);
f55DataStr += "9F08" + len + value;
}*/
/* /*
* merchant category code * merchant category code
* */ * */
// disabled at 18/ July 2025 if (tlvMap.get("9F53") != null) {
// if(tlvMap.get("9F53") !=null){ f55DataStr += "9F5301" + tlvMap.get("9F53").getValue();
// f55DataStr += "9F5301"+ tlvMap.get("9F53").getValue(); } else {
// } f55DataStr += "9F530150";
}
// else{ // else{
//// f55DataStr += "9F530152"; //// f55DataStr += "9F530152";
@ -659,53 +657,62 @@ public class KernelDataProcessUtil {
if (cardType == AidlConstants.CardType.IC.getValue()) { if (cardType == AidlConstants.CardType.IC.getValue()) {
icCardInfo.setICC55(f55DataStr); icCardInfo.setICC55(f55DataStr);
payDetail.setICC55(f55DataStr); payDetail.setICC55(f55DataStr);
LogUtil.d(TAG,"DE55 IC: "+f55DataStr); LogUtil.d(TAG, "DE55 IC: " + f55DataStr);
} else { } else {
String[] tagList = { String[] tagList = {
"9F6E" "9F6E"
}; };
String hexStr = ""; String hexStr = "";
byte[] dataOut = new byte[1024];
try { if (tlvMap.get("9F6E") != null) {
int len = PayLibsUtils.getInstance().emvOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYWAVE, tagList, dataOut); int length = tlvMap.get("9F6E").getLength();
if (len > 0) { String len = getHexLen(length);
byte[] dataOutBytes = Arrays.copyOf(dataOut, len); f55WaveDataStr += "9F6E" + len + tlvMap.get("9F6E").getValue();
hexStr = ByteUtil.bytes2HexStr(dataOutBytes);
LogUtil.d(TAG, "get 9F6E Data :" + hexStr);
Map<String, TLV> map = TLVUtil.buildTLVMap(hexStr);
LogUtil.d(TAG, "get 9F6E Data :" + map);
} else {
LogUtil.e(TAG, "Get the data length of payWave is negative = " + len);
}
} catch (Exception e) {
e.printStackTrace();
} }
if (hexStr.equals("9F6E00")) { LogUtil.d(TAG,"9F6E data :"+hexStr);
try { // byte[] dataOut = new byte[1024];
int len = PayLibsUtils.getInstance().emvOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagList, dataOut); //
if (len > 0) { // try {
byte[] dataOutBytes = Arrays.copyOf(dataOut, len); // int len = PayLibsUtils.getInstance().emvOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYWAVE, tagList, dataOut);
hexStr = ByteUtil.bytes2HexStr(dataOutBytes); // if (len > 0) {
LogUtil.d(TAG, "get 9F6E Data :" + hexStr); // byte[] dataOutBytes = Arrays.copyOf(dataOut, len);
Map<String, TLV> map = TLVUtil.buildTLVMap(hexStr); // hexStr = ByteUtil.bytes2HexStr(dataOutBytes);
LogUtil.d(TAG, "get 9F6E Data :" + map); // LogUtil.d(TAG, "get 9F6E Data :" + hexStr);
// Map<String, TLV> map = TLVUtil.buildTLVMap(hexStr);
} else { // LogUtil.d(TAG, "get 9F6E Data :" + map);
LogUtil.e(TAG, "Get the data length of payPass is negative = " + len); //
} // } else {
} catch (Exception e) { // LogUtil.e(TAG, "Get the data length of payWave is negative = " + len);
e.printStackTrace(); // }
} // } catch (Exception e) {
} // e.printStackTrace();
if(!hexStr.equals("9F6E00")) { // }
f55WaveDataStr += hexStr; //
} // if (hexStr.equals("9F6E00")) {
// try {
// int len = PayLibsUtils.getInstance().emvOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagList, dataOut);
// if (len > 0) {
// byte[] dataOutBytes = Arrays.copyOf(dataOut, len);
// hexStr = ByteUtil.bytes2HexStr(dataOutBytes);
// LogUtil.d(TAG, "get 9F6E Data :" + hexStr);
// Map<String, TLV> map = TLVUtil.buildTLVMap(hexStr);
// LogUtil.d(TAG, "get 9F6E Data :" + map);
//
// } else {
// LogUtil.e(TAG, "Get the data length of payPass is negative = " + len);
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// if (!hexStr.equals("9F6E00")) {
// f55WaveDataStr += hexStr;
// }
icCardInfo.setICC55(f55WaveDataStr); icCardInfo.setICC55(f55WaveDataStr);
payDetail.setICC55(f55WaveDataStr); payDetail.setICC55(f55WaveDataStr);
LogUtil.d(TAG,"DE55 kpay: "+f55WaveDataStr); LogUtil.d(TAG, "DE55 wave: " + f55WaveDataStr);
} }

View File

@ -395,9 +395,9 @@ public class SystemParamsOperation {
saveSystemParamsSettings(params); saveSystemParamsSettings(params);
} }
public void setWavePayInquiryStatus(boolean status) { public void setQRPayInquiryStatus(boolean status) {
SystemParamsSettings params = getSystemParamsSettings(); SystemParamsSettings params = getSystemParamsSettings();
params.setWavePayInquiryStatus(status); params.setQRPayInquiryStatus(status);
saveSystemParamsSettings(params); saveSystemParamsSettings(params);
} }
@ -423,9 +423,9 @@ public class SystemParamsOperation {
saveSystemParamsSettings(params); saveSystemParamsSettings(params);
} }
public boolean getWavePayInquiryStatus() { public boolean getQRPayInquiryStatus() {
SystemParamsSettings params = getSystemParamsSettings(); SystemParamsSettings params = getSystemParamsSettings();
return params.getWavePayInquiryStatus(); return params.getQRPayInquiryStatus();
} }
public void setNfcEnabled(boolean status) { public void setNfcEnabled(boolean status) {
@ -478,9 +478,9 @@ public class SystemParamsOperation {
return params.isFallbackEnabled(); return params.isFallbackEnabled();
} }
public void setWavePayStatus(boolean status) { public void setQRPayStatus(boolean status) {
SystemParamsSettings params = getSystemParamsSettings(); SystemParamsSettings params = getSystemParamsSettings();
params.setWavePayStatus(status); params.setQRPayStatus(status);
saveSystemParamsSettings(params); saveSystemParamsSettings(params);
} }
@ -494,9 +494,9 @@ public class SystemParamsOperation {
SystemParamsSettings params = getSystemParamsSettings(); SystemParamsSettings params = getSystemParamsSettings();
return params.isDecimal_enable(); return params.isDecimal_enable();
} }
public boolean getWavePayStatus() { public boolean getQRPayStatus() {
SystemParamsSettings params = getSystemParamsSettings(); SystemParamsSettings params = getSystemParamsSettings();
return params.getWavePayStatus(); return params.getQRPayStatus();
} }
public boolean getTipsAdjustmentStatus() { public boolean getTipsAdjustmentStatus() {
@ -701,6 +701,28 @@ public class SystemParamsOperation {
return params.isEmvEnabled(); return params.isEmvEnabled();
} }
public String getAppId() {
SystemParamsSettings params = getSystemParamsSettings();
return params.getAppId();
}
public void setAppId(String appId) {
SystemParamsSettings params = getSystemParamsSettings();
params.setAppId(appId);
saveSystemParamsSettings(params);
}
public String getAppKey() {
SystemParamsSettings params = getSystemParamsSettings();
return params.getAppKey();
}
public void setAppKey(String appKey) {
SystemParamsSettings params = getSystemParamsSettings();
params.setAppKey(appKey);
saveSystemParamsSettings(params);
}
public String getComp1(){ public String getComp1(){
SystemParamsSettings params = getSystemParamsSettings(); SystemParamsSettings params = getSystemParamsSettings();
return params.getComp1(); return params.getComp1();

View File

@ -41,7 +41,8 @@ public class SystemParamsSettings implements Serializable {
private boolean checkExpSwitch = false; private boolean checkExpSwitch = false;
// private String tmsAddress = "https://tms.smile-mm.com"; // private String tmsAddress = "https://tms.smile-mm.com";
private String tmsAddress = "http://128.199.170.203"; // private String tmsAddress = "http://128.199.170.203";
private String tmsAddress = "http://sirius-nest.utsmyanmar.com";
private String terminalCapability = "E0E8C8"; private String terminalCapability = "E0E8C8";
@ -129,6 +130,10 @@ public class SystemParamsSettings implements Serializable {
private String lastSuccessTranx = "000001"; private String lastSuccessTranx = "000001";
private String appId = "";
private String appKey = "";
private int fallbackCounter = 2; private int fallbackCounter = 2;
private long jCbCVMLimit = 75000; private long jCbCVMLimit = 75000;
@ -177,7 +182,7 @@ public class SystemParamsSettings implements Serializable {
private boolean cvv_bypass_status = false; private boolean cvv_bypass_status = false;
private boolean wave_pay_inquiry_status = true; private boolean qrpay_inquiry_status = true;
private boolean tips_adjustment_status = true; private boolean tips_adjustment_status = true;
@ -187,7 +192,7 @@ public class SystemParamsSettings implements Serializable {
private boolean refund_status = true; private boolean refund_status = true;
private boolean wave_status = true; private boolean qrpay_status = true;
private boolean full_void_preauth_status = true; private boolean full_void_preauth_status = true;
@ -285,6 +290,22 @@ public class SystemParamsSettings implements Serializable {
this.disabledMessage = disabledMessage; this.disabledMessage = disabledMessage;
} }
public void setAppId(String appId) {
this.appId = appId;
}
public void setAppKey(String appKey) {
this.appKey = appKey;
}
public String getAppId() {
return appId;
}
public String getAppKey() {
return appKey;
}
protected void setjCbCVMLimit(long amount) { protected void setjCbCVMLimit(long amount) {
this.jCbCVMLimit = amount; this.jCbCVMLimit = amount;
} }
@ -369,8 +390,8 @@ public class SystemParamsSettings implements Serializable {
return ecrKey; return ecrKey;
} }
protected void setWavePayInquiryStatus(boolean status) { protected void setQRPayInquiryStatus(boolean status) {
this.wave_pay_inquiry_status = status; this.qrpay_inquiry_status = status;
} }
protected String getTerminalCapability() { return terminalCapability;} protected String getTerminalCapability() { return terminalCapability;}
@ -403,11 +424,11 @@ public class SystemParamsSettings implements Serializable {
protected void setRandomPinPad(boolean randomPinPad) { this.isRandomPinPad = randomPinPad;} protected void setRandomPinPad(boolean randomPinPad) { this.isRandomPinPad = randomPinPad;}
protected boolean isRandomPinPad() { return isRandomPinPad; } protected boolean isRandomPinPad() { return isRandomPinPad; }
protected void setWavePayStatus(boolean status) { protected void setQRPayStatus(boolean status) {
this.wave_status = status; this.qrpay_status = status;
} }
protected boolean getWavePayStatus() { return wave_status; } protected boolean getQRPayStatus() { return qrpay_status; }
protected boolean isAutoPrintCustomerCopy() { protected boolean isAutoPrintCustomerCopy() {
return isAutoPrintCustomerCopy; return isAutoPrintCustomerCopy;
@ -415,8 +436,8 @@ public class SystemParamsSettings implements Serializable {
protected void setAutoPrintCustomerCopy(boolean flag) { this.isAutoPrintCustomerCopy = flag; } protected void setAutoPrintCustomerCopy(boolean flag) { this.isAutoPrintCustomerCopy = flag; }
protected boolean getWavePayInquiryStatus() { protected boolean getQRPayInquiryStatus() {
return wave_pay_inquiry_status; return qrpay_inquiry_status;
} }
protected void setTipsAdjustmentStatus(boolean status) { protected void setTipsAdjustmentStatus(boolean status) {

View File

@ -12,6 +12,8 @@ public enum TransMenu {
REVERSAL, REVERSAL,
CASH_OUT,
CLEAR_BATCH, CLEAR_BATCH,
CLEAR_REVERSAL, CLEAR_REVERSAL,
SUMMARY_REPORT, SUMMARY_REPORT,

Some files were not shown because too many files have changed in this diff Show More