Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.tree.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3012,6 +3012,38 @@
| | +--- org.jetbrains.compose.material3:material3:1.8.2 (*)
| | +--- org.jetbrains.compose.components:components-resources:1.8.2 (*)
| | \--- org.jetbrains.compose.components:components-ui-tooling-preview:1.8.2 (*)
| +--- project :feature:autopay
| | +--- androidx.lifecycle:lifecycle-runtime-compose:2.9.2 (*)
| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.9.2 (*)
| | +--- androidx.tracing:tracing-ktx:1.3.0 (*)
| | +--- io.insert-koin:koin-bom:4.1.0 (*)
| | +--- io.insert-koin:koin-android:4.1.0 (*)
| | +--- io.insert-koin:koin-androidx-compose:4.1.0 (*)
| | +--- io.insert-koin:koin-androidx-navigation:4.1.0 (*)
| | +--- io.insert-koin:koin-core-viewmodel:4.1.0 (*)
| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.1.20 -> 2.1.21 (*)
| | +--- io.insert-koin:koin-core:4.1.0 (*)
| | +--- io.insert-koin:koin-annotations:2.1.0 (*)
| | +--- project :core:ui (*)
| | +--- project :core:designsystem (*)
| | +--- project :core:data (*)
| | +--- io.insert-koin:koin-compose:4.1.0 (*)
| | +--- io.insert-koin:koin-compose-viewmodel:4.1.0 (*)
| | +--- org.jetbrains.compose.runtime:runtime:1.8.2 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.1 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.1 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.1 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.1 (*)
| | +--- org.jetbrains.androidx.savedstate:savedstate:1.3.1 (*)
| | +--- org.jetbrains.androidx.core:core-bundle:1.0.1 (*)
| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.9.0-beta03 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 -> 1.8.0 (*)
| | +--- org.jetbrains.compose.ui:ui:1.8.2 (*)
| | +--- org.jetbrains.compose.foundation:foundation:1.8.2 (*)
| | +--- org.jetbrains.compose.material3:material3:1.8.2 (*)
| | +--- org.jetbrains.compose.components:components-resources:1.8.2 (*)
| | \--- org.jetbrains.compose.components:components-ui-tooling-preview:1.8.2 (*)
| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.1.20 (*)
+--- project :core:data (*)
+--- project :core:ui (*)
Expand Down
1 change: 1 addition & 0 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
:core:ui
:feature:accounts
:feature:auth
:feature:autopay
:feature:editpassword
:feature:faq
:feature:finance
Expand Down
2 changes: 1 addition & 1 deletion cmp-android/prodRelease-badging.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package: name='org.mifospay' versionCode='1' versionName='2025.8.2-beta.0.3' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
package: name='org.mifospay' versionCode='1' versionName='2025.8.4-beta.0.9' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
minSdkVersion:'26'
targetSdkVersion:'34'
uses-permission: name='android.permission.INTERNET'
Expand Down
1 change: 1 addition & 0 deletions cmp-shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ kotlin {
implementation(projects.feature.qr)
implementation(projects.feature.merchants)
implementation(projects.feature.upiSetup)
implementation(projects.feature.autopay)
}

desktopMain.dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.mifospay.core.network.di.LocalModule
import org.mifospay.core.network.di.NetworkModule
import org.mifospay.feature.accounts.di.AccountsModule
import org.mifospay.feature.auth.di.AuthModule
import org.mifospay.feature.autopay.di.AutoPayModule
import org.mifospay.feature.editpassword.di.EditPasswordModule
import org.mifospay.feature.faq.di.FaqModule
import org.mifospay.feature.history.di.HistoryModule
Expand Down Expand Up @@ -88,6 +89,7 @@ object KoinModules {
QrModule,
MerchantsModule,
UpiSetupModule,
AutoPayModule,
)
}
private val LibraryModule = module {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import org.mifospay.feature.accounts.savingsaccount.addEditSavingAccountScreen
import org.mifospay.feature.accounts.savingsaccount.details.navigateToSavingAccountDetails
import org.mifospay.feature.accounts.savingsaccount.details.savingAccountDetailRoute
import org.mifospay.feature.accounts.savingsaccount.navigateToSavingAccountAddEdit
import org.mifospay.feature.autopay.AutoPayScreen
import org.mifospay.feature.autopay.autoPayGraph
import org.mifospay.feature.autopay.navigateToAutoPay
import org.mifospay.feature.autopay.navigateToAutoPayHistory
import org.mifospay.feature.autopay.navigateToAutoPayPreferences
import org.mifospay.feature.autopay.navigateToAutoPayRules
import org.mifospay.feature.autopay.navigateToAutoPaySetup
import org.mifospay.feature.editpassword.navigation.editPasswordScreen
import org.mifospay.feature.editpassword.navigation.navigateToEditPassword
import org.mifospay.feature.faq.navigation.faqScreen
Expand Down Expand Up @@ -72,7 +79,12 @@ import org.mifospay.feature.savedcards.details.cardDetailRoute
import org.mifospay.feature.savedcards.details.navigateToCardDetails
import org.mifospay.feature.send.money.SendMoneyScreen
import org.mifospay.feature.send.money.navigation.SEND_MONEY_BASE_ROUTE
import org.mifospay.feature.send.money.navigation.SEND_MONEY_OPTIONS_ROUTE
import org.mifospay.feature.send.money.navigation.navigateToPayeeDetailsScreen
import org.mifospay.feature.send.money.navigation.navigateToSendMoneyOptionsScreen
import org.mifospay.feature.send.money.navigation.navigateToSendMoneyScreen
import org.mifospay.feature.send.money.navigation.payeeDetailsScreen
import org.mifospay.feature.send.money.navigation.sendMoneyOptionsScreen
import org.mifospay.feature.send.money.navigation.sendMoneyScreen
import org.mifospay.feature.settings.navigation.settingsScreen
import org.mifospay.feature.standing.instruction.StandingInstructionsScreen
Expand All @@ -97,6 +109,7 @@ internal fun MifosNavHost(
onBackClick = navController::navigateUp,
navigateToTransferScreen = navController::navigateToTransferScreen,
navigateToScanQrScreen = navController::navigateToScanQr,
navigateToPayeeDetails = navController::navigateToPayeeDetailsScreen,
showTopBar = false,
)
},
Expand All @@ -121,6 +134,22 @@ internal fun MifosNavHost(
navigateToInvoiceDetailScreen = navController::navigateToInvoiceDetail,
)
},
TabContent(PaymentsScreenContents.AUTOPAY.name) {
AutoPayScreen(
onNavigateToSetup = {
navController.navigateToAutoPaySetup()
},
onNavigateToRules = {
navController.navigateToAutoPayRules()
},
onNavigateToPreferences = {
navController.navigateToAutoPayPreferences()
},
onNavigateToHistory = {
navController.navigateToAutoPayHistory()
},
)
},
)

val tabContents = listOf(
Expand Down Expand Up @@ -160,7 +189,10 @@ internal fun MifosNavHost(
onRequest = {
navController.navigateToShowQrScreen()
},
onPay = navController::navigateToSendMoneyScreen,
onPay = navController::navigateToSendMoneyOptionsScreen,
onAutoPay = {
navController.navigateToAutoPay()
},
navigateToTransactionDetail = navController::navigateToSpecificTransaction,
navigateToAccountDetail = navController::navigateToSavingAccountDetails,
)
Expand Down Expand Up @@ -279,12 +311,55 @@ internal fun MifosNavHost(
navigateBack = navController::navigateUp,
)

sendMoneyOptionsScreen(
onBackClick = navController::popBackStack,
onScanQrClick = {
// This is now handled by the ViewModel using ML Kit scanner
},
onPayAnyoneClick = {
// TODO: Navigate to Pay Anyone screen
},
onBankTransferClick = {
// TODO: Navigate to Bank Transfer screen
},
onFineractPaymentsClick = {
navController.navigateToSendMoneyScreen()
},
onAutoPayClick = {
navController.navigateToAutoPay()
},
onQrCodeScanned = { qrData ->
navController.navigateToSendMoneyScreen(
requestData = qrData,
navOptions = navOptions {
popUpTo(SEND_MONEY_OPTIONS_ROUTE) {
inclusive = true
}
},
)
},
onNavigateToPayeeDetails = { qrCodeData ->
navController.navigateToPayeeDetailsScreen(qrCodeData)
},
)

sendMoneyScreen(
onBackClick = navController::popBackStack,
navigateToTransferScreen = navController::navigateToTransferScreen,
navigateToPayeeDetailsScreen = navController::navigateToPayeeDetailsScreen,
navigateToScanQrScreen = navController::navigateToScanQr,
)

payeeDetailsScreen(
onBackClick = navController::popBackStack,
onNavigateToUpiPayment = { state ->
// TODO: Handle UPI payment navigation
},
onNavigateToFineractPayment = { state ->
// TODO: Handle Fineract payment navigation
},
)

transferScreen(
navigateBack = navController::popBackStack,
onTransferSuccess = {
Expand Down Expand Up @@ -322,6 +397,16 @@ internal fun MifosNavHost(
},
)
},
navigateToPayeeDetailsScreen = {
navController.navigateToPayeeDetailsScreen(
qrCodeData = it,
navOptions = navOptions {
popUpTo(SCAN_QR_ROUTE) {
inclusive = true
}
},
)
},
)

merchantTransferScreen(
Expand All @@ -332,5 +417,10 @@ internal fun MifosNavHost(
setupUpiPinScreen(
navigateBack = navController::navigateUp,
)

autoPayGraph(
navController = navController,
onNavigateBack = navController::navigateUp,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2024 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
*/
package org.mifospay.core.data.util

import org.mifospay.core.model.utils.PaymentQrData
import org.mifospay.core.model.utils.StandardUpiQrData

/**
* Standard UPI QR Code Processor
* Handles parsing of standard UPI QR codes according to UPI specification
*/
object StandardUpiQrCodeProcessor {

/**
* Checks if the given string is a valid UPI QR code
* @param qrData The QR code data string
* @return true if it's a valid UPI QR code, false otherwise
*/
fun isValidUpiQrCode(qrData: String): Boolean {
return qrData.startsWith("upi://") || qrData.startsWith("UPI://")
}

/**
* Parses a standard UPI QR code string
* @param qrData The QR code data string
* @return StandardUpiQrData object with parsed information
* @throws IllegalArgumentException if the QR code is invalid
*/
fun parseUpiQrCode(qrData: String): StandardUpiQrData {
if (!isValidUpiQrCode(qrData)) {
throw IllegalArgumentException("Invalid UPI QR code format")
}

val paramsString = qrData.substringAfter("upi://").substringAfter("UPI://")
val parts = paramsString.split("?", limit = 2)
val params = if (parts.size > 1) parseParams(parts[1]) else emptyMap()

val payeeVpa = params["pa"] ?: run {
throw IllegalArgumentException("Missing payee VPA (pa) in UPI QR code")
}
val payeeName = params["pn"] ?: "Unknown"

val vpaParts = payeeVpa.split("@", limit = 2)
val actualVpa = if (vpaParts.size == 2) payeeVpa else payeeVpa

return StandardUpiQrData(
payeeName = payeeName,
payeeVpa = actualVpa,
amount = params["am"] ?: "",
currency = params["cu"] ?: StandardUpiQrData.DEFAULT_CURRENCY,
transactionNote = params["tn"] ?: "",
merchantCode = params["mc"] ?: "",
transactionReference = params["tr"] ?: "",
url = params["url"] ?: "",
mode = params["mode"] ?: "02",
)
}

/**
* Parses URL parameters into a map
* @param paramsString The parameters string
* @return Map of parameter keys and values
*/
private fun parseParams(paramsString: String): Map<String, String> {
return paramsString
.split("&")
.associate { param ->
val keyValue = param.split("=", limit = 2)
if (keyValue.size == 2) {
keyValue[0] to keyValue[1]
} else {
param to ""
}
}
}

/**
* Converts StandardUpiQrData to PaymentQrData for compatibility with existing code
* @param standardData Standard UPI QR data
* @return PaymentQrData object
* Note: clientId and accountId not available in standard UPI
*/
fun toPaymentQrData(standardData: StandardUpiQrData): PaymentQrData {
return PaymentQrData(
clientId = 0,
clientName = standardData.payeeName,
accountNo = standardData.payeeVpa,
amount = standardData.amount,
accountId = 0,
currency = standardData.currency,
officeId = 1,
accountTypeId = 2,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package org.mifospay.core.designsystem.icon

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.ArrowOutward
import androidx.compose.material.icons.filled.AttachMoney
Expand All @@ -22,11 +23,13 @@ import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.CurrencyRupee
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Description
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.FlashOff
import androidx.compose.material.icons.filled.FlashOn
import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.Person
Expand Down Expand Up @@ -129,4 +132,9 @@ object MifosIcons {
val Scan = Icons.Outlined.QrCodeScanner
val RadioButtonUnchecked = Icons.Default.RadioButtonUnchecked
val RadioButtonChecked = Icons.Filled.RadioButtonChecked

val ArrowForward = Icons.AutoMirrored.Filled.ArrowForward

val CurrencyRupee = Icons.Filled.CurrencyRupee
val History = Icons.Filled.History
}
Loading
Loading