Skip to content

Commit fc705bc

Browse files
committed
feat(feature:send-money): add payment processing screen
1 parent 0f65b8e commit fc705bc

File tree

10 files changed

+326
-24
lines changed

10 files changed

+326
-24
lines changed

cmp-android/prodRelease-badging.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package: name='org.mifospay' versionCode='1' versionName='2025.8.4-beta.0.9' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
1+
package: name='org.mifospay' versionCode='1' versionName='2025.8.4-beta.0.10' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
22
minSdkVersion:'26'
33
targetSdkVersion:'34'
44
uses-permission: name='android.permission.INTERNET'

cmp-shared/src/commonMain/kotlin/org/mifospay/shared/navigation/MifosNavHost.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ import org.mifospay.feature.send.money.SendMoneyScreen
7474
import org.mifospay.feature.send.money.navigation.SEND_MONEY_BASE_ROUTE
7575
import org.mifospay.feature.send.money.navigation.SEND_MONEY_OPTIONS_ROUTE
7676
import org.mifospay.feature.send.money.navigation.navigateToPayeeDetailsScreen
77+
import org.mifospay.feature.send.money.navigation.navigateToPaymentProcessingScreen
7778
import org.mifospay.feature.send.money.navigation.navigateToSendMoneyOptionsScreen
7879
import org.mifospay.feature.send.money.navigation.navigateToSendMoneyScreen
7980
import org.mifospay.feature.send.money.navigation.payeeDetailsScreen
81+
import org.mifospay.feature.send.money.navigation.paymentProcessingScreen
8082
import org.mifospay.feature.send.money.navigation.sendMoneyOptionsScreen
8183
import org.mifospay.feature.send.money.navigation.sendMoneyScreen
8284
import org.mifospay.feature.settings.navigation.settingsScreen
@@ -323,11 +325,26 @@ internal fun MifosNavHost(
323325

324326
payeeDetailsScreen(
325327
onBackClick = navController::popBackStack,
326-
onNavigateToUpiPayment = { state ->
327-
// TODO: Handle UPI payment navigation
328+
onNavigateToPaymentProcessing = { state ->
329+
navController.navigateToPaymentProcessingScreen(
330+
payeeName = state.payeeName,
331+
amount = state.amount,
332+
isUpiCode = state.isUpiCode,
333+
)
334+
},
335+
)
336+
337+
paymentProcessingScreen(
338+
onPaymentComplete = {
339+
navController.navigate(HOME_ROUTE) {
340+
popUpTo(HOME_ROUTE) {
341+
inclusive = false
342+
}
343+
launchSingleTop = true
344+
}
328345
},
329-
onNavigateToFineractPayment = { state ->
330-
// TODO: Handle Fineract payment navigation
346+
onPaymentFailed = { errorMessage ->
347+
navController.popBackStack()
331348
},
332349
)
333350

core/designsystem/src/commonMain/kotlin/org/mifospay/core/designsystem/icon/MifosIcons.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ import androidx.compose.material.icons.outlined.Wallet
5959
import androidx.compose.material.icons.rounded.AccountBalance
6060
import androidx.compose.material.icons.rounded.AccountCircle
6161
import androidx.compose.material.icons.rounded.Add
62+
import androidx.compose.material.icons.rounded.Check
63+
import androidx.compose.material.icons.rounded.CheckCircle
6264
import androidx.compose.material.icons.rounded.Contacts
6365
import androidx.compose.material.icons.rounded.Home
6466
import androidx.compose.material.icons.rounded.Info
@@ -135,4 +137,6 @@ object MifosIcons {
135137
val ArrowForward = Icons.AutoMirrored.Filled.ArrowForward
136138

137139
val CurrencyRupee = Icons.Filled.CurrencyRupee
140+
val CheckCircle = Icons.Rounded.CheckCircle
141+
val CheckRounded = Icons.Rounded.Check
138142
}

feature/send-money/src/commonMain/composeResources/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,6 @@
6666
<string name="feature_send_money_add_bank_account">Add bank account</string>
6767
<string name="feature_send_money_add_bank_account_desc">Add Bank Account</string>
6868
<string name="feature_send_money_continue">Continue</string>
69+
<string name="feature_send_money_paying_securely">Paying securely %1$s to</string>
70+
<string name="feature_send_money_paid_to">Paid to</string>
6971
</resources>

feature/send-money/src/commonMain/kotlin/org/mifospay/feature/send/money/PayeeDetailsScreen.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ import template.core.base.designsystem.theme.KptTheme
9797
@Composable
9898
fun PayeeDetailsScreen(
9999
onBackClick: () -> Unit,
100-
onNavigateToUpiPayment: (PayeeDetailsState) -> Unit,
101-
onNavigateToFineractPayment: (PayeeDetailsState) -> Unit,
100+
onNavigateToPaymentProcessing: (PayeeDetailsState) -> Unit,
102101
modifier: Modifier = Modifier,
103102
viewModel: PayeeDetailsViewModel = koinViewModel(),
104103
) {
@@ -107,8 +106,7 @@ fun PayeeDetailsScreen(
107106
EventsEffect(viewModel) { event ->
108107
when (event) {
109108
PayeeDetailsEvent.NavigateBack -> onBackClick.invoke()
110-
is PayeeDetailsEvent.NavigateToUpiPayment -> onNavigateToUpiPayment.invoke(event.state)
111-
is PayeeDetailsEvent.NavigateToFineractPayment -> onNavigateToFineractPayment.invoke(event.state)
109+
is PayeeDetailsEvent.NavigateToPaymentProcessing -> onNavigateToPaymentProcessing.invoke(event.state)
112110
}
113111
}
114112

@@ -626,7 +624,7 @@ private fun ProceedButton(
626624
} else {
627625
Icon(
628626
imageVector = when {
629-
showCheckMark -> MifosIcons.Check
627+
showCheckMark -> MifosIcons.CheckRounded
630628
else -> MifosIcons.ArrowForward
631629
},
632630
contentDescription = when {
@@ -814,7 +812,7 @@ private fun AccountItem(
814812

815813
if (isSelected) {
816814
Icon(
817-
imageVector = MifosIcons.Check,
815+
imageVector = MifosIcons.CheckCircle,
818816
contentDescription = stringResource(Res.string.feature_send_money_selected),
819817
modifier = Modifier.size(24.dp),
820818
tint = KptTheme.colorScheme.primary,
@@ -938,8 +936,7 @@ private fun AddBankAccountItem(
938936
fun PayeeDetailsScreenPreview() {
939937
PayeeDetailsScreen(
940938
onBackClick = {},
941-
onNavigateToUpiPayment = {},
942-
onNavigateToFineractPayment = {},
939+
onNavigateToPaymentProcessing = {},
943940
modifier = Modifier,
944941
// TODO: Figure out how to instantiate 'PayeeDetailsViewModel'
945942
// viewModel = koinViewModel(),

feature/send-money/src/commonMain/kotlin/org/mifospay/feature/send/money/PayeeDetailsViewModel.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,7 @@ class PayeeDetailsViewModel(
146146
}
147147
is PayeeDetailsAction.ConfirmPayment -> {
148148
val currentState = stateFlow.value
149-
if (currentState.isUpiCode) {
150-
sendEvent(PayeeDetailsEvent.NavigateToUpiPayment(currentState))
151-
} else {
152-
sendEvent(PayeeDetailsEvent.NavigateToFineractPayment(currentState))
153-
}
149+
sendEvent(PayeeDetailsEvent.NavigateToPaymentProcessing(currentState))
154150
}
155151
}
156152
}
@@ -220,8 +216,7 @@ data class BankAccount(
220216

221217
sealed interface PayeeDetailsEvent {
222218
data object NavigateBack : PayeeDetailsEvent
223-
data class NavigateToUpiPayment(val state: PayeeDetailsState) : PayeeDetailsEvent
224-
data class NavigateToFineractPayment(val state: PayeeDetailsState) : PayeeDetailsEvent
219+
data class NavigateToPaymentProcessing(val state: PayeeDetailsState) : PayeeDetailsEvent
225220
}
226221

227222
sealed interface PayeeDetailsAction {
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2024 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.feature.send.money
11+
12+
import androidx.compose.foundation.layout.Arrangement
13+
import androidx.compose.foundation.layout.Box
14+
import androidx.compose.foundation.layout.Column
15+
import androidx.compose.foundation.layout.fillMaxSize
16+
import androidx.compose.foundation.layout.padding
17+
import androidx.compose.foundation.layout.size
18+
import androidx.compose.material3.Icon
19+
import androidx.compose.material3.Text
20+
import androidx.compose.runtime.Composable
21+
import androidx.compose.runtime.getValue
22+
import androidx.compose.ui.Alignment
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.text.font.FontWeight
25+
import androidx.compose.ui.text.style.TextAlign
26+
import androidx.compose.ui.unit.dp
27+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
28+
import mobile_wallet.feature.send_money.generated.resources.Res
29+
import mobile_wallet.feature.send_money.generated.resources.feature_send_money_paid_to
30+
import mobile_wallet.feature.send_money.generated.resources.feature_send_money_paying_securely
31+
import org.jetbrains.compose.resources.stringResource
32+
import org.jetbrains.compose.ui.tooling.preview.Preview
33+
import org.koin.compose.viewmodel.koinViewModel
34+
import org.mifospay.core.designsystem.component.MifosGradientBackground
35+
import org.mifospay.core.designsystem.component.MifosLoadingWheel
36+
import org.mifospay.core.designsystem.component.MifosScaffold
37+
import org.mifospay.core.designsystem.icon.MifosIcons
38+
import org.mifospay.core.ui.utils.EventsEffect
39+
import template.core.base.designsystem.theme.KptTheme
40+
41+
@Composable
42+
fun PaymentProcessingScreen(
43+
onPaymentComplete: () -> Unit,
44+
onPaymentFailed: (String) -> Unit,
45+
modifier: Modifier = Modifier,
46+
viewModel: PaymentProcessingViewModel = koinViewModel(),
47+
) {
48+
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
49+
50+
EventsEffect(viewModel) { event ->
51+
when (event) {
52+
is PaymentProcessingEvent.PaymentComplete -> onPaymentComplete.invoke()
53+
is PaymentProcessingEvent.PaymentFailed -> onPaymentFailed.invoke(event.errorMessage)
54+
}
55+
}
56+
57+
MifosGradientBackground {
58+
MifosScaffold(
59+
modifier = modifier,
60+
) { paddingValues ->
61+
Box(
62+
modifier = Modifier
63+
.fillMaxSize()
64+
.padding(paddingValues),
65+
contentAlignment = Alignment.Center,
66+
) {
67+
PaymentProcessingContent(
68+
state = state,
69+
modifier = Modifier.fillMaxSize(),
70+
)
71+
}
72+
}
73+
}
74+
}
75+
76+
@Composable
77+
private fun PaymentProcessingContent(
78+
state: PaymentProcessingState,
79+
modifier: Modifier = Modifier,
80+
) {
81+
Column(
82+
modifier = modifier,
83+
horizontalAlignment = Alignment.CenterHorizontally,
84+
verticalArrangement = Arrangement.Center,
85+
) {
86+
if (state.isProcessing) {
87+
MifosLoadingWheel(
88+
contentDesc = "Processing Payment",
89+
modifier = Modifier.size(120.dp),
90+
)
91+
} else {
92+
Icon(
93+
imageVector = MifosIcons.CheckCircle,
94+
contentDescription = "Payment Complete",
95+
modifier = Modifier.size(120.dp),
96+
tint = KptTheme.colorScheme.primary,
97+
)
98+
}
99+
100+
androidx.compose.foundation.layout.Spacer(modifier = Modifier.size(KptTheme.spacing.xl))
101+
102+
Text(
103+
text = if (state.isProcessing) {
104+
stringResource(
105+
Res.string.feature_send_money_paying_securely,
106+
state.formattedAmount,
107+
)
108+
} else {
109+
stringResource(Res.string.feature_send_money_paid_to)
110+
},
111+
style = KptTheme.typography.bodyLarge,
112+
fontWeight = FontWeight.Medium,
113+
color = KptTheme.colorScheme.onSurface,
114+
textAlign = TextAlign.Center,
115+
)
116+
117+
androidx.compose.foundation.layout.Spacer(modifier = Modifier.size(KptTheme.spacing.md))
118+
119+
Text(
120+
text = state.payeeName,
121+
style = KptTheme.typography.headlineMedium,
122+
fontWeight = FontWeight.Bold,
123+
color = KptTheme.colorScheme.onSurface,
124+
textAlign = TextAlign.Center,
125+
)
126+
}
127+
}
128+
129+
@Preview
130+
@Composable
131+
fun PaymentProcessingScreenPreview() {
132+
PaymentProcessingScreen(
133+
onPaymentComplete = {},
134+
onPaymentFailed = {},
135+
modifier = Modifier,
136+
)
137+
}
138+
139+
@Preview
140+
@Composable
141+
fun PaymentProcessingContentPreview() {
142+
val state = PaymentProcessingState(
143+
payeeName = "John Doe",
144+
amount = "100.00",
145+
isProcessing = true,
146+
)
147+
PaymentProcessingContent(
148+
state = state,
149+
modifier = Modifier.fillMaxSize(),
150+
)
151+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2024 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.feature.send.money
11+
12+
import androidx.lifecycle.SavedStateHandle
13+
import androidx.lifecycle.viewModelScope
14+
import kotlinx.coroutines.delay
15+
import kotlinx.coroutines.flow.update
16+
import kotlinx.coroutines.launch
17+
import org.mifospay.core.ui.utils.BaseViewModel
18+
19+
class PaymentProcessingViewModel(
20+
savedStateHandle: SavedStateHandle,
21+
) : BaseViewModel<PaymentProcessingState, PaymentProcessingEvent, PaymentProcessingAction>(
22+
initialState = PaymentProcessingState(),
23+
) {
24+
25+
init {
26+
val payeeName = savedStateHandle.get<String>("payeeName") ?: ""
27+
val amount = savedStateHandle.get<String>("amount") ?: ""
28+
val isUpiCode = savedStateHandle.get<Boolean>("isUpiCode") ?: false
29+
30+
mutableStateFlow.update {
31+
it.copy(
32+
payeeName = payeeName,
33+
amount = amount,
34+
isUpiCode = isUpiCode,
35+
)
36+
}
37+
38+
startPaymentProcessing()
39+
}
40+
41+
private fun startPaymentProcessing() {
42+
viewModelScope.launch {
43+
try {
44+
mutableStateFlow.update { it.copy(isProcessing = true) }
45+
46+
delay(3000) // Simulate payment processing time
47+
48+
mutableStateFlow.update { it.copy(isProcessing = false) }
49+
50+
delay(1000) // Show completion state briefly
51+
52+
sendEvent(PaymentProcessingEvent.PaymentComplete)
53+
} catch (e: Exception) {
54+
sendEvent(PaymentProcessingEvent.PaymentFailed(e.message ?: "Payment failed"))
55+
}
56+
}
57+
}
58+
59+
override fun handleAction(action: PaymentProcessingAction) {
60+
when (action) {
61+
PaymentProcessingAction.RetryPayment -> {
62+
startPaymentProcessing()
63+
}
64+
}
65+
}
66+
}
67+
68+
data class PaymentProcessingState(
69+
val payeeName: String = "",
70+
val amount: String = "",
71+
val isUpiCode: Boolean = false,
72+
val isProcessing: Boolean = true,
73+
) {
74+
val formattedAmount: String
75+
get() = if (amount.isEmpty()) "₹0" else "$amount"
76+
}
77+
78+
sealed interface PaymentProcessingEvent {
79+
data object PaymentComplete : PaymentProcessingEvent
80+
data class PaymentFailed(val errorMessage: String) : PaymentProcessingEvent
81+
}
82+
83+
sealed interface PaymentProcessingAction {
84+
data object RetryPayment : PaymentProcessingAction
85+
}

feature/send-money/src/commonMain/kotlin/org/mifospay/feature/send/money/di/SendMoneyModule.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package org.mifospay.feature.send.money.di
1212
import org.koin.core.module.dsl.viewModelOf
1313
import org.koin.dsl.module
1414
import org.mifospay.feature.send.money.PayeeDetailsViewModel
15+
import org.mifospay.feature.send.money.PaymentProcessingViewModel
1516
import org.mifospay.feature.send.money.ScannerModule
1617
import org.mifospay.feature.send.money.SendMoneyOptionsViewModel
1718
import org.mifospay.feature.send.money.SendMoneyViewModel
@@ -21,4 +22,5 @@ val SendMoneyModule = module {
2122
viewModelOf(::SendMoneyViewModel)
2223
viewModelOf(::SendMoneyOptionsViewModel)
2324
viewModelOf(::PayeeDetailsViewModel)
25+
viewModelOf(::PaymentProcessingViewModel)
2426
}

0 commit comments

Comments
 (0)