payments: ability to refund

This commit is contained in:
androidlover5842
2026-02-01 16:16:44 +05:30
parent 4c31a20af4
commit 3219e40a02
10 changed files with 323 additions and 125 deletions

View File

@@ -491,6 +491,7 @@ class MainActivity : ComponentActivity() {
bookingId = currentRoute.bookingId,
canAddCash = canManageRazorpaySettings(currentRoute.propertyId),
canDeleteCash = canDeleteCashPayment(currentRoute.propertyId),
canRefund = canManageRazorpaySettings(currentRoute.propertyId),
onBack = {
route.value = AppRoute.BookingDetailsTabs(
currentRoute.propertyId,

View File

@@ -22,6 +22,8 @@ import com.android.trisolarispms.data.api.model.RazorpayPaymentLinkRequest
import com.android.trisolarispms.data.api.model.RazorpayPaymentLinkResponse
import com.android.trisolarispms.data.api.model.PaymentDto
import com.android.trisolarispms.data.api.model.PaymentCreateRequest
import com.android.trisolarispms.data.api.model.RazorpayRefundRequest
import com.android.trisolarispms.data.api.model.RazorpayRefundResponse
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
@@ -132,6 +134,13 @@ interface BookingApi {
@Body body: PaymentCreateRequest
): Response<PaymentDto>
@POST("properties/{propertyId}/bookings/{bookingId}/payments/razorpay/refund")
suspend fun refundRazorpayPayment(
@Path("propertyId") propertyId: String,
@Path("bookingId") bookingId: String,
@Body body: RazorpayRefundRequest
): Response<RazorpayRefundResponse>
@DELETE("properties/{propertyId}/bookings/{bookingId}/payments/{paymentId}")
suspend fun deletePayment(
@Path("propertyId") propertyId: String,

View File

@@ -23,3 +23,17 @@ data class PaymentDto(
data class PaymentCreateRequest(
val amount: Long
)
data class RazorpayRefundRequest(
val paymentId: String? = null,
val razorpayPaymentId: String? = null,
val amount: Long,
val notes: String? = null
)
data class RazorpayRefundResponse(
val refundId: String? = null,
val status: String? = null,
val amount: Long? = null,
val currency: String? = null
)

View File

@@ -1,22 +1,23 @@
package com.android.trisolarispms.data.api.model
import com.google.gson.annotations.SerializedName
data class RazorpaySettingsRequest(
val keyId: String,
val keyId: String? = null,
val keySecret: String? = null,
val webhookSecret: String? = null,
@SerializedName("isTest") val isTest: Boolean,
val useSalt256: Boolean
val keyIdTest: String? = null,
val keySecretTest: String? = null,
val webhookSecretTest: String? = null,
val isTest: Boolean
)
data class RazorpaySettingsResponse(
val propertyId: String? = null,
val configured: Boolean? = null,
@SerializedName("test") val isTest: Boolean? = null,
val useSalt256: Boolean? = null,
@com.google.gson.annotations.SerializedName("test") val isTest: Boolean? = null,
val hasKeyId: Boolean? = null,
val hasKeySecret: Boolean? = null,
val hasWebhookSecret: Boolean? = null,
val hasSalt256: Boolean? = null
val hasKeyIdTest: Boolean? = null,
val hasKeySecretTest: Boolean? = null,
val hasWebhookSecretTest: Boolean? = null
)

View File

@@ -25,14 +25,17 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.AlertDialog
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
@@ -49,11 +52,15 @@ fun BookingPaymentsScreen(
bookingId: String,
canAddCash: Boolean,
canDeleteCash: Boolean,
canRefund: Boolean,
onBack: () -> Unit,
viewModel: BookingPaymentsViewModel = viewModel()
) {
val state by viewModel.state.collectAsState()
val amountInput = remember { mutableStateOf("") }
val refundTarget = remember { mutableStateOf<PaymentDto?>(null) }
val refundAmount = rememberSaveable { mutableStateOf("") }
val refundNotes = rememberSaveable { mutableStateOf("") }
LaunchedEffect(propertyId, bookingId) {
viewModel.load(propertyId, bookingId)
@@ -108,6 +115,10 @@ fun BookingPaymentsScreen(
Text(text = it, color = MaterialTheme.colorScheme.error)
Spacer(modifier = Modifier.height(8.dp))
}
state.message?.let {
Text(text = it, color = MaterialTheme.colorScheme.primary)
Spacer(modifier = Modifier.height(8.dp))
}
if (!state.isLoading && state.error == null && state.payments.isEmpty()) {
Text(text = "No payments yet")
}
@@ -121,21 +132,87 @@ fun BookingPaymentsScreen(
PaymentCard(
payment = payment,
canDeleteCash = canDeleteCash,
canRefund = canRefund,
onDelete = { paymentId ->
viewModel.deleteCashPayment(propertyId, bookingId, paymentId)
},
onRefund = { target ->
refundTarget.value = target
refundAmount.value = target.amount?.toString().orEmpty()
refundNotes.value = ""
}
)
}
}
}
}
refundTarget.value?.let { payment ->
val hasGateway = !payment.gatewayPaymentId.isNullOrBlank()
val hasPaymentId = !payment.id.isNullOrBlank()
val amountValue = refundAmount.value.toLongOrNull()
val canSubmit = amountValue != null && amountValue > 0 && (hasGateway || hasPaymentId)
AlertDialog(
onDismissRequest = { refundTarget.value = null },
title = { Text("Confirm refund") },
text = {
Column {
Text(
text = "This will initiate a Razorpay refund. Please confirm before proceeding.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = refundAmount.value,
onValueChange = { refundAmount.value = it.filter { ch -> ch.isDigit() } },
label = { Text("Refund amount") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = refundNotes.value,
onValueChange = { refundNotes.value = it },
label = { Text("Notes (optional)") },
modifier = Modifier.fillMaxWidth()
)
}
},
confirmButton = {
TextButton(
onClick = {
viewModel.refundRazorpayPayment(
propertyId = propertyId,
bookingId = bookingId,
paymentId = if (!payment.id.isNullOrBlank()) payment.id else null,
razorpayPaymentId = if (payment.id.isNullOrBlank()) payment.gatewayPaymentId else null,
amount = amountValue ?: 0L,
notes = refundNotes.value
)
refundTarget.value = null
},
enabled = canSubmit
) {
Text("Refund")
}
},
dismissButton = {
TextButton(onClick = { refundTarget.value = null }) {
Text("Cancel")
}
}
)
}
}
@Composable
private fun PaymentCard(
payment: PaymentDto,
canDeleteCash: Boolean,
onDelete: (String) -> Unit
canRefund: Boolean,
onDelete: (String) -> Unit,
onRefund: (PaymentDto) -> Unit
) {
val date = payment.receivedAt?.let {
runCatching { OffsetDateTime.parse(it) }.getOrNull()
@@ -163,6 +240,11 @@ private fun PaymentCard(
payment.currency?.let { append(" $it") }
}
Text(text = amountText, style = MaterialTheme.typography.titleMedium)
if (canRefund && (!payment.id.isNullOrBlank() || !payment.gatewayPaymentId.isNullOrBlank())) {
TextButton(onClick = { onRefund(payment) }) {
Text("Refund")
}
}
if (canDeleteCash && isCash && !payment.id.isNullOrBlank()) {
IconButton(onClick = { onDelete(payment.id) }) {
Icon(

View File

@@ -5,5 +5,6 @@ import com.android.trisolarispms.data.api.model.PaymentDto
data class BookingPaymentsState(
val isLoading: Boolean = false,
val error: String? = null,
val message: String? = null,
val payments: List<PaymentDto> = emptyList()
)

View File

@@ -3,6 +3,7 @@ package com.android.trisolarispms.ui.payment
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.android.trisolarispms.data.api.ApiClient
import com.android.trisolarispms.data.api.model.RazorpayRefundRequest
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@@ -14,7 +15,7 @@ class BookingPaymentsViewModel : ViewModel() {
fun load(propertyId: String, bookingId: String) {
viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null) }
_state.update { it.copy(isLoading = true, error = null, message = null) }
try {
val api = ApiClient.create()
val response = api.listPayments(propertyId, bookingId)
@@ -24,14 +25,16 @@ class BookingPaymentsViewModel : ViewModel() {
it.copy(
isLoading = false,
payments = body,
error = null
error = null,
message = null
)
}
} else {
_state.update {
it.copy(
isLoading = false,
error = "Load failed: ${response.code()}"
error = "Load failed: ${response.code()}",
message = null
)
}
}
@@ -39,7 +42,8 @@ class BookingPaymentsViewModel : ViewModel() {
_state.update {
it.copy(
isLoading = false,
error = e.localizedMessage ?: "Load failed"
error = e.localizedMessage ?: "Load failed",
message = null
)
}
}
@@ -48,11 +52,11 @@ class BookingPaymentsViewModel : ViewModel() {
fun addCashPayment(propertyId: String, bookingId: String, amount: Long) {
if (amount <= 0) {
_state.update { it.copy(error = "Amount must be greater than 0") }
_state.update { it.copy(error = "Amount must be greater than 0", message = null) }
return
}
viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null) }
_state.update { it.copy(isLoading = true, error = null, message = null) }
try {
val api = ApiClient.create()
val response = api.createPayment(
@@ -66,14 +70,16 @@ class BookingPaymentsViewModel : ViewModel() {
current.copy(
isLoading = false,
payments = listOf(body) + current.payments,
error = null
error = null,
message = "Cash payment added"
)
}
} else {
_state.update {
it.copy(
isLoading = false,
error = "Create failed: ${response.code()}"
error = "Create failed: ${response.code()}",
message = null
)
}
}
@@ -81,7 +87,8 @@ class BookingPaymentsViewModel : ViewModel() {
_state.update {
it.copy(
isLoading = false,
error = e.localizedMessage ?: "Create failed"
error = e.localizedMessage ?: "Create failed",
message = null
)
}
}
@@ -90,7 +97,7 @@ class BookingPaymentsViewModel : ViewModel() {
fun deleteCashPayment(propertyId: String, bookingId: String, paymentId: String) {
viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null) }
_state.update { it.copy(isLoading = true, error = null, message = null) }
try {
val api = ApiClient.create()
val response = api.deletePayment(
@@ -103,14 +110,16 @@ class BookingPaymentsViewModel : ViewModel() {
current.copy(
isLoading = false,
payments = current.payments.filterNot { it.id == paymentId },
error = null
error = null,
message = "Cash payment deleted"
)
}
} else {
_state.update {
it.copy(
isLoading = false,
error = "Delete failed: ${response.code()}"
error = "Delete failed: ${response.code()}",
message = null
)
}
}
@@ -118,7 +127,69 @@ class BookingPaymentsViewModel : ViewModel() {
_state.update {
it.copy(
isLoading = false,
error = e.localizedMessage ?: "Delete failed"
error = e.localizedMessage ?: "Delete failed",
message = null
)
}
}
}
}
fun refundRazorpayPayment(
propertyId: String,
bookingId: String,
paymentId: String?,
razorpayPaymentId: String?,
amount: Long,
notes: String?
) {
if (amount <= 0) {
_state.update { it.copy(error = "Amount must be greater than 0", message = null) }
return
}
if (paymentId.isNullOrBlank() && razorpayPaymentId.isNullOrBlank()) {
_state.update { it.copy(error = "Missing payment ID", message = null) }
return
}
viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null, message = null) }
try {
val api = ApiClient.create()
val response = api.refundRazorpayPayment(
propertyId = propertyId,
bookingId = bookingId,
body = RazorpayRefundRequest(
paymentId = paymentId,
razorpayPaymentId = razorpayPaymentId,
amount = amount,
notes = notes?.takeIf { it.isNotBlank() }
)
)
val body = response.body()
if (response.isSuccessful && body != null) {
_state.update {
it.copy(
isLoading = false,
error = null,
message = "Refund processed"
)
}
load(propertyId, bookingId)
} else {
_state.update {
it.copy(
isLoading = false,
error = "Refund failed: ${response.code()}",
message = null
)
}
}
} catch (e: Exception) {
_state.update {
it.copy(
isLoading = false,
error = e.localizedMessage ?: "Refund failed",
message = null
)
}
}

View File

@@ -74,11 +74,12 @@ fun RazorpaySettingsScreen(
propertyId = propertyId,
state = state,
onKeyIdChange = viewModel::onMerchantKeyChange,
onKeySecretChange = viewModel::onSalt32Change,
onKeySecretChange = viewModel::onKeySecretChange,
onWebhookSecretChange = viewModel::onWebhookSecretChange,
onKeyIdTestChange = viewModel::onKeyIdTestChange,
onKeySecretTestChange = viewModel::onKeySecretTestChange,
onWebhookSecretTestChange = viewModel::onWebhookSecretTestChange,
onIsTestChange = viewModel::onIsTestChange,
onUseSalt256Change = viewModel::onUseSalt256Change,
onSalt256Change = viewModel::onSalt256Change,
onSave = { viewModel.save(propertyId) },
clipboard = clipboard
)
@@ -93,9 +94,10 @@ private fun RazorpaySettingsTab(
onKeyIdChange: (String) -> Unit,
onKeySecretChange: (String) -> Unit,
onWebhookSecretChange: (String) -> Unit,
onKeyIdTestChange: (String) -> Unit,
onKeySecretTestChange: (String) -> Unit,
onWebhookSecretTestChange: (String) -> Unit,
onIsTestChange: (Boolean) -> Unit,
onUseSalt256Change: (Boolean) -> Unit,
onSalt256Change: (String) -> Unit,
onSave: () -> Unit,
clipboard: ClipboardManager
) {
@@ -132,15 +134,12 @@ private fun RazorpaySettingsTab(
style = MaterialTheme.typography.bodySmall
)
Spacer(modifier = Modifier.height(8.dp))
if (state.hasKeyId) {
Text(text = "Key ID saved", style = MaterialTheme.typography.bodySmall)
}
if (state.hasKeySecret) {
Text(text = "Key Secret saved", style = MaterialTheme.typography.bodySmall)
}
if (state.hasWebhookSecret) {
Text(text = "Webhook Secret saved", style = MaterialTheme.typography.bodySmall)
}
if (state.hasKeyId) Text(text = "Live Key ID saved", style = MaterialTheme.typography.bodySmall)
if (state.hasKeySecret) Text(text = "Live Key Secret saved", style = MaterialTheme.typography.bodySmall)
if (state.hasWebhookSecret) Text(text = "Live Webhook Secret saved", style = MaterialTheme.typography.bodySmall)
if (state.hasKeyIdTest) Text(text = "Test Key ID saved", style = MaterialTheme.typography.bodySmall)
if (state.hasKeySecretTest) Text(text = "Test Key Secret saved", style = MaterialTheme.typography.bodySmall)
if (state.hasWebhookSecretTest) Text(text = "Test Webhook Secret saved", style = MaterialTheme.typography.bodySmall)
Spacer(modifier = Modifier.height(12.dp))
state.message?.let {
@@ -153,10 +152,20 @@ private fun RazorpaySettingsTab(
Spacer(modifier = Modifier.height(8.dp))
}
Text(text = "Live", style = MaterialTheme.typography.titleSmall)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = state.keyId,
onValueChange = onKeyIdChange,
label = { Text("Key ID") },
label = { Text("Key ID (Live)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.keySecret,
onValueChange = onKeySecretChange,
label = { Text("Key Secret (Live)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
@@ -164,7 +173,34 @@ private fun RazorpaySettingsTab(
OutlinedTextField(
value = state.webhookSecret,
onValueChange = onWebhookSecretChange,
label = { Text("Webhook Secret") },
label = { Text("Webhook Secret (Live)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Text(text = "Test", style = MaterialTheme.typography.titleSmall)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = state.keyIdTest,
onValueChange = onKeyIdTestChange,
label = { Text("Key ID (Test)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.keySecretTest,
onValueChange = onKeySecretTestChange,
label = { Text("Key Secret (Test)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.webhookSecretTest,
onValueChange = onWebhookSecretTestChange,
label = { Text("Webhook Secret (Test)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
@@ -183,53 +219,8 @@ private fun RazorpaySettingsTab(
}
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = "Use secondary secret")
Switch(
checked = state.useSalt256,
onCheckedChange = onUseSalt256Change
)
}
Spacer(modifier = Modifier.height(8.dp))
if (state.useSalt256) {
OutlinedTextField(
value = state.salt256,
onValueChange = onSalt256Change,
label = { Text("Secret (secondary)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
if (state.hasSalt256) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Salt256 saved",
style = MaterialTheme.typography.bodySmall
)
}
} else {
OutlinedTextField(
value = state.keySecret,
onValueChange = onKeySecretChange,
label = { Text("Key Secret") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
modifier = Modifier.fillMaxWidth()
)
if (state.hasKeySecret) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Key Secret saved",
style = MaterialTheme.typography.bodySmall
)
}
}
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = onSave,
enabled = !state.isSaving,

View File

@@ -4,13 +4,16 @@ data class RazorpaySettingsState(
val keyId: String = "",
val keySecret: String = "",
val webhookSecret: String = "",
val salt256: String = "",
val keyIdTest: String = "",
val keySecretTest: String = "",
val webhookSecretTest: String = "",
val isTest: Boolean = false,
val useSalt256: Boolean = false,
val hasKeyId: Boolean = false,
val hasKeySecret: Boolean = false,
val hasWebhookSecret: Boolean = false,
val hasSalt256: Boolean = false,
val hasKeyIdTest: Boolean = false,
val hasKeySecretTest: Boolean = false,
val hasWebhookSecretTest: Boolean = false,
val configured: Boolean = false,
val isLoading: Boolean = false,
val isSaving: Boolean = false,

View File

@@ -23,7 +23,7 @@ class RazorpaySettingsViewModel : ViewModel() {
_state.update { it.copy(keyId = value, error = null) }
}
fun onSalt32Change(value: String) {
fun onKeySecretChange(value: String) {
_state.update { it.copy(keySecret = value, error = null) }
}
@@ -31,37 +31,51 @@ class RazorpaySettingsViewModel : ViewModel() {
_state.update { it.copy(webhookSecret = value, error = null) }
}
fun onSalt256Change(value: String) {
_state.update { it.copy(salt256 = value, error = null) }
fun onKeyIdTestChange(value: String) {
_state.update { it.copy(keyIdTest = value, error = null) }
}
fun onKeySecretTestChange(value: String) {
_state.update { it.copy(keySecretTest = value, error = null) }
}
fun onWebhookSecretTestChange(value: String) {
_state.update { it.copy(webhookSecretTest = value, error = null) }
}
fun onIsTestChange(value: Boolean) {
_state.update { it.copy(isTest = value, error = null) }
}
fun onUseSalt256Change(value: Boolean) {
_state.update { it.copy(useSalt256 = value, error = null) }
}
fun save(propertyId: String) {
val current = state.value
val keyId = current.keyId.trim()
val keySecret = current.keySecret.trim()
val webhookSecret = current.webhookSecret.trim()
val salt256 = current.salt256.trim()
val keyIdTest = current.keyIdTest.trim()
val keySecretTest = current.keySecretTest.trim()
val webhookSecretTest = current.webhookSecretTest.trim()
if (!current.configured) {
if (keyId.isBlank()) {
val needsTest = current.isTest
if (needsTest) {
if (keyIdTest.isBlank() && !current.hasKeyIdTest) {
_state.update { it.copy(error = "Test Key ID is required") }
return
}
if (keySecretTest.isBlank() && !current.hasKeySecretTest) {
_state.update { it.copy(error = "Test Key Secret is required") }
return
}
} else {
if (keyId.isBlank() && !current.hasKeyId) {
_state.update { it.copy(error = "Key ID is required") }
return
}
if (current.useSalt256 && salt256.isBlank() && !current.hasSalt256) {
_state.update { it.copy(error = "Secondary secret is required") }
if (keySecret.isBlank() && !current.hasKeySecret) {
_state.update { it.copy(error = "Key Secret is required") }
return
}
if (!current.useSalt256 && keySecret.isBlank() && !current.hasKeySecret) {
_state.update { it.copy(error = "Key secret is required") }
return
}
} else {
val hasKeyIdInput = keyId.isNotBlank()
@@ -70,6 +84,12 @@ class RazorpaySettingsViewModel : ViewModel() {
_state.update { it.copy(error = "Key ID and Key Secret must be provided together") }
return
}
val hasKeyIdTestInput = keyIdTest.isNotBlank()
val hasKeySecretTestInput = keySecretTest.isNotBlank()
if (hasKeyIdTestInput xor hasKeySecretTestInput) {
_state.update { it.copy(error = "Test Key ID and Test Key Secret must be provided together") }
return
}
}
viewModelScope.launch {
@@ -79,11 +99,13 @@ class RazorpaySettingsViewModel : ViewModel() {
val response = api.updateRazorpaySettings(
propertyId = propertyId,
body = RazorpaySettingsRequest(
keyId = keyId,
keyId = keyId.ifBlank { null },
keySecret = keySecret.ifBlank { null },
webhookSecret = webhookSecret.ifBlank { null },
isTest = current.isTest,
useSalt256 = current.useSalt256
keyIdTest = keyIdTest.ifBlank { null },
keySecretTest = keySecretTest.ifBlank { null },
webhookSecretTest = webhookSecretTest.ifBlank { null },
isTest = current.isTest
)
)
if (response.isSuccessful) {
@@ -124,15 +146,18 @@ class RazorpaySettingsViewModel : ViewModel() {
it.copy(
keyId = "",
isTest = body.isTest == true,
useSalt256 = body.useSalt256 == true,
hasKeyId = body.hasKeyId == true,
hasKeySecret = body.hasKeySecret == true,
hasWebhookSecret = body.hasWebhookSecret == true,
hasSalt256 = body.hasSalt256 == true,
hasKeyIdTest = body.hasKeyIdTest == true,
hasKeySecretTest = body.hasKeySecretTest == true,
hasWebhookSecretTest = body.hasWebhookSecretTest == true,
configured = body.configured == true,
keySecret = "",
webhookSecret = "",
salt256 = "",
keyIdTest = "",
keySecretTest = "",
webhookSecretTest = "",
isLoading = false,
isSaving = false,
error = null