summaryrefslogtreecommitdiff
path: root/wallet/src/main/java/net/taler/wallet/payment
diff options
context:
space:
mode:
Diffstat (limited to 'wallet/src/main/java/net/taler/wallet/payment')
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt61
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateDetails.kt114
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt43
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt110
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt39
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt1
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt6
7 files changed, 267 insertions, 107 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 ffa4875..d744183 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
@@ -34,42 +34,37 @@ import net.taler.wallet.R
import net.taler.wallet.compose.LoadingScreen
import net.taler.wallet.compose.TalerSurface
-sealed class AmountFieldStatus {
- object FixedAmount : AmountFieldStatus()
- class Default(
- val amountStr: String? = null,
- val currency: String? = null,
- ) : AmountFieldStatus()
-
- object Invalid : AmountFieldStatus()
-}
-
@Composable
fun PayTemplateComposable(
- defaultSummary: String?,
- amountStatus: AmountFieldStatus,
currencies: List<String>,
payStatus: PayStatus,
onCreateAmount: (String, String) -> AmountResult,
- onSubmit: (summary: String?, amount: Amount?) -> Unit,
+ onSubmit: (params: TemplateParams) -> 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 (currencies.isEmpty()) {
+ if (currencies.isEmpty()) {
PayTemplateError(stringResource(R.string.payment_balance_insufficient))
} else when (val p = payStatus) {
- is PayStatus.None -> PayTemplateOrderComposable(
- currencies = currencies,
- defaultSummary = defaultSummary,
- amountStatus = amountStatus,
- onCreateAmount = onCreateAmount,
- onError = onError,
- onSubmit = onSubmit,
- )
+ is PayStatus.Checked -> {
+ val usableCurrencies = currencies
+ .intersect(p.supportedCurrencies.toSet())
+ .toList()
+ if (usableCurrencies.isEmpty()) {
+ // If user doesn't have any supported currency, they can't pay either
+ PayTemplateError(stringResource(R.string.payment_balance_insufficient))
+ } else {
+ PayTemplateOrderComposable(
+ usableCurrencies = usableCurrencies,
+ templateDetails = p.details,
+ onCreateAmount = onCreateAmount,
+ onError = onError,
+ onSubmit = onSubmit,
+ )
+ }
+ }
- is PayStatus.Loading -> PayTemplateLoading()
+ is PayStatus.None, is PayStatus.Loading -> PayTemplateLoading()
is PayStatus.AlreadyPaid -> PayTemplateError(stringResource(R.string.payment_already_paid))
is PayStatus.InsufficientBalance -> PayTemplateError(stringResource(R.string.payment_balance_insufficient))
is PayStatus.Pending -> {
@@ -109,14 +104,12 @@ fun PayTemplateLoading() {
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 = { _, _ -> },
+ onSubmit = { _ -> },
onError = { _ -> },
)
}
@@ -127,8 +120,6 @@ fun PayTemplateLoadingPreview() {
fun PayTemplateInsufficientBalancePreview() {
TalerSurface {
PayTemplateComposable(
- defaultSummary = "Donation",
- amountStatus = AmountFieldStatus.Default("20", "ARS"),
payStatus = PayStatus.InsufficientBalance(
ContractTerms(
"test",
@@ -140,7 +131,7 @@ fun PayTemplateInsufficientBalancePreview() {
onCreateAmount = { text, currency ->
AmountResult.Success(amount = Amount.fromString(currency, text))
},
- onSubmit = { _, _ -> },
+ onSubmit = { _ -> },
onError = { _ -> },
)
}
@@ -151,14 +142,12 @@ fun PayTemplateInsufficientBalancePreview() {
fun PayTemplateAlreadyPaidPreview() {
TalerSurface {
PayTemplateComposable(
- defaultSummary = "Donation",
- amountStatus = AmountFieldStatus.Default("20", "ARS"),
payStatus = PayStatus.AlreadyPaid(transactionId = "transactionId"),
currencies = listOf("KUDOS", "ARS"),
onCreateAmount = { text, currency ->
AmountResult.Success(amount = Amount.fromString(currency, text))
},
- onSubmit = { _, _ -> },
+ onSubmit = { _ -> },
onError = { _ -> },
)
}
@@ -170,14 +159,12 @@ fun PayTemplateAlreadyPaidPreview() {
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 = { _, _ -> },
+ onSubmit = { _ -> },
onError = { _ -> },
)
}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateDetails.kt b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateDetails.kt
new file mode 100644
index 0000000..f292e20
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateDetails.kt
@@ -0,0 +1,114 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2024 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 kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import net.taler.common.Amount
+import net.taler.common.RelativeTime
+
+@Serializable
+data class TemplateContractDetails(
+ /**
+ * Human-readable summary for the template.
+ */
+ val summary: String? = null,
+
+ /**
+ * Required currency for payments to the template. The user may specify
+ * any amount, but it must be in this currency. This parameter is
+ * optional and should not be present if "amount" is given.
+ */
+ val currency: String? = null,
+
+ /**
+ * The price is imposed by the merchant and cannot be changed by the
+ * customer. This parameter is optional.
+ */
+ val amount: Amount? = null,
+
+ /**
+ * Minimum age buyer must have (in years). Default is 0.
+ */
+ @SerialName("minimum_age")
+ val minimumAge: Int,
+
+ /**
+ * The time the customer need to pay before his order will be deleted. It
+ * is deleted if the customer did not pay and if the duration is over.
+ */
+ @SerialName("pay_duration")
+ val payDuration: RelativeTime,
+)
+
+@Serializable
+data class TemplateContractDetailsDefaults(
+ val summary: String? = null,
+ val currency: String? = null,
+ val amount: Amount? = null,
+ @SerialName("minimum_age")
+ val minimumAge: Int? = null,
+)
+
+@Serializable
+class WalletTemplateDetails(
+ /**
+ * Hard-coded information about the contract terms for this template.
+ */
+ @SerialName("template_contract")
+ val templateContract: TemplateContractDetails,
+
+ /**
+ * Key-value pairs matching a subset of the fields from template_contract
+ * that are user-editable defaults for this template.
+ */
+ @SerialName("editable_defaults")
+ val editableDefaults: TemplateContractDetailsDefaults? = null,
+) {
+ val defaultSummary get() = editableDefaults?.summary
+ ?: templateContract.summary
+
+ val defaultAmount get() = editableDefaults?.amount
+ ?: templateContract.amount
+
+ val defaultCurrency get() = editableDefaults?.currency
+ ?: templateContract.currency
+
+ fun isSummaryEditable() = templateContract.summary == null
+
+ fun isAmountEditable() = templateContract.amount == null
+
+ fun isCurrencyEditable(usableCurrencies: List<String>) = isAmountEditable()
+ && templateContract.currency == null
+ && usableCurrencies.size > 1
+
+ fun isTemplateEditable(usableCurrencies: List<String>) = isSummaryEditable()
+ || isAmountEditable()
+ || isCurrencyEditable(usableCurrencies)
+
+ // NOTE: it is important to nullify non-editable values!
+ fun toTemplateParams() = TemplateParams(
+ amount = if(isAmountEditable()) templateContract.amount else null,
+ summary = if(isSummaryEditable()) templateContract.summary else null,
+ )
+}
+
+@Serializable
+data class TemplateParams(
+ val amount: Amount? = null,
+ val summary: String? = null,
+)
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 4eb2c11..51c0bc0 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
@@ -26,7 +26,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.asFlow
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
@@ -39,6 +38,7 @@ class PayTemplateFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private lateinit var uriString: String
private lateinit var uri: Uri
+ private val currencies by lazy { model.getCurrencies() }
override fun onCreateView(
inflater: LayoutInflater,
@@ -48,10 +48,6 @@ class PayTemplateFragment : Fragment() {
uriString = arguments?.getString("uri") ?: error("no amount passed")
uri = Uri.parse(uriString)
- val defaultSummary = uri.getQueryParameter("summary")
- val defaultAmount = uri.getQueryParameter("amount")
- val amountFieldStatus = getAmountFieldStatus(defaultAmount)
-
val payStatusFlow = model.paymentManager.payStatus.asFlow()
return ComposeView(requireContext()).apply {
@@ -59,9 +55,7 @@ class PayTemplateFragment : Fragment() {
val payStatus = payStatusFlow.collectAsStateLifecycleAware(initial = PayStatus.None)
TalerSurface {
PayTemplateComposable(
- currencies = model.getCurrencies(),
- defaultSummary = defaultSummary,
- amountStatus = amountFieldStatus,
+ currencies = currencies,
payStatus = payStatus.value,
onCreateAmount = model::createAmount,
onSubmit = this@PayTemplateFragment::createOrder,
@@ -74,9 +68,7 @@ class PayTemplateFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- if (uri.queryParameterNames?.isEmpty() == true) {
- createOrder(null, null)
- }
+ checkTemplate()
model.paymentManager.payStatus.observe(viewLifecycleOwner) { payStatus ->
when (payStatus) {
@@ -88,28 +80,25 @@ class PayTemplateFragment : Fragment() {
showError(payStatus.error)
}
+ is PayStatus.Checked -> {
+ val usableCurrencies = currencies
+ .intersect(payStatus.supportedCurrencies.toSet())
+ .toList()
+ if (!payStatus.details.isTemplateEditable(usableCurrencies)) {
+ createOrder(payStatus.details.toTemplateParams())
+ }
+ }
+
else -> {}
}
}
}
- 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 checkTemplate() {
+ model.paymentManager.checkPayForTemplate(uriString)
}
- private fun createOrder(summary: String?, amount: Amount?) {
- model.paymentManager.preparePayForTemplate(uriString, summary, amount)
+ private fun createOrder(params: TemplateParams) {
+ model.paymentManager.preparePayForTemplate(uriString, params)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt
index d6131c7..2febfbb 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateOrderComposable.kt
@@ -24,84 +24,118 @@ import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
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.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
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.RelativeTime
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(ExperimentalComposeUiApi::class)
@Composable
fun PayTemplateOrderComposable(
- currencies: List<String>, // assumed to have size > 0
- defaultSummary: String? = null,
- amountStatus: AmountFieldStatus,
+ usableCurrencies: List<String>, // non-empty intersection between the stored currencies and the ones supported by the merchant
+ templateDetails: WalletTemplateDetails,
onCreateAmount: (String, String) -> AmountResult,
onError: (msgRes: Int) -> Unit,
- onSubmit: (summary: String?, amount: Amount?) -> Unit,
+ onSubmit: (params: TemplateParams) -> Unit,
) {
- val amountDefault = amountStatus as? AmountFieldStatus.Default
+ val defaultSummary = templateDetails.defaultSummary
+ val defaultAmount = templateDetails.defaultAmount
+ val defaultCurrency = templateDetails.defaultCurrency
- var summary by remember { mutableStateOf(defaultSummary) }
- var currency by remember { mutableStateOf(amountDefault?.currency ?: currencies[0]) }
- var amount by remember { mutableStateOf(amountDefault?.amountStr ?: "0") }
+ val summaryFocusRequester = remember { FocusRequester() }
+ val keyboardController = LocalSoftwareKeyboardController.current
+
+ var summary by remember { mutableStateOf(defaultSummary ?: "") }
+ var currency by remember { mutableStateOf(defaultCurrency ?: usableCurrencies[0]) }
+ var amount by remember { mutableStateOf(defaultAmount?.amountStr ?: "0") }
Column(horizontalAlignment = End) {
- if (defaultSummary != null) OutlinedTextField(
+ OutlinedTextField(
modifier = Modifier
.padding(horizontal = 16.dp)
- .fillMaxWidth(),
- value = summary ?: "",
- isError = summary.isNullOrBlank(),
+ .fillMaxWidth()
+ .focusRequester(summaryFocusRequester)
+ .onFocusChanged {
+ if (it.isFocused) {
+ keyboardController?.show()
+ }
+ },
+ value = summary,
+ isError = templateDetails.isSummaryEditable() && summary.isBlank(),
onValueChange = { summary = it },
singleLine = true,
+ readOnly = !templateDetails.isSummaryEditable(),
label = { Text(stringResource(R.string.withdraw_manual_ready_subject)) },
)
- if (amountDefault != null) AmountField(
+
+ AmountField(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
amount = amount,
currency = currency,
- currencies = currencies,
- fixedCurrency = (amountStatus as? AmountFieldStatus.Default)?.currency != null,
+ currencies = usableCurrencies,
+ readOnlyCurrency = !templateDetails.isCurrencyEditable(usableCurrencies),
+ readOnlyAmount = !templateDetails.isAmountEditable(),
onAmountChosen = { a, c ->
amount = a
currency = c
},
)
+
Button(
modifier = Modifier.padding(16.dp),
- enabled = defaultSummary == null || !summary.isNullOrBlank(),
+ enabled = !templateDetails.isSummaryEditable() || summary.isNotBlank(),
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)
+ is AmountResult.InvalidAmount -> onError(R.string.amount_invalid)
+ // NOTE: it is important to nullify non-editable values!
+ is AmountResult.Success -> onSubmit(TemplateParams(
+ summary = if (templateDetails.isSummaryEditable()) summary else null,
+ amount = if(templateDetails.isAmountEditable()) res.amount else null,
+ ))
}
},
) {
Text(stringResource(R.string.payment_create_order))
}
}
+
+ LaunchedEffect(Unit) {
+ if (templateDetails.isSummaryEditable()
+ && templateDetails.defaultSummary == null) {
+ summaryFocusRequester.requestFocus()
+ }
+ }
}
@Composable
private fun AmountField(
modifier: Modifier = Modifier,
currencies: List<String>,
- fixedCurrency: Boolean,
amount: String,
currency: String,
+ readOnlyAmount: Boolean = true,
+ readOnlyCurrency: Boolean = true,
onAmountChosen: (amount: String, currency: String) -> Unit,
) {
Row(
@@ -113,30 +147,42 @@ private fun AmountField(
.weight(1f),
value = amount,
onValueChange = { onAmountChosen(it, currency) },
- label = { Text(stringResource(R.string.send_amount)) }
+ label = { Text(stringResource(R.string.amount_send)) },
+ readOnly = readOnlyAmount,
)
+
CurrencyDropdown(
modifier = Modifier.weight(1f),
initialCurrency = currency,
currencies = currencies,
onCurrencyChanged = { onAmountChosen(amount, it) },
- readOnly = fixedCurrency,
+ readOnly = readOnlyCurrency,
)
}
}
+val defaultTemplateDetails = WalletTemplateDetails(
+ templateContract = TemplateContractDetails(
+ minimumAge = 18,
+ payDuration = RelativeTime.forever(),
+ ),
+ editableDefaults = TemplateContractDetailsDefaults(
+ summary = "Donation",
+ amount = Amount.fromJSONString("KUDOS:10.0"),
+ ),
+)
+
@Preview
@Composable
fun PayTemplateDefaultPreview() {
TalerSurface {
PayTemplateOrderComposable(
- defaultSummary = "Donation",
- amountStatus = AmountFieldStatus.Default("20", "ARS"),
- currencies = listOf("KUDOS", "ARS"),
+ templateDetails = defaultTemplateDetails,
+ usableCurrencies = listOf("KUDOS", "ARS"),
onCreateAmount = { text, currency ->
AmountResult.Success(amount = Amount.fromString(currency, text))
},
- onSubmit = { _, _ -> },
+ onSubmit = { _ -> },
onError = { },
)
}
@@ -147,13 +193,12 @@ fun PayTemplateDefaultPreview() {
fun PayTemplateFixedAmountPreview() {
TalerSurface {
PayTemplateOrderComposable(
- defaultSummary = "default summary",
- amountStatus = AmountFieldStatus.FixedAmount,
- currencies = listOf("KUDOS", "ARS"),
+ templateDetails = defaultTemplateDetails,
+ usableCurrencies = listOf("KUDOS", "ARS"),
onCreateAmount = { text, currency ->
AmountResult.Success(amount = Amount.fromString(currency, text))
},
- onSubmit = { _, _ -> },
+ onSubmit = { _ -> },
onError = { },
)
}
@@ -164,13 +209,12 @@ fun PayTemplateFixedAmountPreview() {
fun PayTemplateBlankSubjectPreview() {
TalerSurface {
PayTemplateOrderComposable(
- defaultSummary = "",
- amountStatus = AmountFieldStatus.FixedAmount,
- currencies = listOf("KUDOS", "ARS"),
+ templateDetails = defaultTemplateDetails,
+ usableCurrencies = listOf("KUDOS", "ARS"),
onCreateAmount = { text, currency ->
AmountResult.Success(amount = Amount.fromString(currency, text))
},
- onSubmit = { _, _ -> },
+ 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 35cd9e6..647c98c 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -22,9 +22,12 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.encodeToString
import net.taler.common.Amount
import net.taler.common.ContractTerms
import net.taler.wallet.TAG
+import net.taler.wallet.backend.BackendManager
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.payment.PayStatus.AlreadyPaid
@@ -37,8 +40,8 @@ import org.json.JSONObject
val REGEX_PRODUCT_IMAGE = Regex("^data:image/(jpeg|png);base64,([A-Za-z0-9+/=]+)$")
sealed class PayStatus {
- object None : PayStatus()
- object Loading : PayStatus()
+ data object None : PayStatus()
+ data object Loading : PayStatus()
data class Prepared(
val contractTerms: ContractTerms,
val transactionId: String,
@@ -46,6 +49,11 @@ sealed class PayStatus {
val amountEffective: Amount,
) : PayStatus()
+ data class Checked(
+ val details: WalletTemplateDetails,
+ val supportedCurrencies: List<String>,
+ ) : PayStatus()
+
data class InsufficientBalance(
val contractTerms: ContractTerms,
val amountRaw: Amount,
@@ -65,6 +73,12 @@ sealed class PayStatus {
) : PayStatus()
}
+@Serializable
+data class CheckPayTemplateResponse(
+ val templateDetails: WalletTemplateDetails,
+ val supportedCurrencies: List<String>,
+)
+
class PaymentManager(
private val api: WalletBackendApi,
private val scope: CoroutineScope,
@@ -113,14 +127,25 @@ class PaymentManager(
}
}
- fun preparePayForTemplate(url: String, summary: String?, amount: Amount?) = scope.launch {
+ fun checkPayForTemplate(url: String) = scope.launch {
+ mPayStatus.value = PayStatus.Loading
+ api.request("checkPayForTemplate", CheckPayTemplateResponse.serializer()) {
+ put("talerPayTemplateUri", url)
+ }.onError {
+ handleError("checkPayForTemplate", it)
+ }.onSuccess { response ->
+ mPayStatus.value = PayStatus.Checked(
+ details = response.templateDetails,
+ supportedCurrencies = response.supportedCurrencies,
+ )
+ }
+ }
+
+ fun preparePayForTemplate(url: String, params: TemplateParams) = scope.launch {
mPayStatus.value = PayStatus.Loading
api.request("preparePayForTemplate", PreparePayResponse.serializer()) {
put("talerPayTemplateUri", url)
- put("templateParams", JSONObject().apply {
- summary?.let { put("summary", it) }
- amount?.let { put("amount", it.toJSONString()) }
- })
+ put("templateParams", JSONObject(BackendManager.json.encodeToString(params)))
}.onError {
handleError("preparePayForTemplate", it)
}.onSuccess { response ->
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
index 31c26a0..1995f9d 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -98,6 +98,7 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener {
private fun onPaymentStatusChanged(payStatus: PayStatus?) {
when (payStatus) {
null -> {}
+ is PayStatus.Checked -> {} // does not apply, only used for templates
is PayStatus.Prepared -> {
showLoading(false)
val fees = payStatus.amountEffective - payStatus.amountRaw
diff --git a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
index 0f6d661..beb37d9 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
@@ -82,10 +82,10 @@ fun TransactionPaymentComposable(
amountType = AmountType.Neutral,
)
- val fee = t.amountEffective - t.amountRaw
- if (!fee.isZero()) {
+ if (t.amountEffective > t.amountRaw) {
+ val fee = t.amountEffective - t.amountRaw
TransactionAmountComposable(
- label = stringResource(id = R.string.withdraw_fees),
+ label = stringResource(id = R.string.amount_fee),
amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)