diff --git a/.idea/misc.xml b/.idea/misc.xml index 991a888..74dd639 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 19a4a97..cdbb89a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,8 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.compose) + id("com.google.devtools.ksp") + id("com.google.dagger.hilt.android") } android { @@ -44,10 +46,12 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.runtime) implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.ui.graphics) implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.core.ktx) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.navigation.compose) testImplementation(libs.junit) @@ -57,4 +61,18 @@ dependencies { androidTestImplementation(libs.androidx.junit) debugImplementation(libs.androidx.compose.ui.test.manifest) debugImplementation(libs.androidx.compose.ui.tooling) -} \ No newline at end of file + implementation("com.google.dagger:hilt-android:2.59.2") + ksp(libs.hilt.android.compiler) + implementation(libs.rxjava) + implementation(libs.rxandroid) + // local libs + implementation(project(":baselib")) + implementation(project(":mpulib")) + implementation(project(":paylibs")) + implementation(project(":paysdk-lib")) + implementation(project(":qrgen-lib")) + implementation(project(":sunmiui-lib")) + implementation(project(":ecr")) + implementation(project(":xpay")) + implementation(project(":cmhl")) +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bee6fe..72b5544 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,32 @@ + + + + + + + + + + + + + + + + + + + @@ -24,4 +49,4 @@ - \ No newline at end of file + diff --git a/app/src/main/java/com/mob/utsmyanmar/MainActivity.kt b/app/src/main/java/com/mob/utsmyanmar/MainActivity.kt index eac96a8..175edf1 100644 --- a/app/src/main/java/com/mob/utsmyanmar/MainActivity.kt +++ b/app/src/main/java/com/mob/utsmyanmar/MainActivity.kt @@ -7,7 +7,9 @@ import androidx.activity.enableEdgeToEdge import androidx.navigation.compose.rememberNavController import com.mob.utsmyanmar.ui.navigation.AppNavGraph import com.mob.utsmyanmar.ui.theme.MOBPOSTheme +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/mob/utsmyanmar/MyApplication.kt b/app/src/main/java/com/mob/utsmyanmar/MyApplication.kt new file mode 100644 index 0000000..d9dd889 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/MyApplication.kt @@ -0,0 +1,13 @@ +package com.mob.utsmyanmar + +import com.mob.utsmyanmar.utils.AppContextHolder +import com.utsmyanmar.baselib.BaseApplication +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class MyApplication : BaseApplication() { + override fun onCreate() { + super.onCreate() + AppContextHolder.init(this) + } +} diff --git a/app/src/main/java/com/mob/utsmyanmar/model/CardTransactionType.kt b/app/src/main/java/com/mob/utsmyanmar/model/CardTransactionType.kt new file mode 100644 index 0000000..dc45ff9 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/model/CardTransactionType.kt @@ -0,0 +1,5 @@ +package com.mob.utsmyanmar.model + +enum class CardTransactionType { + MPU, EMV, MAG, FALLBACK +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/model/ProcessCode.kt b/app/src/main/java/com/mob/utsmyanmar/model/ProcessCode.kt new file mode 100644 index 0000000..dd257f5 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/model/ProcessCode.kt @@ -0,0 +1,39 @@ +package com.mob.utsmyanmar.model + +object ProcessCode { + const val BALANCE_INQUIRY = "31" + + const val SALE_PURCHASE = "00" + + const val SALE_VOID = "02" + + const val PRE_AUTH_SALE = "30" + + const val PRE_AUTH_VOID = "30" + + const val PRE_AUTH_COMPLETE = "30" + + const val PRE_AUTH_COMPLETE_VOID = "30" + + const val CASH_ADVANCE = "01" // 01-MPU 17-TTIP + + const val FUND_TRANSFER = "61" + + const val PIN_CHANGE = "70" + + const val SETTLEMENT = "92" + + const val SIGN_ON = "95" + + const val CASH_DEPOSIT = "21" + + const val REFUND = "20" + + const val SMART = "00" + + const val SAVING = "10" + + const val CURRENT = "20" + + const val TO_ACCOUNT = "00" +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/model/SettlementType.kt b/app/src/main/java/com/mob/utsmyanmar/model/SettlementType.kt new file mode 100644 index 0000000..5501a2e --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/model/SettlementType.kt @@ -0,0 +1,6 @@ +package com.mob.utsmyanmar.model + +enum class SettlementType { + NORMAL, + CUT_OVER +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/model/TransResultStatus.kt b/app/src/main/java/com/mob/utsmyanmar/model/TransResultStatus.kt new file mode 100644 index 0000000..1ac3aaf --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/model/TransResultStatus.kt @@ -0,0 +1,29 @@ +package com.mob.utsmyanmar.model + +enum class TransResultStatus { + CLICK_CONFIRM, + SUCCESS, + FAIL, + OFFLINE_SUCCESS, + OFFLINE_FAILURE, + SECONDARY, + REVERSAL_PROCESS, + REVERSAL_PREPARE, + REVERSAL_SECONDARY, + REVERSAL_THIRD, + REVERSAL_FAIL, + REVERSAL_SUCCESS, + BEFORE_REVERSAL, + PIN_PAD_CANCEL, + PIN_PAD_CONFIRM, + PIN_PAD_ERROR, + PIN_MISMATCH, + PIN_MISMATCH_END, + REMOVED_CARD, + EMV_ERROR, + ERROR, + RETRY_AGAIN, + NEXT_SCREEN, + EMPTY_PIN, + NETWORK_ERROR +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/model/ecr/ECRResultStatus.kt b/app/src/main/java/com/mob/utsmyanmar/model/ecr/ECRResultStatus.kt new file mode 100644 index 0000000..626966c --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/model/ecr/ECRResultStatus.kt @@ -0,0 +1,7 @@ +package com.mob.utsmyanmar.model.ecr + +enum class ECRResultStatus { + USER_CANCEL, + TIME_OUT, + RESPONSE_RECEIVED +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountScreen.kt b/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountScreen.kt index daa1296..7ec23c6 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountScreen.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountScreen.kt @@ -137,7 +137,7 @@ fun AmountScreen( ) ) { Text( - text = "Next", + text = "Enter", fontSize = 20.sp, fontWeight = FontWeight.Bold ) diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingEvent.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingEvent.kt new file mode 100644 index 0000000..6eca267 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingEvent.kt @@ -0,0 +1,9 @@ +package com.mob.utsmyanmar.ui.cardwaiting + +sealed interface CardWaitingEvent { + data object GoManualEntry : CardWaitingEvent + data object GoProcessingCard : CardWaitingEvent + data object GoTimeout : CardWaitingEvent + data object GoMain : CardWaitingEvent + data object GoBack : CardWaitingEvent +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt new file mode 100644 index 0000000..6d41a0a --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt @@ -0,0 +1,93 @@ +package com.mob.utsmyanmar.ui.cardwaiting + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.mob.utsmyanmar.ui.theme.White + +@Composable +fun CardWaitingScreen( + viewModel: CardWaitingViewModel, + onManualEntry: () -> Unit, + onProcessingCard: () -> Unit, + onTimeout: () -> Unit, + onBack: () -> Unit, + onMain: () -> Unit +) { + val uiState by viewModel.uiState.collectAsState() + + LaunchedEffect(viewModel) { + viewModel.events.collect { event -> + when (event) { + CardWaitingEvent.GoManualEntry -> onManualEntry() + CardWaitingEvent.GoProcessingCard -> onProcessingCard() + CardWaitingEvent.GoTimeout -> onTimeout() + CardWaitingEvent.GoMain -> onMain() + CardWaitingEvent.GoBack -> onBack() + } + } + } + + LaunchedEffect(viewModel) { + viewModel.onScreenResume() + } + + DisposableEffect(viewModel) { + onDispose { + viewModel.onScreenPause() + } + } + + BackHandler { + viewModel.onBackPressed() + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(White) + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Card Capture", + fontSize = 22.sp, + fontWeight = FontWeight.Bold + ) + + Spacer(modifier = Modifier.height(40.dp)) + + Text( + text = uiState.alertMessage, + fontSize = 20.sp, + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.weight(1f)) + + Button( + onClick = viewModel::onManualEntryClick, + modifier = Modifier.fillMaxWidth() + ) { + Text("Manual Entry") + } + } +} diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt new file mode 100644 index 0000000..3739754 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt @@ -0,0 +1,7 @@ +package com.mob.utsmyanmar.ui.cardwaiting + +data class CardWaitingUiState( + val alertMessage: String = "Please insert, tap, or swipe card", + val isLoading: Boolean = false, + val isFallback: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt new file mode 100644 index 0000000..3f63ef9 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt @@ -0,0 +1,293 @@ +package com.mob.utsmyanmar.ui.cardwaiting + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.mob.utsmyanmar.model.CardTransactionType +import com.mob.utsmyanmar.model.ecr.ECRResultStatus +import com.mob.utsmyanmar.utils.CoreUtils +import com.mob.utsmyanmar.viewmodel.CardReaderViewModel +import com.mob.utsmyanmar.viewmodel.SharedViewModel +import com.sunmi.pay.hardware.aidl.AidlConstants +import com.utsmyanmar.checkxread.checkcard.CheckCardResultX +import com.utsmyanmar.checkxread.util.CardTypeX +import com.utsmyanmar.ecr.ECRHelper +import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation +import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class CardWaitingViewModel( + private val cardReadViewModel: CardReaderViewModel, + private val sharedViewModel: SharedViewModel +) : ViewModel() { + + companion object { + fun provideFactory( + cardReadViewModel: CardReaderViewModel, + sharedViewModel: SharedViewModel + ): ViewModelProvider.Factory { + return object : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return CardWaitingViewModel( + cardReadViewModel = cardReadViewModel, + sharedViewModel = sharedViewModel + ) as T + } + } + } + } + + private val _uiState = MutableStateFlow(CardWaitingUiState()) + val uiState = _uiState.asStateFlow() + + private val _events = Channel() + val events = _events.receiveAsFlow() + + private var retryCounter = 0 + private var fallbackCounter = 0 + private var fallbackEnabled = false + private var readerInitJob: Job? = null + + fun onScreenResume() { + retryCounter = 0 + fallbackCounter = SystemParamsOperation.getInstance().fallbackCounter + fallbackEnabled = SystemParamsOperation.getInstance().fallbackEnabled + + if (sharedViewModel.transactionsType.value == TransactionsType.REFUND) { + sharedViewModel.enableCardStatusIcon(false, false, false, false) + } else { + sharedViewModel.enableCardStatusIcon(true, true, true, false) + } + + val isFallback = sharedViewModel.getIsFallback().value == true + if (isFallback) { + _uiState.update { + it.copy( + alertMessage = "Fallback!\nPlease stripe!", + isFallback = true + ) + } + } + + startCardReadWhenReady(isFallback) + } + + fun onScreenPause() { + readerInitJob?.cancel() + stopCardReading() + } + + fun onBackPressed() { + if (sharedViewModel.isEcr.value == true) { + sharedViewModel.isEcr.postValue(false) + CoreUtils.getInstance(sharedViewModel).responseRejectMsg("Transaction cancelled") + sharedViewModel.isEcrFinished.postValue(true) + } + + stopCardReading() + + viewModelScope.launch { + _events.send(CardWaitingEvent.GoBack) + } + } + + fun onManualEntryClick() { + viewModelScope.launch { + _events.send(CardWaitingEvent.GoManualEntry) + } + } + + private fun startCardReadWhenReady(isFallback: Boolean) { + readerInitJob?.cancel() + readerInitJob = viewModelScope.launch { + if (cardReadViewModel.isReaderReady()) { + setupCardReadProcess(isFallback) + return@launch + } + + _uiState.update { + it.copy( + alertMessage = "Initializing card reader...", + isLoading = true + ) + } + + repeat(10) { + delay(300) + if (cardReadViewModel.isReaderReady()) { + _uiState.update { state -> + state.copy( + alertMessage = if (isFallback) { + "Fallback!\nPlease stripe!" + } else { + "Please insert, tap, or swipe card" + }, + isLoading = false + ) + } + setupCardReadProcess(isFallback) + return@launch + } + } + + _uiState.update { + it.copy( + alertMessage = "Card reader unavailable.\nPlease wait and try again.", + isLoading = false + ) + } + } + } + + private fun setupCardReadProcess(isFallback: Boolean) { + initCheckCard(isFallback) + } + + private fun initCheckCard(isFallback: Boolean) { + var allType = + AidlConstants.CardType.NFC.value or + AidlConstants.CardType.IC.value or + AidlConstants.CardType.MAGNETIC.value + + if (isFallback) { + allType = AidlConstants.CardType.MAGNETIC.value + } else if ( + SystemParamsOperation.getInstance().isMagStripeEnabled && + !SystemParamsOperation.getInstance().isNfcEnabled + ) { + allType = + AidlConstants.CardType.IC.value or + AidlConstants.CardType.MAGNETIC.value + } + + cardReadViewModel.startCheckXProcess( + allType, + 65, + object : CheckCardResultX { + override fun onSuccess(cardType: CardTypeX, isMPU: Boolean) { + when { + !isFallback && cardType == CardTypeX.MAG -> { + if (SystemParamsOperation.getInstance().isMagStripeEnabled) { + cardReadViewModel.setCardTransactionType(CardTransactionType.MAG) + } else { + _uiState.update { + it.copy(alertMessage = "Mag stripe not allowed") + } + setupCardReadProcess(false) + return + } + } + + isFallback && cardType == CardTypeX.MAG -> { + sharedViewModel.setEmvTrans(false) + cardReadViewModel.setCardTransactionType(CardTransactionType.FALLBACK) + } + + cardType == CardTypeX.IC || cardType == CardTypeX.NFC -> { + if (isMPU) { + sharedViewModel.setEmvTrans(false) + cardReadViewModel.setCardTransactionType(CardTransactionType.MPU) + } else { + cardReadViewModel.setCardData(cardType.value) + cardReadViewModel.setCardTransactionType(CardTransactionType.EMV) + } + } + } + + viewModelScope.launch { + _events.send(CardWaitingEvent.GoProcessingCard) + } + } + + override fun onError(code: Int, message: String) { + ecrActionCancel("Transaction cancelled") + + viewModelScope.launch { + _uiState.update { + it.copy( + alertMessage = message, + isLoading = false + ) + } + _events.send(CardWaitingEvent.GoMain) + } + } + + override fun onCommError() { + if (fallbackEnabled && retryCounter < fallbackCounter) { + _uiState.update { + it.copy( + alertMessage = "Card not detected!\nRemain Attempt - ${fallbackCounter - retryCounter}" + ) + } + retryCounter++ + setupCardReadProcess(false) + return + } + + if (retryCounter == fallbackCounter) { + _uiState.update { + it.copy(alertMessage = "Fallback!\nPlease stripe!") + } + setupCardReadProcess(true) + return + } + + ecrActionCancel("Transaction cancelled") + + viewModelScope.launch { + _uiState.update { + it.copy( + alertMessage = "Chip not detected!", + isLoading = false + ) + } + _events.send(CardWaitingEvent.GoMain) + } + } + } + ) + } + + private fun ecrActionCancel(msg: String) { + if (sharedViewModel.isEcr.value != true) { + return + } + + sharedViewModel.isEcr.postValue(false) + sharedViewModel.isEcrFinished.postValue(true) + + if (SystemParamsOperation.getInstance().isCMHLEnabled) { + ECRHelper.send( + CoreUtils.getInstance(sharedViewModel) + .generateCMHLResponse(ECRResultStatus.USER_CANCEL) + ) + + CoreUtils.getInstance(sharedViewModel).responseACKCMHL() + return + } + + CoreUtils.getInstance(sharedViewModel).responseRejectMsg(msg) + } + + private fun stopCardReading() { + sharedViewModel.setIsFallback(false) + cardReadViewModel.cancelCheckCard() + cardReadViewModel.resetOneTimeFlag() + cardReadViewModel.cancelCheckXProcess() + } + + override fun onCleared() { + super.onCleared() + readerInitJob?.cancel() + stopCardReading() + } +} diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt index 434dfef..20995b5 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt @@ -1,13 +1,19 @@ package com.mob.utsmyanmar.ui.navigation import androidx.compose.runtime.Composable +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.mob.utsmyanmar.ui.amount.AmountScreen +import com.mob.utsmyanmar.ui.cardwaiting.CardWaitingScreen +import com.mob.utsmyanmar.ui.cardwaiting.CardWaitingViewModel import com.mob.utsmyanmar.ui.dashboard.DashboardScreen +import com.mob.utsmyanmar.viewmodel.CardReaderViewModel +import com.mob.utsmyanmar.viewmodel.SharedViewModel @Composable fun AppNavGraph( @@ -42,7 +48,38 @@ fun AppNavGraph( ) { backStackEntry -> AmountScreen( action = backStackEntry.arguments?.getString("action").orEmpty(), - onBackClick = { navController.popBackStack() } + onBackClick = { navController.popBackStack() }, + onCancelClick = { navController.popBackStack() }, + onNextClick = { + navController.navigate(Routes.CardWaiting.route) + } + ) + } + + composable(Routes.CardWaiting.route) { + val sharedViewModel: SharedViewModel = hiltViewModel() + val cardReaderViewModel: CardReaderViewModel = hiltViewModel() + val cardWaitingViewModel: CardWaitingViewModel = viewModel( + factory = CardWaitingViewModel.provideFactory( + cardReadViewModel = cardReaderViewModel, + sharedViewModel = sharedViewModel + ) + ) + + CardWaitingScreen( + viewModel = cardWaitingViewModel, + onManualEntry = {}, + onProcessingCard = {}, + onTimeout = { navController.popBackStack() }, + onBack = { navController.popBackStack() }, + onMain = { + navController.navigate(Routes.Dashboard.route) { + popUpTo(Routes.Dashboard.route) { + inclusive = false + } + launchSingleTop = true + } + } ) } } diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt index e13a6df..766f8ed 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt @@ -5,4 +5,5 @@ sealed class Routes(val route: String) { data object Amount : Routes("amount/{action}") { fun createRoute(action: String): String = "amount/$action" } + data object CardWaiting : Routes("card_waiting") } diff --git a/app/src/main/java/com/mob/utsmyanmar/utils/AppContextHolder.kt b/app/src/main/java/com/mob/utsmyanmar/utils/AppContextHolder.kt new file mode 100644 index 0000000..336ea47 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/utils/AppContextHolder.kt @@ -0,0 +1,15 @@ +package com.mob.utsmyanmar.utils + +import android.content.Context + +object AppContextHolder { + private lateinit var appContext: Context + + fun init(context: Context) { + appContext = context.applicationContext + } + + fun get(): Context { + return appContext + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/utils/CoreUtils.kt b/app/src/main/java/com/mob/utsmyanmar/utils/CoreUtils.kt new file mode 100644 index 0000000..ca4187a --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/utils/CoreUtils.kt @@ -0,0 +1,1037 @@ +package com.mob.utsmyanmar.utils + +import android.annotation.SuppressLint +import android.util.Log +import com.kizzy.cmhl.EcrManager +import com.kizzy.cmhl.models.* +import com.mob.utsmyanmar.model.ProcessCode +import com.mob.utsmyanmar.model.ecr.ECRResultStatus +import com.mob.utsmyanmar.viewmodel.SharedViewModel +import com.utsmyanmar.ecr.ECRHelper +import com.utsmyanmar.ecr.ECRProcess +import com.utsmyanmar.ecr.data.RespType +import com.utsmyanmar.ecr.data.model.Transactions +import com.utsmyanmar.ecr.data.model.TransactionsResp +import com.utsmyanmar.paylibs.utils.POSUtil +import com.utsmyanmar.paylibs.utils.enums.TransMenu +import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType +import com.utsmyanmar.paylibs.utils.params.Params +import sunmi.sunmiui.utils.LogUtil +import java.text.SimpleDateFormat +import java.util.Calendar + +class CoreUtils( + private val sharedViewModel: SharedViewModel +) : OldECRSetups, ECRSetups, ECRSetupsCMHL { + + companion object { + private const val TAG = "CoreUtils" + + @Volatile + private var instance: CoreUtils? = null + + fun getInstance(sharedViewModel: SharedViewModel): CoreUtils { + return instance ?: synchronized(this) { + instance ?: CoreUtils(sharedViewModel).also { + instance = it + } + } + } + + fun reduceTrailingZeros(inputValue: Int): Int { + return try { + val reduced = inputValue.toString().dropLast(2) + reduced.ifEmpty { "1" }.toInt() + } catch (e: Exception) { + e.printStackTrace() + 1 + } + } + + fun convertToDecimalString(integer: Int): String { + return "%.2f".format(integer / 100.0) + } + } + + private fun ecrResponseAction(msg: String) { + LogUtil.d(TAG, "ECR Respond Message: $msg") + ECRHelper.send(msg.toByteArray()) + } + + fun logTrans(trans: Transactions) { + Log.d(TAG, "logTrans: amount : ${trans.AMT}") + Log.d(TAG, "logTrans: trace : ${trans.TRACE}") + Log.d(TAG, "logTrans: cardNo : ${trans.CARDNO}") + Log.d(TAG, "logTrans: refNo : ${trans.REFNUM}") + Log.d(TAG, "logTrans: approvalCode : ${trans.APPCODE}") + Log.d(TAG, "logTrans: cmd : ${trans.CMD}") + Log.d(TAG, "logTrans: type : ${trans.TYPE}") + } + + @SuppressLint("SimpleDateFormat") + fun coreResponseMsg(status: RespType): TransactionsResp { + val payDetail = sharedViewModel.payDetail.value + + val resp = TransactionsResp() + resp.DATE = SimpleDateFormat("dd-MM-yyyy").format(Calendar.getInstance().time) + resp.TIME = SimpleDateFormat("HH:mm").format(Calendar.getInstance().time) + resp.CMD = sharedViewModel.ecrCMD.value?.toString().orEmpty() + + if (sharedViewModel.transactionsType.value == TransactionsType.SETTLEMENT) { + resp.AMT = sharedViewModel.totalAmount.value + } + + if (payDetail != null) { + resp.RESP = payDetail.tradeAnswerCode + resp.TID = payDetail.terminalNo + resp.MID = payDetail.merchantNo + } else { + val pay = Params.newTrade(false).payDetail + resp.TID = pay.terminalNo + resp.MID = pay.merchantNo + resp.RESP = "-1" + } + + resp.STATUS = status + + if (status == RespType.Cancelled || status == RespType.Rejected) { + resp.RESP = "-1" + } + + return resp + } + + fun generateCMHLResponse(ecrResultStatus: ECRResultStatus): ByteArray { + val payDetail = sharedViewModel.payDetail.value + ?: return byteArrayOf() + + val transactionsType = sharedViewModel.transactionsType.value + val isApproved = + payDetail.tradeAnswerCode == "000" || payDetail.tradeAnswerCode == "00" + + return when (transactionsType) { + TransactionsType.SALE -> { + val response = when { + ecrResultStatus == ECRResultStatus.RESPONSE_RECEIVED && isApproved -> { + SaleResponse.Builder() + .setResponseCode(POSUtil.getInstance().responseCodeConverter(payDetail.tradeAnswerCode)) + .setAmount(POSUtil.getInstance().formatAmount(payDetail.amount)) + .setPan(payDetail.cardNo) + .setTid(payDetail.terminalNo) + .setMid(payDetail.merchantNo) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .setReferenceNum(payDetail.referNo) + .setApprovalCode(payDetail.approvalCode) + .setInvoiceNumber(payDetail.invoiceNo) + .setStan(payDetail.voucherNo) + .setEntry(POSUtil.getInstance().getEntryCode(payDetail)) + .setCardType(POSUtil.getInstance().getCardType(payDetail)) + .setCurrency(payDetail.currencyCode) + .build() + } + + ecrResultStatus == ECRResultStatus.USER_CANCEL -> { + SaleResponse.Builder() + .setResponseCode("FF") + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + ecrResultStatus == ECRResultStatus.RESPONSE_RECEIVED -> { + SaleResponse.Builder() + .setResponseCode(POSUtil.getInstance().responseCodeConverter(payDetail.tradeAnswerCode)) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + else -> { + SaleResponse.Builder() + .setResponseCode("NR") + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + } + + EcrManager.buildPacket(response) + } + + TransactionsType.VOID -> { + val response = when { + ecrResultStatus == ECRResultStatus.RESPONSE_RECEIVED && isApproved -> { + VoidResponse.Builder() + .setResponseCode(POSUtil.getInstance().responseCodeConverter(payDetail.tradeAnswerCode)) + .setAmount(POSUtil.getInstance().formatAmount(payDetail.amount)) + .setVoucherNumber(payDetail.voucherNo) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .setApprovalCode(payDetail.approvalCode) + .setReferenceNum(payDetail.referNo) + .setTid(payDetail.terminalNo) + .setMid(payDetail.merchantNo) + .setEntry(POSUtil.getInstance().getEntryCode(payDetail)) + .build() + } + + ecrResultStatus == ECRResultStatus.USER_CANCEL -> { + VoidResponse.Builder() + .setResponseCode("FF") + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + ecrResultStatus == ECRResultStatus.RESPONSE_RECEIVED -> { + VoidResponse.Builder() + .setResponseCode(POSUtil.getInstance().responseCodeConverter(payDetail.tradeAnswerCode)) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + else -> { + VoidResponse.Builder() + .setResponseCode("NR") + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + } + + EcrManager.buildPacket(response) + } + + TransactionsType.SETTLEMENT -> { + val response = SettlementResponse.Builder() + .setResponseCode(POSUtil.getInstance().responseCodeConverter(payDetail.tradeAnswerCode)) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .setTid(payDetail.terminalNo) + .setMid(payDetail.merchantNo) + .build() + + EcrManager.buildPacket(response) + } + + TransactionsType.WAVEPAY -> { + val response = if (payDetail.qrTransStatus == 1) { + QrPaymentResponse.Builder() + .setResponseCode("00") + .setAmount(POSUtil.getInstance().formatAmount(payDetail.amount)) + .setTid(payDetail.terminalNo) + .setMid(payDetail.merchantNo) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .setApprovalCode(payDetail.qrTransId) + .setRrn(payDetail.qrReferNo) + .setPosEntryMode("011") + .setInvoiceNumber(payDetail.invoiceNo) + .setStan(payDetail.voucherNo) + .setPaymentType(payDetail.customerMobile) + .setCurrencyCode(payDetail.currencyCode) + .build() + } else { + QrPaymentResponse.Builder() + .setResponseCode("05") + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + EcrManager.buildPacket(response) + } + + TransactionsType.PRE_AUTH_SALE -> { + val response = if (isApproved) { + PreAuthResponse.Builder() + .setResponseCode(payDetail.tradeAnswerCode) + .setAmount(POSUtil.getInstance().formatAmount(payDetail.amount)) + .setPan(payDetail.cardNo) + .setTid(payDetail.terminalNo) + .setMid(payDetail.merchantNo) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .setApprovalCode(payDetail.approvalCode) + .setInvoiceNumber(payDetail.invoiceNo) + .setStan(payDetail.voucherNo) + .setRrn(payDetail.referNo) + .setEntry(POSUtil.getInstance().getEntryCode(payDetail)) + .setCardType(POSUtil.getInstance().getCardType(payDetail)) + .build() + } else { + PreAuthResponse.Builder() + .setResponseCode(payDetail.tradeAnswerCode) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + EcrManager.buildPacket(response) + } + + TransactionsType.PRE_AUTH_COMPLETE -> { + val response = if (isApproved) { + PreAuthCompletionResponse.Builder() + .setResponseCode(payDetail.tradeAnswerCode) + .setAmount(POSUtil.getInstance().formatAmount(payDetail.amount)) + .setPan(payDetail.cardNo) + .setTid(payDetail.terminalNo) + .setMid(payDetail.merchantNo) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .setApprovalCode(payDetail.approvalCode) + .setInvoiceNumber(payDetail.invoiceNo) + .setStan(payDetail.voucherNo) + .setRrn(payDetail.referNo) + .setEntry(POSUtil.getInstance().getEntryCode(payDetail)) + .build() + } else { + PreAuthCompletionResponse.Builder() + .setResponseCode(payDetail.tradeAnswerCode) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + } + + EcrManager.buildPacket(response) + } + + else -> { + val response = SaleResponse.Builder() + .setResponseCode(payDetail.tradeAnswerCode) + .setTime(payDetail.tradeTime) + .setDate(payDetail.tradeDate) + .build() + + EcrManager.buildPacket(response) + } + } + } + + fun generateResponseMsg(): TransactionsResp { + val trans = sharedViewModel.ecrTrans.value + val payDetail = sharedViewModel.payDetail.value ?: return TransactionsResp() + + val resp = when { + payDetail.tradeAnswerCode == "00" || payDetail.tradeAnswerCode == "000" -> { + coreResponseMsg(RespType.Approved) + } + + payDetail.transactionType == TransactionsType.WAVEPAY.value && + payDetail.qrTransStatus == 1 -> { + coreResponseMsg(RespType.Approved).apply { + REFNUM = payDetail.qrReferNo + } + } + + payDetail.transactionType == TransactionsType.WAVEPAY.value && + payDetail.qrTransStatus == 2 -> { + coreResponseMsg(RespType.QrCode).apply { + REFNUM = payDetail.qrReferNo + QRDATA = sharedViewModel.qrData.value + } + } + + else -> { + coreResponseMsg(RespType.Declined) + } + } + + resp.AMT = payDetail.amount + resp.TRACE = payDetail.voucherNo + resp.TYPE = trans?.CMD?.name.orEmpty() + resp.EXPDATE = payDetail.expDate + + if (sharedViewModel.transactionsType.value != TransactionsType.SETTLEMENT) { + resp.ENTRYMODE = POSUtil.getInstance().getEntryType(payDetail.cardType) + resp.REFNUM = payDetail.referNo + resp.PAN = POSUtil.getInstance().getCardNumMasking(payDetail.cardNo) + } + + resp.BATCHNO = payDetail.batchNo + + if (!payDetail.icC55.isNullOrEmpty()) { + resp.APP = payDetail.appLabel + resp.TC = payDetail.arqC + resp.AID = payDetail.aid + } + + return resp + } + + fun responseRejectMsg(actionName: String) { + val trans = sharedViewModel.ecrTrans.value ?: return + + LogUtil.d(TAG, "Rejected action name :$actionName") + LogUtil.d(TAG, "Rejected trans type :${trans.TYPE}") + LogUtil.d(TAG, "Rejected trans cmd :${trans.CMD}") + + val resp = coreResponseMsg(RespType.Rejected) + resp.ERROR_MSG = actionName + resp.TYPE = trans.CMD?.name.orEmpty() + + val msg = ECRProcess.generateECRResponse(resp) + ecrResponseAction(msg) + } + + fun responseRejectMsg(trans: Transactions, actionName: String) { + LogUtil.d(TAG, "Rejected action name :$actionName") + LogUtil.d(TAG, "Rejected trans type :${trans.TYPE}") + LogUtil.d(TAG, "Rejected trans cmd :${trans.CMD}") + + val resp = coreResponseMsg(RespType.Rejected) + resp.ERROR_MSG = actionName + resp.TYPE = trans.CMD?.name.orEmpty() + + val msg = ECRProcess.generateECRResponse(resp) + ecrResponseAction(msg) + } + + fun responseACKCMHL() { + ECRHelper.send(byteArrayOf(0x06)) + } + + override fun setUpECRTest() { + ecrResponseAction("Connection Success!") + } + + override fun setUpECREchoTest() { + val resp = TransactionsResp() + resp.TYPE = "ECHO" + resp.STATUS = RespType.Approved + + val pay = Params.newTrade(false).payDetail + resp.TID = pay.terminalNo + resp.MID = pay.merchantNo + resp.RESP = "000" + + val msg = ECRProcess.generateECRResponse(resp) + ecrResponseAction(msg) + } + + override fun setupPingRequest(trans: PingRequest) { + val pingResponse = PingResponse.Builder() + .setResponseCode("00") + .build() + + val packet = EcrManager.buildPacket(pingResponse) + ECRHelper.send(packet) + } + + override fun setUpECRSale(trans: SaleRequest): Boolean { + return try { + LogUtil.d(TAG, "amount is ${trans.amount}") + + val amount = trans.amount?.toLongOrNull() + if (amount == null || amount <= 0) { + LogUtil.d(TAG, "Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue(amount.toString()) + sharedViewModel.transactionsType.postValue(TransactionsType.SALE) + sharedViewModel.processCode.postValue( + ProcessCode.SALE_PURCHASE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRQR(trans: QrPaymentRequest): Boolean { + return try { + LogUtil.d(TAG, "amount is ${trans.amount}") + + val amount = trans.amount?.toLongOrNull() + if (amount == null || amount <= 0) { + LogUtil.d(TAG, "Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue(amount.toString()) + sharedViewModel.transactionsType.postValue(TransactionsType.WAVEPAY) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRQRVoid(trans: VoidQrPaymentRequest): Boolean { + return false + } + + override fun setUpECRVoid(trans: VoidRequest): Boolean { + return try { + if (trans.invoiceNumber.isNullOrEmpty()) { + LogUtil.d(TAG, "Trace field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.traceNo.postValue(trans.invoiceNumber) + sharedViewModel.transactionName.postValue("SALE") + sharedViewModel.transactionsType.postValue(TransactionsType.VOID) + sharedViewModel.processCode.postValue( + ProcessCode.SALE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuth(trans: PreAuthRequest): Boolean { + return try { + LogUtil.d(TAG, "amount is ${trans.amount}") + + val amount = trans.amount?.toLongOrNull() + if (amount == null || amount <= 0) { + LogUtil.d(TAG, "Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue(amount.toString()) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_SALE) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_SALE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuthVoid(trans: PreAuthCancellationRequest): Boolean { + return try { + LogUtil.d(TAG, "approval code is ${trans.approvalCode}") + LogUtil.d(TAG, "reference num is ${trans.referenceNumber}") + + if (trans.approvalCode.isNullOrEmpty()) { + LogUtil.d(TAG, "Approval code field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.rrNNo.postValue(trans.referenceNumber) + sharedViewModel.approvalCode.postValue(trans.approvalCode) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_VOID) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuthComplete(trans: PreAuthCompletionRequest): Boolean { + return try { + LogUtil.d(TAG, "approval code is ${trans.approvalCode}") + LogUtil.d(TAG, "reference num is ${trans.referenceNumber}") + + if (trans.approvalCode.isNullOrEmpty()) { + LogUtil.d(TAG, "Approval code field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.rrNNo.postValue(trans.referenceNumber) + sharedViewModel.approvalCode.postValue(trans.approvalCode) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_COMPLETE) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_COMPLETE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRSettlement(trans: SettlementRequest) { + // TODO: implement if needed + } + + override fun setUpECRSale(trans: Transactions): Boolean { + return try { + LogUtil.d(TAG, "amount is ${trans.AMT}") + + val amount = trans.AMT + if (amount == null || amount <= 0) { + responseRejectMsg("Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue(convertToDecimalString(amount)) + sharedViewModel.transactionsType.postValue(TransactionsType.SALE) + sharedViewModel.processCode.postValue( + ProcessCode.SALE_PURCHASE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRQR(trans: Transactions): Boolean { + return try { + LogUtil.d(TAG, "amount is ${trans.AMT}") + + val amount = trans.AMT + if (amount == null || amount <= 0) { + responseRejectMsg("Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue(convertToDecimalString(amount)) + sharedViewModel.transactionsType.postValue(TransactionsType.WAVEPAY) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRVoid(trans: Transactions): Boolean { + return try { + if (trans.TRACE.isNullOrEmpty()) { + responseRejectMsg("Trace field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.traceNo.postValue(trans.TRACE) + sharedViewModel.transactionName.postValue("SALE") + sharedViewModel.transactionsType.postValue(TransactionsType.VOID) + sharedViewModel.processCode.postValue( + ProcessCode.SALE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRCashAdvance(trans: Transactions): Boolean { + return try { + val amount = trans.AMT + if (amount == null || amount <= 0) { + responseRejectMsg("Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.CASH_OUT) + sharedViewModel.processCode.postValue( + ProcessCode.CASH_ADVANCE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuth(trans: Transactions): Boolean { + return try { + val amount = trans.AMT + if (amount == null || amount <= 0) { + responseRejectMsg("Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_SALE) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_SALE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuthVoid(trans: Transactions): Boolean { + return try { + if (trans.TRACE.isNullOrEmpty()) { + responseRejectMsg("Trace field is invalid!") + return false + } + + if (trans.REFNUM.isNullOrEmpty()) { + responseRejectMsg("Ref no field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.rrNNo.postValue(trans.REFNUM) + sharedViewModel.traceNo.postValue(trans.TRACE) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_VOID) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuthComplete(trans: Transactions): Boolean { + return try { + if (trans.REFNUM.isNullOrEmpty()) { + responseRejectMsg("Ref no field is invalid!") + return false + } + + if (trans.APPCODE.isNullOrEmpty()) { + responseRejectMsg("Approval code field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.rrNNo.postValue(trans.REFNUM) + sharedViewModel.approvalCode.postValue(trans.APPCODE) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_COMPLETE) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_COMPLETE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRPreAuthCompleteVoid(trans: Transactions): Boolean { + return try { + if (trans.TRACE.isNullOrEmpty()) { + responseRejectMsg("Trace field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.traceNo.postValue(trans.TRACE) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.transactionName.postValue("PRE_AUTH_COMPLETE") + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_COMPLETE_VOID) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_COMPLETE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRSale(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size == 1) { + ecrResponseAction("Amount field is empty!") + return false + } + + val amount = parts[1] + + if (POSUtil.getInstance().checkNumberField(amount)) { + ecrResponseAction("Invalid Amount Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.SALE) + sharedViewModel.processCode.postValue( + ProcessCode.SALE_PURCHASE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRVoid(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size == 1) { + ecrResponseAction("Trace No field is empty!") + return false + } + + val traceNo = parts[1] + + if (POSUtil.getInstance().checkNumberField(traceNo)) { + ecrResponseAction("Invalid Trace Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.traceNo.postValue(traceNo) + sharedViewModel.transactionName.postValue("SALE") + sharedViewModel.transactionsType.postValue(TransactionsType.VOID) + sharedViewModel.processCode.postValue( + ProcessCode.SALE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRCashAdvance(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size == 1) { + ecrResponseAction("Amount field is empty!") + return false + } + + val amount = parts[1] + + if (POSUtil.getInstance().checkNumberField(amount)) { + ecrResponseAction("Invalid Amount Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.CASH_OUT) + sharedViewModel.processCode.postValue( + ProcessCode.CASH_ADVANCE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRPreAuth(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size == 1) { + ecrResponseAction("Amount field is empty!") + return false + } + + val amount = parts[1] + + if (POSUtil.getInstance().checkNumberField(amount)) { + ecrResponseAction("Invalid Amount Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_SALE) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_SALE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRPreAuthVoid(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size != 3) { + ecrResponseAction("Please input required field!") + return false + } + + val traceNo = parts[1] + val rrNNo = parts[2] + + if (POSUtil.getInstance().checkNumberField(traceNo)) { + ecrResponseAction("Invalid Trace Field!") + return false + } + + if (POSUtil.getInstance().checkNumberField(rrNNo)) { + ecrResponseAction("Invalid RRN Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.rrNNo.postValue(rrNNo) + sharedViewModel.traceNo.postValue(traceNo) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_VOID) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRPreAuthComplete(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size != 3) { + ecrResponseAction("Please input required field!") + return false + } + + val rrNNo = parts[1] + val approvalCode = parts[2] + + if (POSUtil.getInstance().checkNumberField(rrNNo)) { + ecrResponseAction("Invalid RRN Field!") + return false + } + + if (POSUtil.getInstance().checkApprovalField(approvalCode)) { + ecrResponseAction("Invalid Approval Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.rrNNo.postValue(rrNNo) + sharedViewModel.approvalCode.postValue(approvalCode) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_COMPLETE) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_COMPLETE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRPreAuthCompleteVoid(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size == 1) { + ecrResponseAction("Trace No field is empty!") + return false + } + + val traceNo = parts[1] + + if (POSUtil.getInstance().checkNumberField(traceNo)) { + ecrResponseAction("Invalid Trace Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.traceNo.postValue(traceNo) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.transactionName.postValue("PRE_AUTH_COMPLETE") + sharedViewModel.transactionsType.postValue(TransactionsType.PRE_AUTH_COMPLETE_VOID) + sharedViewModel.processCode.postValue( + ProcessCode.PRE_AUTH_COMPLETE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } + + override fun setUpECRSettlement() { + sharedViewModel.isEcr.postValue(true) + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.setTransMenu(TransMenu.SETTLEMENT) + sharedViewModel.transactionsType.postValue(TransactionsType.SETTLEMENT) + } + + override fun setUpECRRefund(trans: Transactions): Boolean { + return try { + if (trans.REFNUM.isNullOrEmpty()) { + responseRejectMsg("Ref no field is invalid!") + return false + } + + if (trans.CARDNO.isNullOrEmpty()) { + responseRejectMsg("Card no field is invalid!") + return false + } + + val amount = trans.AMT + if (amount == null || amount <= 0) { + responseRejectMsg("Amount field is invalid!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.rrNNo.postValue(trans.REFNUM) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.REFUND) + sharedViewModel.processCode.postValue( + ProcessCode.REFUND + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + true + } catch (e: Exception) { + false + } + } + + override fun setUpECRRefund(msg: String): Boolean { + val parts = msg.split("-") + + if (parts.size != 4) { + ecrResponseAction("Please input required field!") + return false + } + + val cardNo = parts[1] + val rrNNo = parts[2] + val amount = parts[3] + + if (POSUtil.getInstance().checkNumberField(cardNo)) { + ecrResponseAction("Invalid CardNo Field!") + return false + } + + if (POSUtil.getInstance().checkNumberField(rrNNo)) { + ecrResponseAction("Invalid RRN Field!") + return false + } + + if (POSUtil.getInstance().checkNumberField(amount)) { + ecrResponseAction("Invalid Amount Field!") + return false + } + + sharedViewModel.isEcrFinished.postValue(false) + sharedViewModel.isEcr.postValue(true) + sharedViewModel.rrNNo.postValue(rrNNo) + sharedViewModel.cardNo.postValue(cardNo) + sharedViewModel.amount.postValue("$amount.00") + sharedViewModel.transactionsType.postValue(TransactionsType.REFUND) + sharedViewModel.processCode.postValue( + ProcessCode.REFUND + ProcessCode.SMART + ProcessCode.TO_ACCOUNT + ) + + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/utils/ECRSetups.kt b/app/src/main/java/com/mob/utsmyanmar/utils/ECRSetups.kt new file mode 100644 index 0000000..35411ad --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/utils/ECRSetups.kt @@ -0,0 +1,17 @@ +package com.mob.utsmyanmar.utils + +import com.utsmyanmar.ecr.data.model.Transactions + +interface ECRSetups { + fun setUpECREchoTest() + fun setUpECRSale(trans: Transactions): Boolean + fun setUpECRQR(trans: Transactions): Boolean + fun setUpECRVoid(trans: Transactions): Boolean + fun setUpECRCashAdvance(trans: Transactions): Boolean + fun setUpECRPreAuth(trans: Transactions): Boolean + fun setUpECRPreAuthVoid(trans: Transactions): Boolean + fun setUpECRPreAuthComplete(trans: Transactions): Boolean + fun setUpECRPreAuthCompleteVoid(trans: Transactions): Boolean + fun setUpECRSettlement() + fun setUpECRRefund(trans: Transactions): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/utils/ECRSetupsCMHL.kt b/app/src/main/java/com/mob/utsmyanmar/utils/ECRSetupsCMHL.kt new file mode 100644 index 0000000..71c0745 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/utils/ECRSetupsCMHL.kt @@ -0,0 +1,24 @@ +package com.mob.utsmyanmar.utils + +import com.kizzy.cmhl.models.PingRequest +import com.kizzy.cmhl.models.PreAuthCancellationRequest +import com.kizzy.cmhl.models.PreAuthCompletionRequest +import com.kizzy.cmhl.models.PreAuthRequest +import com.kizzy.cmhl.models.QrPaymentRequest +import com.kizzy.cmhl.models.SaleRequest +import com.kizzy.cmhl.models.SettlementRequest +import com.kizzy.cmhl.models.VoidQrPaymentRequest +import com.kizzy.cmhl.models.VoidRequest + +interface ECRSetupsCMHL { + fun setUpECREchoTest() + fun setupPingRequest(trans: PingRequest) + fun setUpECRSale(trans: SaleRequest): Boolean + fun setUpECRQR(trans: QrPaymentRequest): Boolean + fun setUpECRQRVoid(trans: VoidQrPaymentRequest): Boolean + fun setUpECRVoid(trans: VoidRequest): Boolean + fun setUpECRPreAuth(trans: PreAuthRequest): Boolean + fun setUpECRPreAuthVoid(trans: PreAuthCancellationRequest): Boolean + fun setUpECRPreAuthComplete(trans: PreAuthCompletionRequest): Boolean + fun setUpECRSettlement(trans: SettlementRequest) +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/utils/OldECRSetups.kt b/app/src/main/java/com/mob/utsmyanmar/utils/OldECRSetups.kt new file mode 100644 index 0000000..7958be7 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/utils/OldECRSetups.kt @@ -0,0 +1,14 @@ +package com.mob.utsmyanmar.utils + +interface OldECRSetups { + fun setUpECRTest() + fun setUpECRSale(msg: String): Boolean + fun setUpECRVoid(msg: String): Boolean + fun setUpECRCashAdvance(msg: String): Boolean + fun setUpECRPreAuth(msg: String): Boolean + fun setUpECRPreAuthVoid(msg: String): Boolean + fun setUpECRPreAuthComplete(msg: String): Boolean + fun setUpECRPreAuthCompleteVoid(msg: String): Boolean + fun setUpECRSettlement() + fun setUpECRRefund(msg: String): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/viewmodel/CardReaderViewModel.kt b/app/src/main/java/com/mob/utsmyanmar/viewmodel/CardReaderViewModel.kt new file mode 100644 index 0000000..92889a1 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/viewmodel/CardReaderViewModel.kt @@ -0,0 +1,152 @@ +package com.mob.utsmyanmar.viewmodel + + +import android.os.Handler +import android.os.Looper +import androidx.lifecycle.ViewModel +import com.mob.utsmyanmar.model.CardTransactionType +import com.utsmyanmar.checkxread.CheckXRead +import com.utsmyanmar.checkxread.checkcard.CheckCardResultX +import com.utsmyanmar.checkxread.model.CardDataX +import com.utsmyanmar.checkxread.readcard.ReadCardResultX +import com.utsmyanmar.checkxread.readcard.ReadCardX +import com.utsmyanmar.checkxread.sdk.SunmiSDK +import com.utsmyanmar.paylibs.model.PayDetail +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class CardReaderViewModel @Inject constructor() : ViewModel() { + + companion object { + private val TAG = CardReaderViewModel::class.java.simpleName + } + + private val mainThreadHandler = Handler(Looper.getMainLooper()) + + private var oneTimeFlag = false + + private var cardTransactionType: CardTransactionType? = null + + /* + * UI States + */ + + private val _errorCode = MutableStateFlow("") + val errorCode = _errorCode.asStateFlow() + + private val _cardData = MutableStateFlow(null) + val cardData = _cardData.asStateFlow() + + private var _cardTypeData = MutableStateFlow(null) + val cardTypeData = _cardTypeData.asStateFlow() + + private val _payDetail = MutableStateFlow(null) + val payDetail = _payDetail.asStateFlow() + + private val _checkCardAlertMsg = + MutableStateFlow(null) + val checkCardAlertMsg = + _checkCardAlertMsg.asStateFlow() + + /* + * Transaction Type + */ + + fun setCardTransactionType( + cardTransactionType: CardTransactionType + ) { + this.cardTransactionType = cardTransactionType + } + + fun getCardTransactionType(): CardTransactionType? { + return cardTransactionType + } + + /* + * Check Card Process + */ + + fun startCheckXProcess( + allType: Int, + timeOut: Int, + cardResultX: CheckCardResultX + ) { + CheckXRead.getInstance() + .startCheckXProcess( + allType, + timeOut, + cardResultX + ) + } + + fun isReaderReady(): Boolean { + return SunmiSDK.getInstance().readCardOptV2 != null + } + + fun cancelCheckXProcess() { + CheckXRead.getInstance() + .cancelCheckXProcess() + } + + /* + * Read Card Process + */ + + fun startReadXProcess( + readCardX: ReadCardX, + readCardResultX: ReadCardResultX + ) { + CheckXRead.getInstance() + .startReadXProcess( + readCardX, + readCardResultX + ) + } + + /* + * Alert Message + */ + + fun setCheckCardAlertMsg( + msg: String, + isAutoHide: Boolean + ) { + _checkCardAlertMsg.value = msg + + if (isAutoHide) { + Handler(Looper.getMainLooper()).postDelayed({ + _checkCardAlertMsg.value = null + }, 5000) + } + } + + fun resetUI() { + _checkCardAlertMsg.value = null + } + + /* + * One Time Flag + */ + + fun resetOneTimeFlag() { + oneTimeFlag = false + } + + /* + * Cancel Card Checking + */ + + fun cancelCheckCard() { + if (isReaderReady()) { + SunmiSDK.getInstance() + .cancelCheckCard() + } + } + + fun setCardData(value: Int){ + _cardTypeData.value = value + } +} diff --git a/app/src/main/java/com/mob/utsmyanmar/viewmodel/ProcessingTransaction.kt b/app/src/main/java/com/mob/utsmyanmar/viewmodel/ProcessingTransaction.kt new file mode 100644 index 0000000..2ea907f --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/viewmodel/ProcessingTransaction.kt @@ -0,0 +1,25 @@ +package com.mob.utsmyanmar.viewmodel + +import com.mob.utsmyanmar.model.TransResultStatus +import com.utsmyanmar.paylibs.model.PayDetail +import com.utsmyanmar.paylibs.system.SingleLiveEvent + +interface ProcessingTransaction { + fun resetTransactionStatus() + + fun getTransStatus(): SingleLiveEvent + + fun startOnlineProcess() + + fun insertDB(payResult: PayDetail) + + fun processVoidDB(payResult: PayDetail) + + fun processPreVoidDb(payResult: PayDetail) + + fun processPreCompDb(payResult: PayDetail) + + fun processPreCompVoidDb(payResult: PayDetail) + + fun processRefundDB(payResult: PayDetail) +} \ No newline at end of file diff --git a/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt b/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt new file mode 100644 index 0000000..8b5fc58 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/viewmodel/SharedViewModel.kt @@ -0,0 +1,476 @@ +package com.mob.utsmyanmar.viewmodel + +import android.graphics.Bitmap +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.mob.utsmyanmar.model.SettlementType +import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest +import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse +import com.utsmyanmar.baselib.repo.Repository +import com.utsmyanmar.ecr.data.TransType +import com.utsmyanmar.ecr.data.model.Transactions +import com.utsmyanmar.paylibs.model.PayDetail +import com.utsmyanmar.paylibs.print.printx.PrintXReceipt +import com.utsmyanmar.paylibs.print.printx.PrintXStatus +import com.utsmyanmar.paylibs.system.SingleLiveEvent +import com.utsmyanmar.paylibs.utils.AccountType +import com.utsmyanmar.paylibs.utils.PrintStatus +import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation +import com.utsmyanmar.paylibs.utils.enums.HostType +import com.utsmyanmar.paylibs.utils.enums.TransMenu +import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType +import dagger.hilt.android.lifecycle.HiltViewModel +import io.reactivex.rxjava3.core.Observable +import sunmi.sunmiui.utils.LogUtil +import javax.inject.Inject + +@HiltViewModel +class SharedViewModel @Inject constructor( + private val repository: Repository +) : ViewModel() { + + companion object { + private const val TAG = "SharedViewModel" + } + + val transactionsType = SingleLiveEvent() + + val settlementType = SingleLiveEvent() + + val accountType = SingleLiveEvent() + + val amount = SingleLiveEvent() + + val totalAmount = SingleLiveEvent() + + val cardNo = SingleLiveEvent() + + val processCode = SingleLiveEvent() + + val payDetail = SingleLiveEvent() + + val printStatus = SingleLiveEvent() + + val merchantName = SingleLiveEvent() + + val transactionName = SingleLiveEvent() + + val payDetailList = SingleLiveEvent>() + + val isEcr = SingleLiveEvent() + + val traceNo = SingleLiveEvent() + + val rrNNo = SingleLiveEvent() + + val approvalCode = SingleLiveEvent() + + val isEcrFinished = SingleLiveEvent() + + val isEmv = SingleLiveEvent() + + private val settlementStatus = MutableLiveData() + + private val wavePayStatus = MutableLiveData() + + val sendMsg = SingleLiveEvent() + + val qrData = SingleLiveEvent() + + val qrRefNum = SingleLiveEvent() + + val ecrCMD = SingleLiveEvent() + + val _transMenu = SingleLiveEvent() + + val twoBtnLayout = MutableLiveData(0) + + val oneBtnLayout = MutableLiveData(8) + + private val reprintBtnLayout = MutableLiveData(8) + + val isReprint = SingleLiveEvent() + + var signBitmap: Bitmap? = null + + val hostType = SingleLiveEvent() + + private val printReceiptButtons = MutableLiveData(0) + + val printReceiptMsg = SingleLiveEvent() + + val reprintTransTypeMsg = SingleLiveEvent() + + val ecrTrans = SingleLiveEvent() + + val _manualEntryStatus = MutableLiveData() + + val fullVoidPreauthStatus = MutableLiveData() + + val partialVoidPreauthStatus = MutableLiveData() + + val printXStatus = SingleLiveEvent() + + private val _errorFragmentMsg = SingleLiveEvent() + + private val _successFragmentMsg = SingleLiveEvent() + + private val _currencyText = SingleLiveEvent() + + val tapCardStatus = MutableLiveData(1) + + val tapDeviceStatus = MutableLiveData(1) + + val insertCardStatus = MutableLiveData(1) + + val swipeCardStatus = MutableLiveData(0) + + val countDownTxt = MutableLiveData() + + val mmqrLoading = MutableLiveData() + + val isMMPay = MutableLiveData() + + val isWavePay = MutableLiveData() + + val mockData = SingleLiveEvent() + + val qrPayVisibility = MutableLiveData() + + val loadingView = MutableLiveData(8) + + val loadingMsg = SingleLiveEvent() + + private val isFallback = SingleLiveEvent() + + private val _isCardDataExist = SingleLiveEvent() + + private val _isAmountExist = SingleLiveEvent() + + private var mPayDetail = PayDetail() + + init { + setPrintStatus(PrintStatus.FIRST_PRINT) + isReprint.value = false + cardNo.value = "" + } + + fun setSettlementStatus(status: Boolean) { + settlementStatus.value = status + } + + fun getSettlementStatus(): MutableLiveData { + return settlementStatus + } + + fun setWavePayStatus(status: Boolean) { + wavePayStatus.value = status + } + + fun getWavePayStatus(): MutableLiveData { + return wavePayStatus + } + + fun setEmvTrans(status: Boolean) { + isEmv.value = status + } + + fun isEmvTrans(): SingleLiveEvent { + return isEmv + } + + private fun getPayDetail(): PayDetail { + return mPayDetail + } + + private fun cachePayDetail(payDetail: PayDetail) { + mPayDetail = payDetail + } + + fun setPrintReceiptButtons(visible: Boolean) { + printReceiptButtons.value = if (visible) 0 else 8 + } + + fun postPrintReceiptButtons(visible: Boolean) { + printReceiptButtons.postValue(if (visible) 0 else 8) + } + + fun getPrintReceiptButtons(): MutableLiveData { + return printReceiptButtons + } + + fun getReprintBtnLayout(): MutableLiveData { + return reprintBtnLayout + } + + fun setReprintBtnLayout(visible: Boolean) { + reprintBtnLayout.value = if (visible) 0 else 8 + } + + fun setPrintReceiptMsg(msg: String) { + printReceiptMsg.value = msg + } + + fun postPrintReceiptMsg(msg: String) { + printReceiptMsg.postValue(msg) + } + + fun setPrintStatus(printStatus: PrintStatus) { + this.printStatus.value = printStatus + } + + fun postPrintStatus(printStatus: PrintStatus) { + this.printStatus.postValue(printStatus) + } + + fun getPrintStatusEvent(): SingleLiveEvent { + return printStatus + } + + fun setIsFallback(status: Boolean) { + isFallback.value = status + } + + fun getIsFallback(): SingleLiveEvent { + return isFallback + } + + fun setCardDataExist(exist: Boolean) { + _isCardDataExist.value = exist + } + + fun getCardDataExist(): SingleLiveEvent { + return _isCardDataExist + } + + fun setAmountExist(exist: Boolean) { + _isAmountExist.value = exist + } + + fun getAmountExist(): SingleLiveEvent { + return _isAmountExist + } + + fun getManualEntryStatus(): MutableLiveData { + return _manualEntryStatus + } + + fun setManualEntryStatus(status: Boolean) { + _manualEntryStatus.value = status + } + + fun getErrorFragmentMsg(): SingleLiveEvent { + return _errorFragmentMsg + } + + fun set_errorFragmentMsg(msg: String) { + _errorFragmentMsg.value = msg + } + + fun getSuccessFragmentMsg(): SingleLiveEvent { + return _successFragmentMsg + } + + fun set_currencyText(msg: String) { + _currencyText.value = msg + } + + fun get_currencyText(): SingleLiveEvent { + return _currencyText + } + + fun set_successFragmentMsg(msg: String) { + _successFragmentMsg.value = msg + } + + fun getTransMenu(): SingleLiveEvent { + return _transMenu + } + + fun setTransMenu(transMenu: TransMenu?) { + _transMenu.value = transMenu + } + + fun loadingMsg(msg: String) { + loadingView.value = 0 + loadingMsg.value = msg + } + + fun dismissLoadingMsg() { + loadingView.value = 8 + loadingMsg.value = "" + } + + fun getParams(siriusRequest: SiriusRequest): Observable { + return repository.getParams(siriusRequest) + } + + private fun printReceipt(isMerchantCopy: Boolean) { + PrintXReceipt.getInstance() + .printSmileReceipt(payDetail.value, isMerchantCopy, object : PrintXStatus { + + override fun onSuccess() { + + if (isMerchantCopy) { + + if (!SystemParamsOperation.getInstance().demoStatus) { + if (SystemParamsOperation.getInstance().printISOStatus) { + PrintXReceipt.getInstance() + .printSmileISoReceipt(payDetail.value) + } + } + + postPrintStatus(PrintStatus.FIRST_PRINT_DONE) + + } else { + setPrintStatus(PrintStatus.SECOND_PRINT_DONE) + } + } + + override fun onFailure() { + LogUtil.d(TAG, "Print Status Result Failure!") + printXStatus.postValue(PrintStatus.EMPTY_PAPER_ROLL) + postPrintReceiptButtons(true) + } + }) + } + + fun startPrintReceipt(isFirstPrint: Boolean) { + + payDetail.value?.let { + cachePayDetail(it) + } + + PrintXReceipt.getInstance() + .printSmileReceipt(getPayDetail(), isFirstPrint, object : PrintXStatus { + + override fun onSuccess() { + + if (isFirstPrint) { + + if (!SystemParamsOperation.getInstance().demoStatus) { + if (SystemParamsOperation.getInstance().printISOStatus) { + PrintXReceipt.getInstance() + .printSmileISoReceipt(getPayDetail()) + } + } + + postPrintStatus(PrintStatus.FIRST_PRINT_DONE) + + if (isEcr.value == true && + SystemParamsOperation.getInstance() + .isAutoPrintCustomerCopy + ) { + printReceiptButtons.postValue(8) + } else { + printReceiptButtons.postValue(0) + } + + } else { + postPrintStatus(PrintStatus.SECOND_PRINT_DONE) + } + } + + override fun onFailure() { + LogUtil.d(TAG, "Print Status Result Failure!") + + if (isFirstPrint) { + setPrintStatus(PrintStatus.EMPTY_PAPER_ROLL_FIRST) + } else { + setPrintStatus(PrintStatus.EMPTY_PAPER_ROLL_SECOND) + } + + postPrintReceiptButtons(true) + } + }) + } + + fun startPrintProcess() { + + LogUtil.d(TAG, "Print status : ${getPrintStatusEvent().value}") + + when (getPrintStatusEvent().value) { + + PrintStatus.FIRST_PRINT -> { + printReceipt(true) + postPrintReceiptMsg("Printing Receipt for Merchant") + postPrintReceiptButtons(false) + } + + PrintStatus.SECOND_PRINT -> { + printReceipt(false) + postPrintReceiptMsg("Printing Receipt for Customer") + } + + PrintStatus.NOT_PRINT -> { + } + + else -> {} + } + } + + fun startPrintProcessSettlement() { + + val detail = payDetail.value ?: return + + PrintXReceipt.getInstance() + .printSmileSettlementReport(detail, object : PrintXStatus { + + override fun onSuccess() { + } + + override fun onFailure() { + } + }) + } + + fun getLastThreeTransactions(): LiveData> { + return repository.getLastThreeTransactions() + } + + fun getReversalTransaction(voucherNo: String): LiveData { + return repository.getReversalTransaction(voucherNo) + } + + fun updatePayDetail(payDetail: PayDetail) { + repository.updatePayDetail(payDetail) + } + + fun insertPayDetail(payDetail: PayDetail) { + repository.insertPayDetail(payDetail) + } + + fun enableCardStatusIcon( + tapCard: Boolean, + tapDevice: Boolean, + insertCard: Boolean, + swipeCard: Boolean + ) { + + tapCardStatus.value = if (tapCard) 1 else 0 + tapDeviceStatus.value = if (tapDevice) 1 else 0 + insertCardStatus.value = if (insertCard) 1 else 0 + swipeCardStatus.value = if (swipeCard) 1 else 0 + } + + fun resetParamsMain() { + + isEcrFinished.postValue(true) + isEcr.postValue(false) + + setAmountExist(false) + setCardDataExist(false) + setTransMenu(null) + } + + fun updateButtonStatus() { + setSettlementStatus( + SystemParamsOperation.getInstance().settlementStatus + ) + + setWavePayStatus( + SystemParamsOperation.getInstance().wavePayStatus + ) + } +} diff --git a/app/src/main/java/com/mob/utsmyanmar/viewmodel/TransProcessViewModel.kt b/app/src/main/java/com/mob/utsmyanmar/viewmodel/TransProcessViewModel.kt new file mode 100644 index 0000000..16b1edd --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/viewmodel/TransProcessViewModel.kt @@ -0,0 +1,346 @@ +package com.mob.utsmyanmar.viewmodel + +import android.text.TextUtils +import androidx.lifecycle.ViewModel +import com.mob.utsmyanmar.model.TransResultStatus +import com.utsmyanmar.baselib.repo.Repository +import com.utsmyanmar.paylibs.model.PayDetail +import com.utsmyanmar.paylibs.model.TradeData +import com.utsmyanmar.paylibs.network.ISOSocket +import com.utsmyanmar.paylibs.reversal.ReversalAction +import com.utsmyanmar.paylibs.reversal.ReversalListener +import com.utsmyanmar.paylibs.system.SystemDateTime +import com.utsmyanmar.paylibs.transactions.TransactionsOperation +import com.utsmyanmar.paylibs.transactions.TransactionsOperationListener +import com.utsmyanmar.paylibs.utils.PrintStatus +import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation +import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class TransProcessViewModel @Inject constructor( + private val repository: Repository +) : ViewModel(), ProcessingTransaction { + + companion object { + private const val RC_APPROVED_V1 = "00" + private const val RC_APPROVED_V2 = "000" + private const val TRY_SECONDARY = "TRY_SECONDARY" + } + + private var pan: String = "" + + private lateinit var tradeData: TradeData + + var payDetail: PayDetail? = null + + private var oldTransPayDetail: PayDetail? = null + + private var isSecondCall = false + private var isThirdCall = false + + /* + * States + */ + + private val _transResultStatus = + MutableStateFlow(null) + + val transResultStatus = + _transResultStatus.asStateFlow() + + private val _transType = + MutableStateFlow(null) + + val transType = + _transType.asStateFlow() + + private val _printStatus = + MutableStateFlow(PrintStatus.FIRST_PRINT) + + val printStatus = + _printStatus.asStateFlow() + + private val _payDetailResult = + MutableStateFlow(null) + + val payDetailResult = + _payDetailResult.asStateFlow() + + private val _errorMessage = + MutableStateFlow(null) + + val errorMessage = + _errorMessage.asStateFlow() + + /* + * Setup + */ + + fun setTradeData(tradeData: TradeData) { + this.tradeData = tradeData + payDetail = tradeData.payDetail + pan = payDetail?.cardNo ?: "" + } + + fun getTradeData(): TradeData { + return tradeData + } + + fun updatePayDetail(payDetail: PayDetail) { + this.payDetail = payDetail + + tradeData = TradeData().apply { + this.payDetail = payDetail + } + } + + fun setOldTransPayDetail(payDetail: PayDetail) { + oldTransPayDetail = payDetail + } + + /* + * Transaction + */ + + override fun startOnlineProcess() { + + TransactionsOperation.getInstance() + .getStartOperation( + tradeData, + transType.value + ) + .checkOperation(object : TransactionsOperationListener { + + override fun onSuccess(tradeData: TradeData) { + + val payDetailRes = + tradeData.payDetail + + if ( + TextUtils.equals( + payDetailRes.tradeAnswerCode, + RC_APPROVED_V1 + ) || + TextUtils.equals( + payDetailRes.tradeAnswerCode, + RC_APPROVED_V2 + ) + ) { + + payDetailRes.invoiceNo = + SystemParamsOperation + .getInstance() + .incrementInvoiceNum + + when (transType.value) { + + TransactionsType.VOID -> { + processVoidDB(payDetailRes) + } + + TransactionsType.REFUND -> { + processRefundDB(payDetailRes) + } + + TransactionsType.PRE_AUTH_VOID -> { + processPreVoidDb(payDetailRes) + } + + TransactionsType.PRE_AUTH_COMPLETE -> { + processPreCompDb(payDetailRes) + } + + TransactionsType.PRE_AUTH_COMPLETE_VOID -> { + processPreCompVoidDb(payDetailRes) + } + + else -> { + insertDB(payDetailRes) + } + } + + _payDetailResult.value = + payDetailRes + + _transResultStatus.value = + TransResultStatus.SUCCESS + + } else { + + _payDetailResult.value = + payDetailRes + + _transResultStatus.value = + TransResultStatus.FAIL + } + } + + override fun onReversal( + tradeData: TradeData + ) { + + _transResultStatus.value = + TransResultStatus.REVERSAL_PREPARE + + callReversal(tradeData) + } + + override fun onError(message: String) { + + if (message == TRY_SECONDARY) { + + _transResultStatus.value = + TransResultStatus.SECONDARY + + startOnlineProcess() + + } else { + + _errorMessage.value = message + + _transResultStatus.value = + TransResultStatus.ERROR + } + } + }) + } + + /* + * Database + */ + + override fun insertDB(payResult: PayDetail) { + + payDetail?.pinCipher = "" + + repository.insertPayDetail( + payDetail + ) + } + + override fun processVoidDB(payResult: PayDetail) { + + payDetail?.isCanceled = true + + payDetail?.let { + repository.updatePayDetail(it) + } + + repository.insertPayDetail( + updateCurrentDateAndTime(payResult) + ) + } + + override fun processRefundDB(payResult: PayDetail) { + + oldTransPayDetail?.apply { + isReturnGood = true + isCanceled = true + + repository.updatePayDetail(this) + } + + repository.insertPayDetail( + updateCurrentDateAndTime(payResult) + ) + } + + override fun processPreVoidDb(payResult: PayDetail) {} + override fun processPreCompDb(payResult: PayDetail) {} + override fun processPreCompVoidDb(payResult: PayDetail) {} + + /* + * Reversal + */ + + private fun callReversal(tradeData: TradeData) { + + payDetail = tradeData.payDetail + + ReversalAction.getInstance() + .setData(tradeData) + .enqueue() + .startReversal(object : ReversalListener { + + override fun onSuccessReversal() { + + _transResultStatus.value = + TransResultStatus.REVERSAL_SUCCESS + + payDetail?.let { + repository.insertPayDetail(it) + } + } + + override fun onNetworkFail(msg: String) { + + SystemParamsOperation + .getInstance() + .setSecondHostEnable(true) + + if ( + SystemParamsOperation + .getInstance() + .isSecondHostEnabled + ) { + + if (!isSecondCall) { + + _transResultStatus.value = + TransResultStatus.REVERSAL_SECONDARY + + ISOSocket.getInstance() + .switchIp() + + isSecondCall = true + + callReversal(tradeData) + + } else { + + _transResultStatus.value = + TransResultStatus.REVERSAL_FAIL + } + + } else { + + _transResultStatus.value = + TransResultStatus.REVERSAL_FAIL + } + } + + override fun onFailReversal(msg: String) { + + _transResultStatus.value = + TransResultStatus.REVERSAL_FAIL + } + }) + } + + /* + * Utils + */ + + private fun updateCurrentDateAndTime( + payDetail: PayDetail + ): PayDetail { + + payDetail.tradeDate = + SystemDateTime.getMMDD() + + payDetail.tradeTime = + SystemDateTime.getHHmmss() + + return payDetail + } + + override fun resetTransactionStatus() { + _transResultStatus.value = null + } + + override fun getTransStatus() = TODO() +} diff --git a/baselib/src/main/java/com/utsmyanmar/baselib/BaseApplication.java b/baselib/src/main/java/com/utsmyanmar/baselib/BaseApplication.java index 2bf3f31..1efb57b 100644 --- a/baselib/src/main/java/com/utsmyanmar/baselib/BaseApplication.java +++ b/baselib/src/main/java/com/utsmyanmar/baselib/BaseApplication.java @@ -98,13 +98,16 @@ public class BaseApplication extends Application { mPinPadOptV2 = sunmiPayKernel.mPinPadOptV2; mReadCardOptV2 = sunmiPayKernel.mReadCardOptV2; mSecurityOptV2 = sunmiPayKernel.mSecurityOptV2; - //init - - initTerminal(); PayLibsUtils.getInstance().initLib(mSecurityOptV2,mEMVOptV2,securityOpt,mReadCardOptV2); SunmiSDK.getInstance().initSDK(mReadCardOptV2,basicOptV2); + try { + initTerminal(); + } catch (Exception terminalInitException) { + terminalInitException.printStackTrace(); + } + } catch (Exception e) { e.printStackTrace(); diff --git a/baselib/src/main/res/values/colors.xml b/baselib/src/main/res/values/colors.xml index f5439cf..25b9949 100644 --- a/baselib/src/main/res/values/colors.xml +++ b/baselib/src/main/res/values/colors.xml @@ -4,7 +4,7 @@ #222222 #666666 #999999 - # + #DDDDDD #FEE135 #D0312D #FD5A52 @@ -19,4 +19,4 @@ #dddddd #FFFFFF - \ No newline at end of file + diff --git a/build.gradle.kts b/build.gradle.kts index 7548a4a..76f463a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false - alias(libs.plugins.hilt.android) apply false alias(libs.plugins.kotlin.kapt) apply false + id("com.google.dagger.hilt.android") version "2.59.2" apply false + id("com.google.devtools.ksp") version "2.3.4" apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2e222ec..7563956 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] agp = "9.2.0" coreKtx = "1.18.0" +hiltAndroidCompiler = "2.59.2" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" @@ -11,10 +12,15 @@ composeBom = "2026.02.01" navigationCompose = "2.9.8" hilt = "2.57.1" foundation = "1.11.1" +runtime = "1.11.1" +rxandroid = "3.0.2" +rxjava = "3.1.12" +hiltNavigationCompose = "1.2.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } +hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -31,11 +37,14 @@ androidx-compose-material3 = { group = "androidx.compose.material3", name = "mat hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" } +androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "runtime" } +rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" } +rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" } +androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } diff --git a/mpu-lib/build/.transforms/1dc9ee087b8688559ab13789bf87db3e/results.bin b/mpu-lib/build/.transforms/1dc9ee087b8688559ab13789bf87db3e/results.bin new file mode 100644 index 0000000..c34496d --- /dev/null +++ b/mpu-lib/build/.transforms/1dc9ee087b8688559ab13789bf87db3e/results.bin @@ -0,0 +1 @@ +o/mpulib-1.2-runtime diff --git a/mpu-lib/build/.transforms/1dc9ee087b8688559ab13789bf87db3e/transformed/mpulib-1.2-runtime/mpulib-1.2-runtime_dex/classes.dex b/mpu-lib/build/.transforms/1dc9ee087b8688559ab13789bf87db3e/transformed/mpulib-1.2-runtime/mpulib-1.2-runtime_dex/classes.dex new file mode 100644 index 0000000..9bb2ba0 Binary files /dev/null and b/mpu-lib/build/.transforms/1dc9ee087b8688559ab13789bf87db3e/transformed/mpulib-1.2-runtime/mpulib-1.2-runtime_dex/classes.dex differ diff --git a/paysdk-lib/build/.transforms/062ef812db3ec2ffd42a09209f2c2a5e/results.bin b/paysdk-lib/build/.transforms/062ef812db3ec2ffd42a09209f2c2a5e/results.bin new file mode 100644 index 0000000..c790d94 --- /dev/null +++ b/paysdk-lib/build/.transforms/062ef812db3ec2ffd42a09209f2c2a5e/results.bin @@ -0,0 +1 @@ +o/PayLib-release-1.4.64-runtime diff --git a/paysdk-lib/build/.transforms/062ef812db3ec2ffd42a09209f2c2a5e/transformed/PayLib-release-1.4.64-runtime/PayLib-release-1.4.64-runtime_dex/classes.dex b/paysdk-lib/build/.transforms/062ef812db3ec2ffd42a09209f2c2a5e/transformed/PayLib-release-1.4.64-runtime/PayLib-release-1.4.64-runtime_dex/classes.dex new file mode 100644 index 0000000..2dc28a9 Binary files /dev/null and b/paysdk-lib/build/.transforms/062ef812db3ec2ffd42a09209f2c2a5e/transformed/PayLib-release-1.4.64-runtime/PayLib-release-1.4.64-runtime_dex/classes.dex differ diff --git a/paysdk-lib/build/.transforms/eb46e7d31217f72d46dae54cad665c07/results.bin b/paysdk-lib/build/.transforms/eb46e7d31217f72d46dae54cad665c07/results.bin new file mode 100644 index 0000000..c790d94 --- /dev/null +++ b/paysdk-lib/build/.transforms/eb46e7d31217f72d46dae54cad665c07/results.bin @@ -0,0 +1 @@ +o/PayLib-release-1.4.64-runtime diff --git a/paysdk-lib/build/.transforms/eb46e7d31217f72d46dae54cad665c07/transformed/PayLib-release-1.4.64-runtime/PayLib-release-1.4.64-runtime_dex/classes.dex b/paysdk-lib/build/.transforms/eb46e7d31217f72d46dae54cad665c07/transformed/PayLib-release-1.4.64-runtime/PayLib-release-1.4.64-runtime_dex/classes.dex new file mode 100644 index 0000000..2dc28a9 Binary files /dev/null and b/paysdk-lib/build/.transforms/eb46e7d31217f72d46dae54cad665c07/transformed/PayLib-release-1.4.64-runtime/PayLib-release-1.4.64-runtime_dex/classes.dex differ diff --git a/sunmiui-lib/build/.transforms/0761bc4d0c7f4f1381da14166fb53562/results.bin b/sunmiui-lib/build/.transforms/0761bc4d0c7f4f1381da14166fb53562/results.bin new file mode 100644 index 0000000..800956c --- /dev/null +++ b/sunmiui-lib/build/.transforms/0761bc4d0c7f4f1381da14166fb53562/results.bin @@ -0,0 +1 @@ +o/sunmiui-1.1.27-runtime diff --git a/sunmiui-lib/build/.transforms/0761bc4d0c7f4f1381da14166fb53562/transformed/sunmiui-1.1.27-runtime/sunmiui-1.1.27-runtime_dex/classes.dex b/sunmiui-lib/build/.transforms/0761bc4d0c7f4f1381da14166fb53562/transformed/sunmiui-1.1.27-runtime/sunmiui-1.1.27-runtime_dex/classes.dex new file mode 100644 index 0000000..a06cd2e Binary files /dev/null and b/sunmiui-lib/build/.transforms/0761bc4d0c7f4f1381da14166fb53562/transformed/sunmiui-1.1.27-runtime/sunmiui-1.1.27-runtime_dex/classes.dex differ diff --git a/sunmiui-lib/build/.transforms/b3f7de568906871cda6fe2f5fb4f398e/results.bin b/sunmiui-lib/build/.transforms/b3f7de568906871cda6fe2f5fb4f398e/results.bin new file mode 100644 index 0000000..800956c --- /dev/null +++ b/sunmiui-lib/build/.transforms/b3f7de568906871cda6fe2f5fb4f398e/results.bin @@ -0,0 +1 @@ +o/sunmiui-1.1.27-runtime diff --git a/sunmiui-lib/build/.transforms/b3f7de568906871cda6fe2f5fb4f398e/transformed/sunmiui-1.1.27-runtime/sunmiui-1.1.27-runtime_dex/classes.dex b/sunmiui-lib/build/.transforms/b3f7de568906871cda6fe2f5fb4f398e/transformed/sunmiui-1.1.27-runtime/sunmiui-1.1.27-runtime_dex/classes.dex new file mode 100644 index 0000000..a06cd2e Binary files /dev/null and b/sunmiui-lib/build/.transforms/b3f7de568906871cda6fe2f5fb4f398e/transformed/sunmiui-1.1.27-runtime/sunmiui-1.1.27-runtime_dex/classes.dex differ diff --git a/xpay/src/main/java/com/kizzy/xpay/util/Sign.kt b/xpay/src/main/java/com/kizzy/xpay/util/Sign.kt index 7e574a6..4a4bf2e 100644 --- a/xpay/src/main/java/com/kizzy/xpay/util/Sign.kt +++ b/xpay/src/main/java/com/kizzy/xpay/util/Sign.kt @@ -2,6 +2,7 @@ package com.kizzy.xpay.util import java.security.MessageDigest import java.util.Locale +import java.util.Locale.getDefault object Sign { @@ -13,7 +14,7 @@ object Sign { val sorted = filtered.toSortedMap() val stringA = sorted.entries.joinToString("&") {"${it.key}=${it.value}"} val stringToSign = "$stringA&key=$appKey" - val hash = stringToSign.hashedWithSha256().toUpperCase() + val hash = stringToSign.hashedWithSha256().uppercase(getDefault()) return hash; }