summaryrefslogtreecommitdiff
path: root/wallet/src/main/java/net/taler
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2023-09-20 15:14:30 +0200
committerTorsten Grote <t@grobox.de>2023-09-26 18:30:53 +0200
commit559bfbe2f722144e669ff5810dee3c9e41f9e06e (patch)
tree9ac983c0dee0eaeb7becc3ee82fa1e18b7bedf33 /wallet/src/main/java/net/taler
parentf967d32d89dde913ea66aba4884cd9970bef53a5 (diff)
downloadtaler-android-559bfbe2f722144e669ff5810dee3c9e41f9e06e.tar.gz
taler-android-559bfbe2f722144e669ff5810dee3c9e41f9e06e.tar.bz2
taler-android-559bfbe2f722144e669ff5810dee3c9e41f9e06e.zip
[wallet] simplify pay templates
Diffstat (limited to 'wallet/src/main/java/net/taler')
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt211
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt65
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt179
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt9
4 files changed, 295 insertions, 169 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
index 59a088d..815f463 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
@@ -17,151 +17,62 @@
package net.taler.wallet.payment
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.Center
-import androidx.compose.ui.Alignment.Companion.End
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
import net.taler.common.Amount
+import net.taler.common.ContractTerms
import net.taler.wallet.AmountResult
import net.taler.wallet.R
-import net.taler.wallet.compose.AmountInputField
import net.taler.wallet.compose.TalerSurface
-import net.taler.wallet.deposit.CurrencyDropdown
sealed class AmountFieldStatus {
- object FixedAmount: AmountFieldStatus()
+ object FixedAmount : AmountFieldStatus()
class Default(
val amountStr: String? = null,
val currency: String? = null,
- ): AmountFieldStatus()
- object Invalid: AmountFieldStatus()
+ ) : AmountFieldStatus()
+
+ object Invalid : AmountFieldStatus()
}
@Composable
fun PayTemplateComposable(
- summary: String?,
+ defaultSummary: String?,
amountStatus: AmountFieldStatus,
currencies: List<String>,
payStatus: PayStatus,
onCreateAmount: (String, String) -> AmountResult,
- onSubmit: (Map<String, String>) -> Unit,
+ onSubmit: (summary: String?, amount: Amount?) -> Unit,
onError: (resId: Int) -> Unit,
) {
-
// If wallet is empty, there's no way the user can pay something
if (amountStatus is AmountFieldStatus.Invalid) {
PayTemplateError(stringResource(R.string.receive_amount_invalid))
- } else if (payStatus is PayStatus.InsufficientBalance || currencies.isEmpty()) {
+ } else if (currencies.isEmpty()) {
PayTemplateError(stringResource(R.string.payment_balance_insufficient))
} else when (payStatus) {
- is PayStatus.None -> PayTemplateDefault(
+ is PayStatus.None -> PayTemplateOrderComposable(
currencies = currencies,
- summary = summary,
+ defaultSummary = defaultSummary,
amountStatus = amountStatus,
onCreateAmount = onCreateAmount,
onError = onError,
- onSubmit = { s, a ->
- onSubmit(mutableMapOf<String, String>().apply {
- s?.let { put("summary", it) }
- a?.let { put("amount", it.toJSONString()) }
- })
- }
+ onSubmit = onSubmit,
)
+
is PayStatus.Loading -> PayTemplateLoading()
is PayStatus.AlreadyPaid -> PayTemplateError(stringResource(R.string.payment_already_paid))
-
- // TODO we should handle the other cases or explain why we don't handle them
- else -> {}
- }
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun PayTemplateDefault(
- currencies: List<String>,
- summary: String? = null,
- amountStatus: AmountFieldStatus,
- onCreateAmount: (String, String) -> AmountResult,
- onError: (msgRes: Int) -> Unit,
- onSubmit: (summary: String?, amount: Amount?) -> Unit,
-) {
- val amountDefault = amountStatus as? AmountFieldStatus.Default
-
- var localSummary by remember { mutableStateOf(summary) }
- var localAmount by remember { mutableStateOf(
- amountDefault?.let { s -> s.amountStr ?: "0" }
- ) }
- var localCurrency by remember { mutableStateOf(
- amountDefault?.let { s -> s.currency ?: currencies[0] }
- ) }
-
- Column(horizontalAlignment = End) {
- localSummary?.let { summary ->
- OutlinedTextField(
- modifier = Modifier
- .padding(horizontal = 16.dp)
- .fillMaxWidth(),
- value = summary,
- isError = summary.isBlank(),
- onValueChange = { localSummary = it },
- singleLine = true,
- label = { Text(stringResource(R.string.withdraw_manual_ready_subject)) },
- )
- }
-
- localAmount?.let { amount ->
- localCurrency?.let { currency ->
- AmountField(
- modifier = Modifier
- .padding(16.dp)
- .fillMaxWidth(),
- amount = amount,
- currency = currency,
- currencies = currencies,
- fixedCurrency = (amountStatus as? AmountFieldStatus.Default)?.currency != null,
- onAmountChosen = { a, c ->
- localAmount = a
- localCurrency = c
- },
- )
- }
- }
-
- Button(
- modifier = Modifier.padding(16.dp),
- enabled = localSummary == null || localSummary!!.isNotBlank(),
- onClick = {
- localAmount?.let { amount ->
- localCurrency?.let { currency ->
- when (val res = onCreateAmount(amount, currency)) {
- is AmountResult.InsufficientBalance -> onError(R.string.payment_balance_insufficient)
- is AmountResult.InvalidAmount -> onError(R.string.receive_amount_invalid)
- is AmountResult.Success -> onSubmit(summary, res.amount)
- }
- }
- }
- },
- ) {
- Text(stringResource(R.string.payment_create_order))
- }
+ is PayStatus.InsufficientBalance -> PayTemplateError(stringResource(R.string.payment_balance_insufficient))
+ is PayStatus.Error -> {} // handled in fragment will show bottom sheet FIXME white page?
+ is PayStatus.Prepared -> {} // handled in fragment, will redirect
+ is PayStatus.Success -> {} // handled by other UI flow, no need for content here
}
}
@@ -189,51 +100,81 @@ fun PayTemplateLoading() {
}
}
+@Preview
@Composable
-private fun AmountField(
- modifier: Modifier = Modifier,
- currencies: List<String>,
- fixedCurrency: Boolean,
- amount: String,
- currency: String,
- onAmountChosen: (amount: String, currency: String) -> Unit,
-) {
- Row(
- modifier = modifier,
- ) {
- AmountInputField(
- modifier = Modifier
- .padding(end = 16.dp)
- .weight(1f),
- value = amount,
- onValueChange = { onAmountChosen(it, currency) },
- label = { Text(stringResource(R.string.send_amount)) }
+fun PayTemplateLoadingPreview() {
+ TalerSurface {
+ PayTemplateComposable(
+ defaultSummary = "Donation",
+ amountStatus = AmountFieldStatus.Default("20", "ARS"),
+ payStatus = PayStatus.Loading,
+ currencies = listOf("KUDOS", "ARS"),
+ onCreateAmount = { text, currency ->
+ AmountResult.Success(amount = Amount.fromString(currency, text))
+ },
+ onSubmit = { _, _ -> },
+ onError = { _ -> },
)
- CurrencyDropdown(
- modifier = Modifier.weight(1f),
- initialCurrency = currency,
- currencies = currencies,
- onCurrencyChanged = { onAmountChosen(amount, it) },
- readOnly = fixedCurrency,
+ }
+}
+
+@Preview
+@Composable
+fun PayTemplateInsufficientBalancePreview() {
+ TalerSurface {
+ PayTemplateComposable(
+ defaultSummary = "Donation",
+ amountStatus = AmountFieldStatus.Default("20", "ARS"),
+ payStatus = PayStatus.InsufficientBalance(
+ ContractTerms(
+ "test",
+ amount = Amount.zero("TESTKUDOS"),
+ products = emptyList()
+ ), Amount.zero("TESTKUDOS")
+ ),
+ currencies = listOf("KUDOS", "ARS"),
+ onCreateAmount = { text, currency ->
+ AmountResult.Success(amount = Amount.fromString(currency, text))
+ },
+ onSubmit = { _, _ -> },
+ onError = { _ -> },
)
}
}
@Preview
@Composable
-fun PayTemplateComposablePreview() {
+fun PayTemplateAlreadyPaidPreview() {
TalerSurface {
PayTemplateComposable(
- summary = "Donation",
- amountStatus = AmountFieldStatus.Default("20", "ARS"),
+ defaultSummary = "Donation",
+ amountStatus = AmountFieldStatus.Default("20", "ARS"),
+ payStatus = PayStatus.AlreadyPaid,
currencies = listOf("KUDOS", "ARS"),
- // TODO create previews for other states
+ onCreateAmount = { text, currency ->
+ AmountResult.Success(amount = Amount.fromString(currency, text))
+ },
+ onSubmit = { _, _ -> },
+ onError = { _ -> },
+ )
+ }
+}
+
+
+@Preview
+@Composable
+fun PayTemplateNoCurrenciesPreview() {
+ TalerSurface {
+ PayTemplateComposable(
+ defaultSummary = "Donation",
+ amountStatus = AmountFieldStatus.Default("20", "ARS"),
payStatus = PayStatus.None,
+ currencies = emptyList(),
onCreateAmount = { text, currency ->
AmountResult.Success(amount = Amount.fromString(currency, text))
},
- onSubmit = { },
- onError = { },
+ onSubmit = { _, _ -> },
+ onError = { _ -> },
)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
index 01160ec..c902d65 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
@@ -21,18 +21,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.asFlow
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
+import net.taler.common.Amount
import net.taler.common.showError
import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.compose.TalerSurface
+import net.taler.wallet.compose.collectAsStateLifecycleAware
import net.taler.wallet.showError
class PayTemplateFragment : Fragment() {
@@ -49,37 +49,23 @@ class PayTemplateFragment : Fragment() {
uriString = arguments?.getString("uri") ?: error("no amount passed")
uri = Uri.parse(uriString)
- val queryParams = uri.queryParameterNames
+ val defaultSummary = uri.getQueryParameter("summary")
+ val defaultAmount = uri.getQueryParameter("amount")
+ val amountFieldStatus = getAmountFieldStatus(defaultAmount)
- val summary = if ("summary" in queryParams)
- uri.getQueryParameter("summary")!! else null
-
- val amountStatus = if ("amount" in queryParams) {
- val amount = uri.getQueryParameter("amount")!!
- val parts = if (amount.isEmpty()) emptyList() else amount.split(":")
- when (parts.size) {
- 0 -> AmountFieldStatus.Default()
- 1 -> AmountFieldStatus.Default(currency = parts[0])
- 2 -> AmountFieldStatus.Default(parts[1], parts[0])
- else -> AmountFieldStatus.Invalid
- }
- } else AmountFieldStatus.FixedAmount
+ val payStatusFlow = model.paymentManager.payStatus.asFlow()
return ComposeView(requireContext()).apply {
setContent {
- val payStatus by model.paymentManager.payStatus
- .asFlow()
- .collectAsState(initial = PayStatus.None)
+ val payStatus = payStatusFlow.collectAsStateLifecycleAware(initial = PayStatus.None)
TalerSurface {
PayTemplateComposable(
currencies = model.getCurrencies(),
- summary = summary,
- amountStatus = amountStatus,
- payStatus = payStatus,
- onCreateAmount = { text, currency ->
- model.createAmount(text, currency)
- },
- onSubmit = { createOrder(it) },
+ defaultSummary = defaultSummary,
+ amountStatus = amountFieldStatus,
+ payStatus = payStatus.value,
+ onCreateAmount = model::createAmount,
+ onSubmit = this@PayTemplateFragment::createOrder,
onError = { this@PayTemplateFragment.showError(it) },
)
}
@@ -90,7 +76,7 @@ class PayTemplateFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (uri.queryParameterNames?.isEmpty() == true) {
- createOrder(emptyMap())
+ createOrder(null, null)
}
model.paymentManager.payStatus.observe(viewLifecycleOwner) { payStatus ->
@@ -99,9 +85,9 @@ class PayTemplateFragment : Fragment() {
val navOptions = NavOptions.Builder()
.setPopUpTo(R.id.nav_main, true)
.build()
- findNavController()
- .navigate(R.id.action_global_promptPayment, null, navOptions)
+ findNavController().navigate(R.id.action_global_promptPayment, null, navOptions)
}
+
is PayStatus.Error -> {
if (model.devMode.value == true) {
showError(payStatus.error)
@@ -109,12 +95,29 @@ class PayTemplateFragment : Fragment() {
showError(R.string.payment_template_error, payStatus.error.userFacingMsg)
}
}
+
else -> {}
}
}
}
- private fun createOrder(params: Map<String, String>) {
- model.paymentManager.preparePayForTemplate(uriString, params)
+ private fun getAmountFieldStatus(defaultAmount: String?): AmountFieldStatus {
+ return if (defaultAmount == null) {
+ AmountFieldStatus.FixedAmount
+ } else if (defaultAmount.isBlank()) {
+ AmountFieldStatus.Default()
+ } else {
+ val parts = defaultAmount.split(":")
+ when (parts.size) {
+ 0 -> AmountFieldStatus.Default()
+ 1 -> AmountFieldStatus.Default(currency = parts[0])
+ 2 -> AmountFieldStatus.Default(parts[1], parts[0])
+ else -> AmountFieldStatus.Invalid
+ }
+ }
+ }
+
+ private fun createOrder(summary: String?, amount: Amount?) {
+ model.paymentManager.preparePayForTemplate(uriString, summary, amount)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt
new file mode 100644
index 0000000..1524faf
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt
@@ -0,0 +1,179 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2023 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.payment
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment.Companion.End
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import net.taler.common.Amount
+import net.taler.wallet.AmountResult
+import net.taler.wallet.R
+import net.taler.wallet.compose.AmountInputField
+import net.taler.wallet.compose.TalerSurface
+import net.taler.wallet.deposit.CurrencyDropdown
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun PayTemplateOrderComposable(
+ currencies: List<String>, // assumed to have size > 0
+ defaultSummary: String? = null,
+ amountStatus: AmountFieldStatus,
+ onCreateAmount: (String, String) -> AmountResult,
+ onError: (msgRes: Int) -> Unit,
+ onSubmit: (summary: String?, amount: Amount?) -> Unit,
+) {
+ val amountDefault = amountStatus as? AmountFieldStatus.Default
+
+ var summary by remember { mutableStateOf(defaultSummary) }
+ var currency by remember { mutableStateOf(amountDefault?.currency ?: currencies[0]) }
+ var amount by remember { mutableStateOf(amountDefault?.amountStr ?: "0") }
+
+ Column(horizontalAlignment = End) {
+ if (defaultSummary != null) OutlinedTextField(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth(),
+ value = summary ?: "",
+ isError = summary.isNullOrBlank(),
+ onValueChange = { summary = it },
+ singleLine = true,
+ label = { Text(stringResource(R.string.withdraw_manual_ready_subject)) },
+ )
+ if (amountDefault != null) AmountField(
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth(),
+ amount = amount,
+ currency = currency,
+ currencies = currencies,
+ fixedCurrency = (amountStatus as? AmountFieldStatus.Default)?.currency != null,
+ onAmountChosen = { a, c ->
+ amount = a
+ currency = c
+ },
+ )
+ Button(
+ modifier = Modifier.padding(16.dp),
+ enabled = defaultSummary == null || !summary.isNullOrBlank(),
+ onClick = {
+ when (val res = onCreateAmount(amount, currency)) {
+ is AmountResult.InsufficientBalance -> onError(R.string.payment_balance_insufficient)
+ is AmountResult.InvalidAmount -> onError(R.string.receive_amount_invalid)
+ is AmountResult.Success -> onSubmit(summary, res.amount)
+ }
+ },
+ ) {
+ Text(stringResource(R.string.payment_create_order))
+ }
+ }
+}
+
+@Composable
+private fun AmountField(
+ modifier: Modifier = Modifier,
+ currencies: List<String>,
+ fixedCurrency: Boolean,
+ amount: String,
+ currency: String,
+ onAmountChosen: (amount: String, currency: String) -> Unit,
+) {
+ Row(
+ modifier = modifier,
+ ) {
+ AmountInputField(
+ modifier = Modifier
+ .padding(end = 16.dp)
+ .weight(1f),
+ value = amount,
+ onValueChange = { onAmountChosen(it, currency) },
+ label = { Text(stringResource(R.string.send_amount)) }
+ )
+ CurrencyDropdown(
+ modifier = Modifier.weight(1f),
+ initialCurrency = currency,
+ currencies = currencies,
+ onCurrencyChanged = { onAmountChosen(amount, it) },
+ readOnly = fixedCurrency,
+ )
+ }
+}
+
+@Preview
+@Composable
+fun PayTemplateDefaultPreview() {
+ TalerSurface {
+ PayTemplateOrderComposable(
+ defaultSummary = "Donation",
+ amountStatus = AmountFieldStatus.Default("20", "ARS"),
+ currencies = listOf("KUDOS", "ARS"),
+ onCreateAmount = { text, currency ->
+ AmountResult.Success(amount = Amount.fromString(currency, text))
+ },
+ onSubmit = { _, _ -> },
+ onError = { },
+ )
+ }
+}
+
+@Preview
+@Composable
+fun PayTemplateFixedAmountPreview() {
+ TalerSurface {
+ PayTemplateOrderComposable(
+ defaultSummary = "default summary",
+ amountStatus = AmountFieldStatus.FixedAmount,
+ currencies = listOf("KUDOS", "ARS"),
+ onCreateAmount = { text, currency ->
+ AmountResult.Success(amount = Amount.fromString(currency, text))
+ },
+ onSubmit = { _, _ -> },
+ onError = { },
+ )
+ }
+}
+
+@Preview
+@Composable
+fun PayTemplateBlankSubjectPreview() {
+ TalerSurface {
+ PayTemplateOrderComposable(
+ defaultSummary = "",
+ amountStatus = AmountFieldStatus.FixedAmount,
+ currencies = listOf("KUDOS", "ARS"),
+ onCreateAmount = { text, currency ->
+ AmountResult.Success(amount = Amount.fromString(currency, text))
+ },
+ onSubmit = { _, _ -> },
+ onError = { },
+ )
+ }
+}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
index 627c05d..3a3069c 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -79,6 +79,7 @@ class PaymentManager(
response.contractTerms,
response.amountRaw
)
+
is AlreadyConfirmedResponse -> AlreadyPaid
}
}
@@ -103,22 +104,24 @@ class PaymentManager(
resetPayStatus()
}
- fun preparePayForTemplate(url: String, params: Map<String, String>) = scope.launch {
+ fun preparePayForTemplate(url: String, summary: String?, amount: Amount?) = scope.launch {
mPayStatus.value = PayStatus.Loading
api.request("preparePayForTemplate", PreparePayResponse.serializer()) {
put("talerPayTemplateUri", url)
put("templateParams", JSONObject().apply {
- params.forEach { put(it.key, it.value) }
+ summary?.let { put("summary", it) }
+ amount?.let { put("amount", it.toJSONString()) }
})
}.onError {
handleError("preparePayForTemplate", it)
- }.onSuccess { response ->
+ }.onSuccess { response ->
mPayStatus.value = when (response) {
is PaymentPossibleResponse -> response.toPayStatusPrepared()
is InsufficientBalanceResponse -> InsufficientBalance(
contractTerms = response.contractTerms,
amountRaw = response.amountRaw,
)
+
is AlreadyConfirmedResponse -> AlreadyPaid
}
}