new ViewModels added
This commit is contained in:
parent
d3a6ddbc37
commit
37a1a2e38d
13
app/src/main/java/com/mob/utsmyanmar/config/Constants.kt
Normal file
13
app/src/main/java/com/mob/utsmyanmar/config/Constants.kt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package com.mob.utsmyanmar.config
|
||||||
|
|
||||||
|
import com.mob.utsmyanmar.R
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
const val WALLET = "WALLET"
|
||||||
|
const val MPU_CARD_SCHEME = "MPU"
|
||||||
|
const val TIMEOUT = 30
|
||||||
|
const val PROCESSING_TIMEOUT = 180
|
||||||
|
const val PIN_PAD_TIMEOUT = 60
|
||||||
|
const val REVERSAL = "REVERSAL"
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package com.mob.utsmyanmar.config
|
||||||
|
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.security.SecurityOptV2
|
||||||
|
import jakarta.inject.Inject
|
||||||
|
import jakarta.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class SunmiPayManager @Inject constructor(){
|
||||||
|
var pinPadOptV2: PinPadOptV2? = null
|
||||||
|
var securityOptV2: SecurityOptV2? = null
|
||||||
|
|
||||||
|
fun isReady(): Boolean {
|
||||||
|
return pinPadOptV2 != null && securityOptV2 != null
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/src/main/java/com/mob/utsmyanmar/model/PinPadStatus.kt
Normal file
12
app/src/main/java/com/mob/utsmyanmar/model/PinPadStatus.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.mob.utsmyanmar.model
|
||||||
|
|
||||||
|
enum class PinPadStatus {
|
||||||
|
ON_CONFIRM,
|
||||||
|
ON_CANCEL ,
|
||||||
|
ON_EMPTY,
|
||||||
|
ON_ERROR,
|
||||||
|
ON_TIMEOUT,
|
||||||
|
ON_NEXT_SCREEN,
|
||||||
|
ON_CARD_REMOVED,
|
||||||
|
ON_ERROR_DUKPT,
|
||||||
|
}
|
||||||
@ -1,15 +1,28 @@
|
|||||||
package com.mob.utsmyanmar.ui.cardwaiting
|
package com.mob.utsmyanmar.ui.cardwaiting
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@ -17,12 +30,17 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.mob.utsmyanmar.R
|
||||||
|
import com.mob.utsmyanmar.ui.theme.Black
|
||||||
|
import com.mob.utsmyanmar.ui.theme.Primary
|
||||||
import com.mob.utsmyanmar.ui.theme.White
|
import com.mob.utsmyanmar.ui.theme.White
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CardWaitingScreen(
|
fun CardWaitingScreen(
|
||||||
viewModel: CardWaitingViewModel,
|
viewModel: CardWaitingViewModel,
|
||||||
@ -60,34 +78,158 @@ fun CardWaitingScreen(
|
|||||||
viewModel.onBackPressed()
|
viewModel.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Scaffold(
|
||||||
modifier = Modifier
|
topBar = {
|
||||||
.fillMaxSize()
|
CenterAlignedTopAppBar(
|
||||||
.background(White)
|
title = {
|
||||||
.padding(24.dp),
|
Text(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
text = "CARD CAPTURE",
|
||||||
) {
|
color = White,
|
||||||
Text(
|
fontSize = 16.sp,
|
||||||
text = "Card Capture",
|
fontWeight = FontWeight.SemiBold
|
||||||
fontSize = 22.sp,
|
)
|
||||||
fontWeight = FontWeight.Bold
|
},
|
||||||
)
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onBack) {
|
||||||
Spacer(modifier = Modifier.height(40.dp))
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_left_arrow),
|
||||||
Text(
|
contentDescription = "Back",
|
||||||
text = uiState.alertMessage,
|
tint = White
|
||||||
fontSize = 20.sp,
|
)
|
||||||
textAlign = TextAlign.Center
|
}
|
||||||
)
|
},
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
containerColor = Primary
|
||||||
|
)
|
||||||
Button(
|
)
|
||||||
onClick = viewModel::onManualEntryClick,
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
containerColor = White
|
||||||
|
) { paddingValues ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues)
|
||||||
|
.background(White)
|
||||||
) {
|
) {
|
||||||
Text("Manual Entry")
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(
|
||||||
|
color = Primary,
|
||||||
|
shape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||||
|
)
|
||||||
|
.padding(18.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = White,
|
||||||
|
shape = RoundedCornerShape(24.dp)
|
||||||
|
)
|
||||||
|
.padding(horizontal = 22.dp, vertical = 28.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(92.dp)
|
||||||
|
.background(
|
||||||
|
color = Primary.copy(alpha = 0.08f),
|
||||||
|
shape = RoundedCornerShape(28.dp)
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.buy_ecommerce_finance_payment_pos_shop_svgrepo_com),
|
||||||
|
contentDescription = "Card reader"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = if (uiState.isFallback) {
|
||||||
|
"Swipe Card"
|
||||||
|
} else {
|
||||||
|
"Present Card"
|
||||||
|
},
|
||||||
|
color = Black,
|
||||||
|
fontSize = 24.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = uiState.alertMessage,
|
||||||
|
color = Black,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
lineHeight = 26.sp,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = White.copy(alpha = 0.12f),
|
||||||
|
shape = RoundedCornerShape(22.dp)
|
||||||
|
)
|
||||||
|
.padding(horizontal = 18.dp, vertical = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = if (uiState.isLoading) {
|
||||||
|
"Reader status"
|
||||||
|
} else {
|
||||||
|
"Ready for card"
|
||||||
|
},
|
||||||
|
color = White,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = if (uiState.isLoading) "Initializing" else "Waiting",
|
||||||
|
color = White,
|
||||||
|
fontSize = 15.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = viewModel::onManualEntryClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(64.dp),
|
||||||
|
shape = RoundedCornerShape(18.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = White,
|
||||||
|
contentColor = Primary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Manual Entry",
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.mob.utsmyanmar.ui.cardwaiting
|
package com.mob.utsmyanmar.ui.cardwaiting
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
@ -173,6 +174,7 @@ class CardWaitingViewModel(
|
|||||||
65,
|
65,
|
||||||
object : CheckCardResultX {
|
object : CheckCardResultX {
|
||||||
override fun onSuccess(cardType: CardTypeX, isMPU: Boolean) {
|
override fun onSuccess(cardType: CardTypeX, isMPU: Boolean) {
|
||||||
|
Log.d("CardWaitingViewModel", "on success cardType=$cardType and isMpu=$isMPU")
|
||||||
when {
|
when {
|
||||||
!isFallback && cardType == CardTypeX.MAG -> {
|
!isFallback && cardType == CardTypeX.MAG -> {
|
||||||
if (SystemParamsOperation.getInstance().isMagStripeEnabled) {
|
if (SystemParamsOperation.getInstance().isMagStripeEnabled) {
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
package com.mob.utsmyanmar.ui.processing_card
|
||||||
|
|
||||||
|
sealed interface ProcessingCardEvent {
|
||||||
|
data object StartProcess : ProcessingCardEvent
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.mob.utsmyanmar.ui.processing_card
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ProcessingCardRoute(
|
||||||
|
viewModel: ProcessingCardViewModel = hiltViewModel(),
|
||||||
|
onNavigatePinPad: () -> Unit,
|
||||||
|
onNavigateInputAmount: () -> Unit,
|
||||||
|
onNavigateProcessing: () -> Unit,
|
||||||
|
onNavigateEmvTransaction: () -> Unit,
|
||||||
|
onNavigateError: (String) -> Unit,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
onShowDecline: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.onEvent(ProcessingCardEvent.StartProcess)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.uiEvent.collect { event ->
|
||||||
|
when (event) {
|
||||||
|
ProcessingCardUiEvent.NavigateToPinPad -> onNavigatePinPad()
|
||||||
|
ProcessingCardUiEvent.NavigateToInputAmount -> onNavigateInputAmount()
|
||||||
|
ProcessingCardUiEvent.NavigateToProcessing -> onNavigateProcessing()
|
||||||
|
ProcessingCardUiEvent.NavigateToEmvTransaction -> onNavigateEmvTransaction()
|
||||||
|
is ProcessingCardUiEvent.NavigateToError -> onNavigateError(event.message)
|
||||||
|
is ProcessingCardUiEvent.ShowDeclineAndBack -> {
|
||||||
|
onShowDecline(event.message)
|
||||||
|
onBack()
|
||||||
|
}
|
||||||
|
ProcessingCardUiEvent.Back -> onBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessingCardScreen(state = state)
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.mob.utsmyanmar.ui.processing_card
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ProcessingCardScreen(
|
||||||
|
state: ProcessingCardState
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
if (state.isLoading) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(text = state.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.mob.utsmyanmar.ui.processing_card
|
||||||
|
|
||||||
|
data class ProcessingCardState(
|
||||||
|
val title: String = "Processing Card",
|
||||||
|
val message: String = "Please wait...",
|
||||||
|
val isLoading: Boolean = true
|
||||||
|
)
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.mob.utsmyanmar.ui.processing_card
|
||||||
|
|
||||||
|
sealed interface ProcessingCardUiEvent {
|
||||||
|
data object NavigateToPinPad : ProcessingCardUiEvent
|
||||||
|
data object NavigateToInputAmount : ProcessingCardUiEvent
|
||||||
|
data object NavigateToProcessing : ProcessingCardUiEvent
|
||||||
|
data object NavigateToEmvTransaction : ProcessingCardUiEvent
|
||||||
|
data class NavigateToError(val message: String) : ProcessingCardUiEvent
|
||||||
|
data class ShowDeclineAndBack(val message: String) : ProcessingCardUiEvent
|
||||||
|
data object Back : ProcessingCardUiEvent
|
||||||
|
}
|
||||||
@ -0,0 +1,265 @@
|
|||||||
|
package com.mob.utsmyanmar.ui.processing_card
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.mob.utsmyanmar.model.CardTransactionType
|
||||||
|
import com.mob.utsmyanmar.utils.TransactionUtil
|
||||||
|
import com.mob.utsmyanmar.viewmodel.CardReaderViewModel
|
||||||
|
import com.mob.utsmyanmar.viewmodel.EmvTransactionProcessViewModel
|
||||||
|
import com.mob.utsmyanmar.viewmodel.PinPadViewModel
|
||||||
|
import com.mob.utsmyanmar.viewmodel.SharedViewModel
|
||||||
|
import com.mob.utsmyanmar.viewmodel.TransProcessViewModel
|
||||||
|
import com.utsmyanmar.checkxread.model.CardDataX
|
||||||
|
import com.utsmyanmar.checkxread.readcard.MAGXReadCard
|
||||||
|
import com.utsmyanmar.checkxread.readcard.MPUXReadCard
|
||||||
|
import com.utsmyanmar.checkxread.readcard.ReadCardResultX
|
||||||
|
import com.utsmyanmar.checkxread.util.CardTypeX
|
||||||
|
import com.utsmyanmar.paylibs.model.PayDetail
|
||||||
|
import com.utsmyanmar.paylibs.model.TradeData
|
||||||
|
import com.utsmyanmar.paylibs.model.enums.TransCVM
|
||||||
|
import com.utsmyanmar.paylibs.utils.POSUtil
|
||||||
|
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
|
||||||
|
import com.utsmyanmar.paylibs.utils.enums.TransMenu
|
||||||
|
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
|
||||||
|
import com.utsmyanmar.paylibs.utils.params.Params
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import sunmi.sunmiui.utils.LogUtil
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ProcessingCardViewModel @Inject constructor(
|
||||||
|
private val cardReadViewModel: CardReaderViewModel,
|
||||||
|
private val sharedViewModel: SharedViewModel,
|
||||||
|
private val transProcessViewModel: TransProcessViewModel,
|
||||||
|
private val pinPadViewModel: PinPadViewModel,
|
||||||
|
private val emvTransactionViewModel: EmvTransactionProcessViewModel
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(ProcessingCardState())
|
||||||
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
|
private val _uiEvent = Channel<ProcessingCardUiEvent>()
|
||||||
|
val uiEvent = _uiEvent.receiveAsFlow()
|
||||||
|
|
||||||
|
fun onEvent(event: ProcessingCardEvent) {
|
||||||
|
when (event) {
|
||||||
|
ProcessingCardEvent.StartProcess -> {
|
||||||
|
resetEmvTransResult()
|
||||||
|
checkCardTransactionType()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetEmvTransResult() {
|
||||||
|
emvTransactionViewModel.resetTransactionStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCardTransactionType() {
|
||||||
|
when (cardReadViewModel.getCardTransactionType()) {
|
||||||
|
CardTransactionType.MPU -> readMPUCard()
|
||||||
|
CardTransactionType.EMV -> handlePreEmvProcess()
|
||||||
|
CardTransactionType.FALLBACK -> readMAGStripe(
|
||||||
|
isFallback = true,
|
||||||
|
isNeedPin = true
|
||||||
|
)
|
||||||
|
|
||||||
|
CardTransactionType.MAG -> readMAGStripe(
|
||||||
|
isFallback = false,
|
||||||
|
isNeedPin = true
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
Log.d(
|
||||||
|
"ProcessingCardViewModel",
|
||||||
|
"error on CardTransactionType : ${cardReadViewModel.getCardTransactionType()}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readMPUCard() {
|
||||||
|
cardReadViewModel.startReadXProcess(
|
||||||
|
MPUXReadCard.getInstance(),
|
||||||
|
object : ReadCardResultX {
|
||||||
|
override fun onSuccess(cardDataX: CardDataX) {
|
||||||
|
transProcessViewModel.setTransType(sharedViewModel.transactionsType.value)
|
||||||
|
pinPadViewModel.setTransType(sharedViewModel.transactionsType.value)
|
||||||
|
val tradeData = TransactionUtil.initMPUTransaction(cardDataX, CardTypeX.IC)
|
||||||
|
transProcessViewModel.setTradeData(tradeData)
|
||||||
|
pinPadViewModel.setTradeData(tradeData)
|
||||||
|
|
||||||
|
when {
|
||||||
|
sharedViewModel._transMenu.value == TransMenu.PRE_AUTH_PARTIAL_VOID -> {
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.NavigateToError(
|
||||||
|
"Function not supported"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedViewModel.transactionsType.value == TransactionsType.REFUND -> {
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.NavigateToError(
|
||||||
|
"Card not supported"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedViewModel.setCardDataExist(true)
|
||||||
|
|
||||||
|
if (sharedViewModel.getAmountExist().value == false) {
|
||||||
|
sendUiEvent(ProcessingCardUiEvent.NavigateToInputAmount)
|
||||||
|
} else {
|
||||||
|
sendUiEvent(ProcessingCardUiEvent.NavigateToPinPad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(code: Int, message: String) {
|
||||||
|
LogUtil.d(TAG, "Failure at $code message: $message")
|
||||||
|
sharedViewModel.setCardDataExist(false)
|
||||||
|
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.ShowDeclineAndBack(
|
||||||
|
"FAILURE :$message"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readMAGStripe(
|
||||||
|
isFallback: Boolean,
|
||||||
|
isNeedPin: Boolean
|
||||||
|
) {
|
||||||
|
cardReadViewModel.startReadXProcess(
|
||||||
|
MAGXReadCard.getInstance(),
|
||||||
|
object : ReadCardResultX {
|
||||||
|
override fun onSuccess(cardDataX: CardDataX) {
|
||||||
|
sharedViewModel.setEmvTrans(false)
|
||||||
|
|
||||||
|
if (isNeedPin) {
|
||||||
|
transProcessViewModel.setTransType(sharedViewModel.transactionsType.value)
|
||||||
|
pinPadViewModel.setTransType(sharedViewModel.transactionsType.value)
|
||||||
|
|
||||||
|
val tradeData = TransactionUtil.initMagStripeTransaction(cardDataX, isFallback)
|
||||||
|
|
||||||
|
transProcessViewModel.setTradeData(tradeData)
|
||||||
|
pinPadViewModel.setTradeData(tradeData)
|
||||||
|
} else {
|
||||||
|
transProcessViewModel.setTransType(sharedViewModel.transactionsType.value)
|
||||||
|
|
||||||
|
val tradeData = TransactionUtil.initMagStripeTransaction(cardDataX, isFallback)
|
||||||
|
val processCode = sharedViewModel.processCode.value
|
||||||
|
val amount = sharedViewModel.amount.value
|
||||||
|
val payDetail: PayDetail = tradeData.payDetail
|
||||||
|
|
||||||
|
payDetail.amount = POSUtil.getInstance().convertAmount(amount)
|
||||||
|
|
||||||
|
if (!TextUtils.equals(processCode, "")) {
|
||||||
|
payDetail.processCode = processCode
|
||||||
|
}
|
||||||
|
|
||||||
|
payDetail.transCVM = TransCVM.SIGNATURE
|
||||||
|
transProcessViewModel.setTradeData(tradeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
sharedViewModel.getTransMenu().value == TransMenu.PRE_AUTH_PARTIAL_VOID -> {
|
||||||
|
sharedViewModel.setTransMenu(null)
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.NavigateToError(
|
||||||
|
"Function not supported"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedViewModel.transactionsType.value == TransactionsType.REFUND -> {
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.NavigateToError(
|
||||||
|
"Card not supported"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedViewModel.setCardDataExist(true)
|
||||||
|
|
||||||
|
if (
|
||||||
|
sharedViewModel.getTransMenu().value != TransMenu.PRE_AUTH_FULL_VOID &&
|
||||||
|
sharedViewModel.getAmountExist().value == false
|
||||||
|
) {
|
||||||
|
sendUiEvent(ProcessingCardUiEvent.NavigateToInputAmount)
|
||||||
|
} else {
|
||||||
|
if (isNeedPin) {
|
||||||
|
sendUiEvent(ProcessingCardUiEvent.NavigateToPinPad)
|
||||||
|
} else {
|
||||||
|
sendUiEvent(ProcessingCardUiEvent.NavigateToProcessing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(code: Int, message: String) {
|
||||||
|
LogUtil.d(TAG, "Failure at $code message: $message")
|
||||||
|
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.ShowDeclineAndBack(
|
||||||
|
"FAILURE :$message"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePreEmvProcess() {
|
||||||
|
emvTransactionViewModel.transType.value =
|
||||||
|
sharedViewModel.transactionsType.value
|
||||||
|
|
||||||
|
if (SystemParamsOperation.getInstance().isEmvEnabled) {
|
||||||
|
prepareEmvTransaction()
|
||||||
|
sendUiEvent(ProcessingCardUiEvent.NavigateToEmvTransaction)
|
||||||
|
} else {
|
||||||
|
sendUiEvent(
|
||||||
|
ProcessingCardUiEvent.NavigateToError(
|
||||||
|
"Please enable EMV"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareEmvTransaction() {
|
||||||
|
sharedViewModel.setEmvTrans(true)
|
||||||
|
|
||||||
|
val cardType = cardReadViewModel.cardTypeData.value ?: return
|
||||||
|
|
||||||
|
val tradeData: TradeData = Params.newTrade(false)
|
||||||
|
val payDetail = tradeData.payDetail
|
||||||
|
|
||||||
|
payDetail.cardType = cardType
|
||||||
|
|
||||||
|
emvTransactionViewModel.setTradeData(tradeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendUiEvent(event: ProcessingCardUiEvent) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_uiEvent.send(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = ProcessingCardViewModel::class.java.simpleName
|
||||||
|
}
|
||||||
|
}
|
||||||
120
app/src/main/java/com/mob/utsmyanmar/utils/TransactionUtil.kt
Normal file
120
app/src/main/java/com/mob/utsmyanmar/utils/TransactionUtil.kt
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package com.mob.utsmyanmar.utils
|
||||||
|
|
||||||
|
import com.mob.utsmyanmar.config.Constants
|
||||||
|
import com.utsmyanmar.checkxread.model.CardDataX
|
||||||
|
import com.utsmyanmar.checkxread.util.CardTypeX
|
||||||
|
import com.utsmyanmar.paylibs.model.CardInfo
|
||||||
|
import com.utsmyanmar.paylibs.model.MAGCardInfo
|
||||||
|
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.utsmyanmar.paylibs.utils.params.Params
|
||||||
|
import sunmi.sunmiui.utils.LogUtil
|
||||||
|
|
||||||
|
object TransactionUtil {
|
||||||
|
private const val TAG = "TransactionUtil"
|
||||||
|
private const val MPU_CARD_SCHEME = "MPU"
|
||||||
|
private const val VISA_CARD_SCHEME = "VISA"
|
||||||
|
private const val MASTER_CARD_SCHEME = "MASTER"
|
||||||
|
private const val UPI_CARD_SCHEME = "UPI"
|
||||||
|
private val qrTerminalId: String
|
||||||
|
get() = SystemParamsOperation.getInstance().secHostTerminalId
|
||||||
|
|
||||||
|
private val qrMerchantId: String
|
||||||
|
get() = SystemParamsOperation.getInstance().secHostMerchantId
|
||||||
|
|
||||||
|
fun getQRMerchantId(): String {
|
||||||
|
return qrMerchantId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getQRTerminalId(): String {
|
||||||
|
return qrTerminalId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initMPUTransaction(
|
||||||
|
cardDataX: CardDataX,
|
||||||
|
cardTypeX: CardTypeX
|
||||||
|
): TradeData {
|
||||||
|
|
||||||
|
LogUtil.d(TAG, "CardDataX : $cardDataX")
|
||||||
|
|
||||||
|
val tradeData = Params.newTrade(false)
|
||||||
|
val payDetail = tradeData.payDetail
|
||||||
|
|
||||||
|
payDetail.cardNo = cardDataX.pan
|
||||||
|
payDetail.expDate = cardDataX.exp
|
||||||
|
payDetail.cardType = cardTypeX.value
|
||||||
|
payDetail.accountType = MPU_CARD_SCHEME
|
||||||
|
payDetail.cardHolderName = cardDataX.cardHolderName
|
||||||
|
|
||||||
|
val cardInfo = CardInfo()
|
||||||
|
|
||||||
|
val magCardInfo = MAGCardInfo()
|
||||||
|
magCardInfo.track2Cipher = cardDataX.track2
|
||||||
|
|
||||||
|
cardInfo.magCardInfo = magCardInfo
|
||||||
|
|
||||||
|
payDetail.cardInfo = cardInfo
|
||||||
|
|
||||||
|
return tradeData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initMagStripeTransaction(
|
||||||
|
cardDataX: CardDataX,
|
||||||
|
isFallback: Boolean
|
||||||
|
): TradeData {
|
||||||
|
|
||||||
|
LogUtil.d(TAG, "CardDataX : $cardDataX")
|
||||||
|
|
||||||
|
val tradeData = Params.newTrade(false)
|
||||||
|
val payDetail = tradeData.payDetail
|
||||||
|
|
||||||
|
payDetail.cardNo = cardDataX.pan
|
||||||
|
payDetail.expDate = cardDataX.exp
|
||||||
|
|
||||||
|
payDetail.accountType = when {
|
||||||
|
cardDataX.pan.startsWith("4") -> VISA_CARD_SCHEME
|
||||||
|
cardDataX.pan.startsWith("5") -> MASTER_CARD_SCHEME
|
||||||
|
cardDataX.pan.startsWith("6") -> UPI_CARD_SCHEME
|
||||||
|
else -> MPU_CARD_SCHEME
|
||||||
|
}
|
||||||
|
|
||||||
|
payDetail.cardType = if (isFallback) {
|
||||||
|
-9
|
||||||
|
} else {
|
||||||
|
CardTypeX.MAG.value
|
||||||
|
}
|
||||||
|
|
||||||
|
payDetail.cardHolderName = cardDataX.cardHolderName
|
||||||
|
|
||||||
|
val cardInfo = CardInfo()
|
||||||
|
|
||||||
|
val magCardInfo = MAGCardInfo()
|
||||||
|
magCardInfo.track2Cipher = cardDataX.track2
|
||||||
|
|
||||||
|
cardInfo.magCardInfo = magCardInfo
|
||||||
|
|
||||||
|
payDetail.cardInfo = cardInfo
|
||||||
|
|
||||||
|
return tradeData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initWalletTransaction(
|
||||||
|
transactionType: TransactionsType
|
||||||
|
): PayDetail {
|
||||||
|
|
||||||
|
val tradeData = Params.newTrade(true)
|
||||||
|
val payDetail = tradeData.payDetail
|
||||||
|
|
||||||
|
payDetail.accountType = Constants.WALLET
|
||||||
|
payDetail.transType = transactionType.name
|
||||||
|
payDetail.transactionType = transactionType.value
|
||||||
|
payDetail.terminalNo = qrTerminalId
|
||||||
|
payDetail.merchantNo = qrMerchantId
|
||||||
|
payDetail.currencyCode = "104"
|
||||||
|
payDetail.isCanceled = false
|
||||||
|
|
||||||
|
return payDetail
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,429 @@
|
|||||||
|
package com.mob.utsmyanmar.viewmodel
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Message
|
||||||
|
import android.text.TextUtils
|
||||||
|
import com.utsmyanmar.baselib.emv.EmvParamOperation
|
||||||
|
import com.utsmyanmar.baselib.repo.Repository
|
||||||
|
import com.utsmyanmar.baselib.util.enums.EmvResultStatus
|
||||||
|
import com.utsmyanmar.baselib.viewModel.EmvBaseViewModel
|
||||||
|
import com.utsmyanmar.checkxread.util.CardTypeX
|
||||||
|
import com.utsmyanmar.paylibs.model.PayDetail
|
||||||
|
import com.utsmyanmar.paylibs.model.TradeData
|
||||||
|
import com.utsmyanmar.paylibs.model.enums.TransCVM
|
||||||
|
import com.utsmyanmar.paylibs.network.ISOSocket
|
||||||
|
import com.utsmyanmar.paylibs.reversal.ReversalAction
|
||||||
|
import com.utsmyanmar.paylibs.reversal.ReversalListener
|
||||||
|
import com.utsmyanmar.paylibs.system.SingleLiveEvent
|
||||||
|
import com.utsmyanmar.paylibs.transactions.TransactionsOperation
|
||||||
|
import com.utsmyanmar.paylibs.transactions.TransactionsOperationListener
|
||||||
|
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
|
||||||
|
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType
|
||||||
|
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import sunmi.sunmiui.utils.LogUtil
|
||||||
|
import javax.inject.Inject
|
||||||
|
import com.mob.utsmyanmar.model.TransResultStatus
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class EmvTransactionProcessViewModel @Inject constructor(
|
||||||
|
private val repository: Repository,
|
||||||
|
emvParamOperation: EmvParamOperation
|
||||||
|
) : EmvBaseViewModel(repository, emvParamOperation), ProcessingTransaction {
|
||||||
|
|
||||||
|
private val transResult = SingleLiveEvent<TransResultStatus>()
|
||||||
|
|
||||||
|
override fun handleMsg(msg: Message) {
|
||||||
|
when (msg.what) {
|
||||||
|
|
||||||
|
PIN_CLICK_NUMBER -> {
|
||||||
|
showPasswordView(msg.arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
PIN_CLICK_CONFIRM -> {
|
||||||
|
if (!mPayDetail.PINCipher.isNullOrEmpty()) {
|
||||||
|
importPinInputStatus(0)
|
||||||
|
} else if (isOfflinePinEntered) {
|
||||||
|
importPinInputStatus(0)
|
||||||
|
} else {
|
||||||
|
importPinInputStatus(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_COMPLETE ||
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_VOID ||
|
||||||
|
transType.value == TransactionsType.REFUND
|
||||||
|
) {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.ON_NEXT_SCREEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PIN_CLICK_CANCEL -> {
|
||||||
|
importPinInputStatus(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
PIN_ERROR -> {
|
||||||
|
increasedKSN()
|
||||||
|
importPinInputStatus(3)
|
||||||
|
handleTransactionFail(msg.arg1, PIN_PAD_FAILED)
|
||||||
|
}
|
||||||
|
|
||||||
|
PIN_CLICK_EMPTY -> {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.PIN_EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_APP_SELECT -> {
|
||||||
|
val candiNames = msg.obj as Array<String>
|
||||||
|
LogUtil.d(TAG, "CandiNames size:${candiNames.size}")
|
||||||
|
candiList.value = candiNames
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.SELECT_APP)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_FINAL_APP_SELECT -> {
|
||||||
|
importFinalAppSelectStatus(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_CONFIRM_CARD_NO -> {
|
||||||
|
importCardNoStatus(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_CERT_VERIFY -> {
|
||||||
|
importCertStatus(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_SIGNATURE -> {
|
||||||
|
mPayDetail.setIsFreeSign(false)
|
||||||
|
mPayDetail.transCVM = TransCVM.SIGNATURE
|
||||||
|
importSignatureStatus(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_SHOW_PIN_PAD -> {
|
||||||
|
startPinProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_ONLINE_PROCESS -> {
|
||||||
|
handleTransactionProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_ERROR -> {
|
||||||
|
handleTransactionFail(msg.arg1, msg.obj.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_TRY_AGAIN -> {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.TRY_AGAIN)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_CONFIRM_CODE_VERIFY -> {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.CONFIRM_CODE_VERIFY)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_SUCCESS_ONLINE -> {
|
||||||
|
transResult.postValue(TransResultStatus.SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_FAILURE_ONLINE -> {
|
||||||
|
transResult.postValue(TransResultStatus.FAIL)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_SUCCESS_OFFLINE -> {
|
||||||
|
transResult.postValue(TransResultStatus.OFFLINE_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
EMV_FAILURE_OFFLINE -> {
|
||||||
|
transResult.postValue(TransResultStatus.OFFLINE_FAILURE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCancelEmvTransaction() {
|
||||||
|
LogUtil.e(TAG, "Terminate Transaction : $mProcessStep")
|
||||||
|
|
||||||
|
when (mProcessStep) {
|
||||||
|
EMV_APP_SELECT -> importAppSelect(-1)
|
||||||
|
EMV_FINAL_APP_SELECT -> importFinalAppSelectStatus(-1)
|
||||||
|
EMV_CONFIRM_CARD_NO -> importCardNoStatus(1)
|
||||||
|
EMV_CERT_VERIFY -> importCertStatus(1)
|
||||||
|
PIN_ERROR -> importPinInputStatus(3)
|
||||||
|
EMV_ONLINE_PROCESS -> importOnlineProcessStatus(1)
|
||||||
|
EMV_SIGNATURE -> importSignatureStatus(1)
|
||||||
|
EMV_SHOW_PIN_PAD -> importPinInputStatus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPayDetail(payDetail: PayDetail) {
|
||||||
|
mPayDetail = payDetail
|
||||||
|
mTradeData = TradeData().apply {
|
||||||
|
setPayDetail(payDetail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPayDetail(): PayDetail {
|
||||||
|
return mPayDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetProcessStepCount() {
|
||||||
|
mProcessStep = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetTransactionStatus() {
|
||||||
|
emvResultStatus.clear()
|
||||||
|
transResult.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTransStatus(): SingleLiveEvent<TransResultStatus> {
|
||||||
|
return transResult
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCardLessTrans(): Boolean {
|
||||||
|
return transType.value == TransactionsType.VOID ||
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_COMPLETE_VOID ||
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_VOID ||
|
||||||
|
transType.value == TransactionsType.REFUND
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPinPadCancel() {
|
||||||
|
importPinInputStatus(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startOnlineProcess() {
|
||||||
|
isReversal = false
|
||||||
|
|
||||||
|
TransactionsOperation.getInstance()
|
||||||
|
.getStartOperation(mTradeData, transType.value)
|
||||||
|
.checkOperation(object : TransactionsOperationListener {
|
||||||
|
|
||||||
|
override fun onSuccess(tradeData: TradeData) {
|
||||||
|
val payDetailRes = tradeData.payDetail
|
||||||
|
|
||||||
|
payDetailRes.invoiceNo =
|
||||||
|
SystemParamsOperation.getInstance().incrementInvoiceNum
|
||||||
|
|
||||||
|
LogUtil.d(TAG, "Transaction Operation Success: ${payDetailRes.tradeAnswerCode}")
|
||||||
|
LogUtil.d(TAG, "Transaction Type: ${payDetailRes.transactionType}")
|
||||||
|
|
||||||
|
if (
|
||||||
|
TextUtils.equals(payDetailRes.tradeAnswerCode, RC_APPROVED_V1) ||
|
||||||
|
TextUtils.equals(payDetailRes.tradeAnswerCode, RC_APPROVED_V2)
|
||||||
|
) {
|
||||||
|
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
|
||||||
|
|
||||||
|
if (isCardLessTrans()) {
|
||||||
|
transResult.value = TransResultStatus.SUCCESS
|
||||||
|
} else if (payDetailRes.cardType == CardTypeX.MANUAL.value) {
|
||||||
|
transResult.value = TransResultStatus.SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
payDetailResult.value = payDetailRes
|
||||||
|
|
||||||
|
if (isCardLessTrans()) {
|
||||||
|
transResult.value = TransResultStatus.FAIL
|
||||||
|
} else if (payDetailRes.cardType == CardTypeX.MANUAL.value) {
|
||||||
|
transResult.value = TransResultStatus.FAIL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReversal(tradeData: TradeData) {
|
||||||
|
LogUtil.d(TAG, "<<<< On Reversal >>>>")
|
||||||
|
|
||||||
|
isReversal = true
|
||||||
|
transResult.postValue(TransResultStatus.REVERSAL_PREPARE)
|
||||||
|
|
||||||
|
var reversalDelay = 15000
|
||||||
|
|
||||||
|
val delayValue = SystemParamsOperation.getInstance().reversalDelay
|
||||||
|
if (!delayValue.isNullOrEmpty()) {
|
||||||
|
reversalDelay = "${delayValue}000".toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
val payDetailReversal = tradeData.payDetail
|
||||||
|
|
||||||
|
if (
|
||||||
|
payDetailReversal.transactionType == TransactionType.SALE ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.VOID ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.REFUND ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.PRE_SALE ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.CASH_ADVANCE ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.PRE_SALE_CANCEL ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.PRE_SALE_COMPLETE ||
|
||||||
|
payDetailReversal.transactionType == TransactionType.PRE_SALE_COMPLETE_VOID
|
||||||
|
) {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.REVERSAL_PROCESS)
|
||||||
|
transResult.postValue(TransResultStatus.REVERSAL_PROCESS)
|
||||||
|
callReversal(tradeData)
|
||||||
|
} else {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.REVERSAL_FAIL)
|
||||||
|
transResult.postValue(TransResultStatus.REVERSAL_FAIL)
|
||||||
|
}
|
||||||
|
}, reversalDelay.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(message: String) {
|
||||||
|
LogUtil.e(TAG, "<<<< On Error >>>>")
|
||||||
|
LogUtil.e(TAG, "error message:$message")
|
||||||
|
|
||||||
|
if (TextUtils.equals(message, TRY_SECONDARY)) {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.SECONDARY)
|
||||||
|
transResult.postValue(TransResultStatus.SECONDARY)
|
||||||
|
startOnlineProcess()
|
||||||
|
} else {
|
||||||
|
importOnlineProcessStatus(1)
|
||||||
|
setNetWorkErrorDesc(message)
|
||||||
|
errorCodeMsg.value = message
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.NETWORK_ERROR)
|
||||||
|
transResult.postValue(TransResultStatus.NETWORK_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun insertDB(payResult: PayDetail) {
|
||||||
|
payResult.PINCipher = ""
|
||||||
|
repository.insertPayDetail(payResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processVoidDB(payResult: PayDetail) {
|
||||||
|
mPayDetail.setIsCanceled(true)
|
||||||
|
updatePayDetail(mPayDetail)
|
||||||
|
repository.insertPayDetail(updateCurrentDateAndTime(payResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processPreVoidDb(payResult: PayDetail) {
|
||||||
|
oldTransPayDetail.setIsCanceled(true)
|
||||||
|
updatePayDetail(oldTransPayDetail)
|
||||||
|
repository.insertPayDetail(updateCurrentDateAndTime(payResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processPreCompDb(payResult: PayDetail) {
|
||||||
|
if (oldTransPayDetail.amount == payResult.amount) {
|
||||||
|
oldTransPayDetail.setIsCanceled(true)
|
||||||
|
updatePayDetail(oldTransPayDetail)
|
||||||
|
} else {
|
||||||
|
oldTransPayDetail.setIsCanceled(false)
|
||||||
|
updatePayDetail(oldTransPayDetail)
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.insertPayDetail(updateCurrentDateAndTime(payResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processPreCompVoidDb(payResult: PayDetail) {
|
||||||
|
mPayDetail.setIsCanceled(true)
|
||||||
|
updatePayDetail(mPayDetail)
|
||||||
|
repository.insertPayDetail(updateCurrentDateAndTime(payResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processRefundDB(payResult: PayDetail) {
|
||||||
|
mPayDetail.setReturnGood(true)
|
||||||
|
updatePayDetail(mPayDetail)
|
||||||
|
repository.insertPayDetail(updateCurrentDateAndTime(payResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleTransactionFail(code: Int, description: String) {
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
LogUtil.d(TAG, "On handleTransactionFail ::")
|
||||||
|
|
||||||
|
if (isReversal) {
|
||||||
|
transResult.value = TransResultStatus.REVERSAL_PREPARE
|
||||||
|
} else {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.ERROR)
|
||||||
|
transResult.value = TransResultStatus.FAIL
|
||||||
|
errorCodeMsg.postValue("$code:$description")
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleTransactionProcess() {
|
||||||
|
val cardNo = mPayDetail.cardNo
|
||||||
|
|
||||||
|
if (cardNo.isNullOrEmpty()) {
|
||||||
|
getCardInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_COMPLETE ||
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_VOID ||
|
||||||
|
transType.value == TransactionsType.REFUND
|
||||||
|
) {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.ON_NEXT_SCREEN)
|
||||||
|
} else {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun callReversal(tradeData: TradeData) {
|
||||||
|
if (transType.value == TransactionsType.VOID) {
|
||||||
|
tradeData.payDetail.icC55 = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ReversalAction.getInstance()
|
||||||
|
.setData(tradeData)
|
||||||
|
.enqueue()
|
||||||
|
.startReversal(object : ReversalListener {
|
||||||
|
|
||||||
|
override fun onSuccessReversal() {
|
||||||
|
importOnlineProcessStatus(0)
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.REVERSAL_SUCCESS)
|
||||||
|
transResult.postValue(TransResultStatus.REVERSAL_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNetworkFail(msg: String) {
|
||||||
|
importOnlineProcessStatus(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailReversal(msg: String) {
|
||||||
|
importOnlineProcessStatus(0)
|
||||||
|
|
||||||
|
if (!isSecondCall) {
|
||||||
|
if (!TextUtils.equals(msg, REVERSAL)) {
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.REVERSAL_SECONDARY)
|
||||||
|
transResult.postValue(TransResultStatus.REVERSAL_SECONDARY)
|
||||||
|
|
||||||
|
ISOSocket.getInstance().switchIp()
|
||||||
|
isSecondCall = true
|
||||||
|
|
||||||
|
callReversal(tradeData)
|
||||||
|
} else {
|
||||||
|
saveNeedReversal()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
saveNeedReversal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveNeedReversal() {
|
||||||
|
mPayDetail.setNeedReversal(true)
|
||||||
|
mPayDetail.transactionType = TransactionType.REVERSAL
|
||||||
|
mPayDetail.transType = REVERSAL
|
||||||
|
mPayDetail.pid = null
|
||||||
|
|
||||||
|
repository.insertPayDetail(mPayDetail)
|
||||||
|
|
||||||
|
emvResultStatus.postValue(EmvResultStatus.REVERSAL_FAIL)
|
||||||
|
transResult.postValue(TransResultStatus.REVERSAL_FAIL)
|
||||||
|
|
||||||
|
isSecondCall = false
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = EmvTransactionProcessViewModel::class.java.simpleName
|
||||||
|
private const val TRY_SECONDARY = "TRY_SECONDARY"
|
||||||
|
private const val PIN_PAD_FAILED = "Pin pad failed"
|
||||||
|
private const val RC_APPROVED_V1 = "00"
|
||||||
|
private const val RC_APPROVED_V2 = "000"
|
||||||
|
private const val REVERSAL = "REVERSAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,585 @@
|
|||||||
|
package com.mob.utsmyanmar.viewmodel
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.RemoteException
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewTreeObserver
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.AidlErrorCodeV2
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.bean.PinPadConfigV2
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.bean.PinPadDataV2
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadListenerV2
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2
|
||||||
|
import com.mob.utsmyanmar.config.Constants
|
||||||
|
import com.mob.utsmyanmar.config.SunmiPayManager
|
||||||
|
import com.mob.utsmyanmar.model.PinPadStatus
|
||||||
|
import com.sunmi.pay.hardware.aidl.AidlConstants
|
||||||
|
import com.utsmyanmar.baselib.ui.CustomPinPadKeyboard
|
||||||
|
import com.utsmyanmar.checkxread.sdk.SunmiSDK
|
||||||
|
import com.utsmyanmar.paylibs.model.PayDetail
|
||||||
|
import com.utsmyanmar.paylibs.model.TradeData
|
||||||
|
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil
|
||||||
|
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 sunmi.sunmiui.utils.LogUtil
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class PinPadViewModel @Inject constructor(
|
||||||
|
private val sunmiPayManager: SunmiPayManager
|
||||||
|
) : ViewModel() {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "PinPadViewModel"
|
||||||
|
private const val ON_CONFIRM_CLICK = 2
|
||||||
|
private const val ON_CANCEL_CLICK = 3
|
||||||
|
private const val ON_ERROR_PIN_PAD = 4
|
||||||
|
private const val ON_NUMBER_CLICK = 5
|
||||||
|
private const val ON_EMPTY_PIN_BLOCK = 6
|
||||||
|
private const val ON_TIMEOUT_PIN_PAD = 7
|
||||||
|
private const val ON_ERROR_DUKPT = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mPinPadOptV2: PinPadOptV2? = null
|
||||||
|
private var mPinType = 0
|
||||||
|
private var mWidth = 239
|
||||||
|
private var mHeight = 130
|
||||||
|
private var mInterval = 1
|
||||||
|
private var mKeyboardCoordinate = intArrayOf(0, 661)
|
||||||
|
private var mCancelWidth = 112
|
||||||
|
private var mCancelHeight = 112
|
||||||
|
private var mCancelCoordinate = intArrayOf(0, 48)
|
||||||
|
private var dukptIndex = 0
|
||||||
|
private var tradeData: TradeData? = null
|
||||||
|
private var payDetail: PayDetail? = null
|
||||||
|
private var pan: String = ""
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UI States
|
||||||
|
*/
|
||||||
|
|
||||||
|
private val _pinText = MutableStateFlow("")
|
||||||
|
val pinText = _pinText.asStateFlow()
|
||||||
|
|
||||||
|
private val _alertMsg = MutableStateFlow<String?>(null)
|
||||||
|
val alertMsg = _alertMsg.asStateFlow()
|
||||||
|
|
||||||
|
private val _errorCode = MutableStateFlow<Int?>(null)
|
||||||
|
val errorCode = _errorCode.asStateFlow()
|
||||||
|
|
||||||
|
private val _transType = MutableStateFlow<TransactionsType?>(null)
|
||||||
|
val transType = _transType.asStateFlow()
|
||||||
|
|
||||||
|
private val _pinStatus =
|
||||||
|
MutableStateFlow<PinPadStatus?>(null)
|
||||||
|
|
||||||
|
val pinStatus = _pinStatus.asStateFlow()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trade Data
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun setTradeData(tradeData: TradeData) {
|
||||||
|
this.tradeData = tradeData
|
||||||
|
|
||||||
|
payDetail = tradeData.payDetail
|
||||||
|
|
||||||
|
pan = payDetail?.cardNo ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTradeData(): TradeData? {
|
||||||
|
return tradeData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPayDetail(payDetail: PayDetail) {
|
||||||
|
this.payDetail = payDetail
|
||||||
|
|
||||||
|
tradeData = TradeData().apply {
|
||||||
|
this.payDetail = payDetail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPayDetail(): PayDetail? {
|
||||||
|
return payDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTransType(type: TransactionsType?) {
|
||||||
|
_transType.value = type
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pin Pad
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun startPinPadProcess(
|
||||||
|
customPinPadKeyboard: CustomPinPadKeyboard
|
||||||
|
) {
|
||||||
|
|
||||||
|
dukptIndex =
|
||||||
|
SystemParamsOperation.getInstance()
|
||||||
|
.tmkIndex
|
||||||
|
.toInt()
|
||||||
|
|
||||||
|
initData()
|
||||||
|
|
||||||
|
if(mPinPadOptV2 == null){
|
||||||
|
_alertMsg.value = "PinPad service is not ready!"
|
||||||
|
}
|
||||||
|
|
||||||
|
getKSN()
|
||||||
|
initPinPad(customPinPadKeyboard)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initData() {
|
||||||
|
|
||||||
|
mPinPadOptV2 = sunmiPayManager.pinPadOptV2
|
||||||
|
|
||||||
|
if (mPinPadOptV2 == null) {
|
||||||
|
Log.d(TAG, "PinPad service are not ready!")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
val result = mPinPadOptV2?.setAntiExhaustiveProtectionMode(3)
|
||||||
|
|
||||||
|
if ((result ?: -1) >= 0) {
|
||||||
|
LogUtil.d(
|
||||||
|
TAG,
|
||||||
|
"Pin anti exhaustive result:$result"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LogUtil.d(
|
||||||
|
TAG, "Pin Anti Exhaustive failed"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelPinPad() {
|
||||||
|
try {
|
||||||
|
mPinPadOptV2?.cancelInputPin()
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KSN
|
||||||
|
*/
|
||||||
|
|
||||||
|
private fun increasedKSN() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
val securityOptV2 = sunmiPayManager.securityOptV2;
|
||||||
|
if(securityOptV2 == null){
|
||||||
|
Log.d(TAG, "Security service is not ready!")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val result = securityOptV2.dukptIncreaseKSN(dukptIndex);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
LogUtil.d(
|
||||||
|
TAG,
|
||||||
|
"--------KSN increased Success-----"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LogUtil.d(
|
||||||
|
TAG,
|
||||||
|
"--------KSN result:$result-----"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getKSN() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
val securityOptV2 = sunmiPayManager.securityOptV2;
|
||||||
|
if(securityOptV2 == null){
|
||||||
|
Log.d(TAG, "Security service is not ready!")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataOut = ByteArray(10)
|
||||||
|
|
||||||
|
val result = securityOptV2.dukptCurrentKSN(dukptIndex, dataOut)
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
|
||||||
|
val ksnStr =
|
||||||
|
ByteUtil.bytes2HexStr(dataOut)
|
||||||
|
|
||||||
|
LogUtil.d(TAG, "ksnStr: $ksnStr")
|
||||||
|
|
||||||
|
payDetail?.reference = ksnStr
|
||||||
|
payDetail?.tempKSN = ksnStr
|
||||||
|
|
||||||
|
LogUtil.d(
|
||||||
|
TAG,
|
||||||
|
"final ksnStr: $ksnStr"
|
||||||
|
)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
LogUtil.e(
|
||||||
|
TAG,
|
||||||
|
"Result code for current KSN:$result"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init Pin Pad
|
||||||
|
*/
|
||||||
|
|
||||||
|
private fun initPinPad(
|
||||||
|
customPinPadKeyboard: CustomPinPadKeyboard
|
||||||
|
) {
|
||||||
|
|
||||||
|
LogUtil.e(TAG, "Init Pin Pad PAN:$pan")
|
||||||
|
val pikIndex = dukptIndex
|
||||||
|
var timeout = Constants.PIN_PAD_TIMEOUT
|
||||||
|
var pinPadOrder = true
|
||||||
|
pinPadOrder = !SystemParamsOperation.getInstance().isRandomPinPad
|
||||||
|
if (SunmiSDK.getInstance().checkCardExist() != 2) {
|
||||||
|
timeout = Constants.TIMEOUT
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
|
val config = PinPadConfigV2()
|
||||||
|
config.maxInput = 6
|
||||||
|
config.minInput = 0
|
||||||
|
config.pinPadType = 1
|
||||||
|
config.algorithmType = 0
|
||||||
|
config.pinType = mPinType
|
||||||
|
config.timeout = timeout * 1000
|
||||||
|
config.isOrderNumKey = pinPadOrder
|
||||||
|
config.keySystem = 1
|
||||||
|
config.pinKeyIndex = pikIndex
|
||||||
|
config.pinblockFormat = 0
|
||||||
|
|
||||||
|
val length = pan.length
|
||||||
|
|
||||||
|
val panBytes = pan.substring(length - 13, length - 1).toByteArray(StandardCharsets.US_ASCII)
|
||||||
|
|
||||||
|
config.pan = panBytes
|
||||||
|
|
||||||
|
val result = mPinPadOptV2?.initPinPad(config, mPinPadListener)
|
||||||
|
|
||||||
|
LogUtil.e(TAG, "result:$result")
|
||||||
|
|
||||||
|
result?.let {
|
||||||
|
getKeyboardCoordinate(
|
||||||
|
it,
|
||||||
|
customPinPadKeyboard
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_pinText.value = ""
|
||||||
|
|
||||||
|
customPinPadKeyboard.keepScreenOn = true
|
||||||
|
|
||||||
|
customPinPadKeyboard.setKeyBoard(result)
|
||||||
|
|
||||||
|
customPinPadKeyboard.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keyboard
|
||||||
|
*/
|
||||||
|
|
||||||
|
private fun getKeyboardCoordinate(
|
||||||
|
keyBoardText: String,
|
||||||
|
customPinPadKeyboard: CustomPinPadKeyboard
|
||||||
|
) {
|
||||||
|
|
||||||
|
customPinPadKeyboard
|
||||||
|
.viewTreeObserver
|
||||||
|
.addOnGlobalLayoutListener(
|
||||||
|
object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
|
|
||||||
|
override fun onGlobalLayout() {
|
||||||
|
|
||||||
|
customPinPadKeyboard
|
||||||
|
.viewTreeObserver
|
||||||
|
.removeOnGlobalLayoutListener(this)
|
||||||
|
|
||||||
|
val textView: TextView =
|
||||||
|
customPinPadKeyboard.key_0
|
||||||
|
|
||||||
|
textView.getLocationOnScreen(
|
||||||
|
mKeyboardCoordinate
|
||||||
|
)
|
||||||
|
|
||||||
|
mWidth = textView.width
|
||||||
|
mHeight = textView.height
|
||||||
|
|
||||||
|
mInterval = 1
|
||||||
|
|
||||||
|
importPinPadData(keyBoardText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importPinPadData(text: String) {
|
||||||
|
|
||||||
|
val pinPadData = PinPadDataV2()
|
||||||
|
|
||||||
|
pinPadData.numX = mKeyboardCoordinate[0]
|
||||||
|
pinPadData.numY = mKeyboardCoordinate[1]
|
||||||
|
|
||||||
|
pinPadData.numW = mWidth
|
||||||
|
pinPadData.numH = mHeight
|
||||||
|
|
||||||
|
pinPadData.lineW = mInterval
|
||||||
|
|
||||||
|
pinPadData.cancelX = mCancelCoordinate[0]
|
||||||
|
pinPadData.cancelY = mCancelCoordinate[1]
|
||||||
|
|
||||||
|
pinPadData.cancelW = mCancelWidth
|
||||||
|
pinPadData.cancelH = mCancelHeight
|
||||||
|
|
||||||
|
pinPadData.lineW = 0
|
||||||
|
|
||||||
|
pinPadData.rows = 5
|
||||||
|
pinPadData.clos = 3
|
||||||
|
|
||||||
|
keyMap(text, pinPadData)
|
||||||
|
|
||||||
|
try {
|
||||||
|
mPinPadOptV2?.importPinPadData(pinPadData)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun keyMap(
|
||||||
|
str: String,
|
||||||
|
data: PinPadDataV2
|
||||||
|
) {
|
||||||
|
|
||||||
|
data.keyMap = ByteArray(64)
|
||||||
|
|
||||||
|
var j = 0
|
||||||
|
|
||||||
|
for (i in 0 until 15) {
|
||||||
|
|
||||||
|
when (i) {
|
||||||
|
|
||||||
|
9, 12 -> {
|
||||||
|
data.keyMap[i] = 0x1B
|
||||||
|
}
|
||||||
|
|
||||||
|
13 -> {
|
||||||
|
data.keyMap[i] = 0x0C
|
||||||
|
}
|
||||||
|
|
||||||
|
11, 14 -> {
|
||||||
|
data.keyMap[i] = 0x0D
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
data.keyMap[i] =
|
||||||
|
str[j].code.toByte()
|
||||||
|
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Password View
|
||||||
|
*/
|
||||||
|
|
||||||
|
private fun showPasswordView(len: Int) {
|
||||||
|
|
||||||
|
val sb = StringBuilder()
|
||||||
|
|
||||||
|
repeat(len) {
|
||||||
|
sb.append("*")
|
||||||
|
}
|
||||||
|
|
||||||
|
_pinText.value = sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
private val handler =
|
||||||
|
Handler(Looper.getMainLooper()) { msg ->
|
||||||
|
when (msg.what) {
|
||||||
|
ON_NUMBER_CLICK -> {
|
||||||
|
showPasswordView(msg.arg1)
|
||||||
|
}
|
||||||
|
ON_CONFIRM_CLICK -> {
|
||||||
|
LogUtil.d(TAG, "ON CLICK CONFIRM")
|
||||||
|
_pinStatus.value = PinPadStatus.ON_CONFIRM
|
||||||
|
}
|
||||||
|
ON_CANCEL_CLICK -> {
|
||||||
|
LogUtil.d(TAG, "ON CLICK CANCEL")
|
||||||
|
_pinStatus.value = PinPadStatus.ON_CANCEL
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_ERROR_PIN_PAD -> {
|
||||||
|
LogUtil.d(TAG, "ON ERROR CODE: ${msg.arg1}")
|
||||||
|
_errorCode.value = msg.arg1
|
||||||
|
_pinStatus.value = PinPadStatus.ON_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_EMPTY_PIN_BLOCK -> {
|
||||||
|
_pinStatus.value = PinPadStatus.ON_EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_TIMEOUT_PIN_PAD -> {
|
||||||
|
_pinStatus.value = PinPadStatus.ON_TIMEOUT
|
||||||
|
}
|
||||||
|
|
||||||
|
ON_ERROR_DUKPT -> {
|
||||||
|
increasedKSN()
|
||||||
|
_alertMsg.value = "Try Again!"
|
||||||
|
_pinStatus.value = PinPadStatus.ON_ERROR_DUKPT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Listener
|
||||||
|
*/
|
||||||
|
|
||||||
|
private val mPinPadListener =
|
||||||
|
object : PinPadListenerV2.Stub() {
|
||||||
|
|
||||||
|
override fun onPinLength(len: Int) {
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_NUMBER_CLICK,
|
||||||
|
len,
|
||||||
|
0
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfirm(
|
||||||
|
status: Int,
|
||||||
|
pinBlock: ByteArray?
|
||||||
|
) {
|
||||||
|
|
||||||
|
LogUtil.e(
|
||||||
|
TAG,
|
||||||
|
"onConfirm status:$status"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (pinBlock != null) {
|
||||||
|
|
||||||
|
increasedKSN()
|
||||||
|
|
||||||
|
val hexStr =
|
||||||
|
ByteUtil.bytes2HexStr(pinBlock)
|
||||||
|
|
||||||
|
if (
|
||||||
|
SunmiSDK.getInstance().checkCardExist() == 2 ||
|
||||||
|
payDetail?.cardType == AidlConstants.CardType.MAGNETIC.value ||
|
||||||
|
payDetail?.cardType == -9
|
||||||
|
) {
|
||||||
|
|
||||||
|
payDetail?.pinCipher = hexStr
|
||||||
|
|
||||||
|
if (
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_COMPLETE ||
|
||||||
|
transType.value == TransactionsType.PRE_AUTH_VOID ||
|
||||||
|
transType.value == TransactionsType.REFUND
|
||||||
|
) {
|
||||||
|
|
||||||
|
_pinStatus.value =
|
||||||
|
PinPadStatus.ON_NEXT_SCREEN
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_CONFIRM_CLICK
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
_pinStatus.value =
|
||||||
|
PinPadStatus.ON_CARD_REMOVED
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_EMPTY_PIN_BLOCK
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancel() {
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_CANCEL_CLICK
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(code: Int) {
|
||||||
|
|
||||||
|
val msg =
|
||||||
|
AidlErrorCodeV2
|
||||||
|
.valueOf(code)
|
||||||
|
.msg
|
||||||
|
|
||||||
|
LogUtil.d(
|
||||||
|
TAG,
|
||||||
|
"error code:$code - message :$msg"
|
||||||
|
)
|
||||||
|
|
||||||
|
when (code) {
|
||||||
|
|
||||||
|
-60001 -> {
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_TIMEOUT_PIN_PAD
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
|
||||||
|
-3025 -> {
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_ERROR_DUKPT
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
handler.obtainMessage(
|
||||||
|
ON_ERROR_PIN_PAD,
|
||||||
|
code,
|
||||||
|
code,
|
||||||
|
code
|
||||||
|
).sendToTarget()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -52,11 +52,13 @@ class TransProcessViewModel @Inject constructor(
|
|||||||
val transResultStatus =
|
val transResultStatus =
|
||||||
_transResultStatus.asStateFlow()
|
_transResultStatus.asStateFlow()
|
||||||
|
|
||||||
private val _transType =
|
private val _transType = MutableStateFlow<TransactionsType?>(null)
|
||||||
MutableStateFlow<TransactionsType?>(null)
|
|
||||||
|
|
||||||
val transType =
|
val transType =_transType.asStateFlow()
|
||||||
_transType.asStateFlow()
|
|
||||||
|
fun setTransType(type: TransactionsType?) {
|
||||||
|
_transType.value = type
|
||||||
|
}
|
||||||
|
|
||||||
private val _printStatus =
|
private val _printStatus =
|
||||||
MutableStateFlow(PrintStatus.FIRST_PRINT)
|
MutableStateFlow(PrintStatus.FIRST_PRINT)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user