commit 0c817c3ab94cd068af289c1f9f21718c4c23b30d
parent 993a2b5880d2ce79e5c12fe049afb69234120755
Author: Marc Stibane <marc@taler.net>
Date: Fri, 14 Aug 2026 22:00:24 +0200
fixes for currency conversion (e.g. NetzBon)
Diffstat:
3 files changed, 64 insertions(+), 39 deletions(-)
diff --git a/TalerWallet1/Views/Transactions/KYCauth.swift b/TalerWallet1/Views/Transactions/KYCauth.swift
@@ -9,25 +9,31 @@ import SwiftUI
import SymLog
import taler_swift
+struct KycData {
+ let kycAmount: Amount
+ let debitIBAN: String
+}
// pretty much the same as ManualDetailsV
struct KYCauth: View {
let stack: CallStack
let common: TransactionCommon
var body: some View {
+ let kycErr = "KYC Error"
if let info = common.kycAuthTransferInfo {
let debitPayTo = PayTo(info.debitPaytoUri)
- let accountDetails = info.transferOptionsExt
- if let validDetails = accountDetails.validDetails {
- WireTransferView(stack: stack.push(),
- title: String(localized: "You need to prove having control over the bank account for the deposit.", comment: ""),
- isKYC: true,
- reservePub: info.accountPub,
- amountRaw: info.amount,
- amountEffective: nil,
- debitIBAN: debitPayTo.iban,
- validDetails: validDetails)
- } else { ErrorView(stack.push(), title: "KYC Error", message: "No valid details") }
- } else { ErrorView(stack.push(), title: "KYC Error", message: "No KYC infos") }
+ if let debitIban = debitPayTo.iban {
+ let kycData = KycData(kycAmount: info.amount, debitIBAN: debitIban)
+ let accountDetails = info.transferOptionsExt
+ if let validDetails = accountDetails.validDetails {
+ WireTransferView(stack: stack.push(),
+ title: String(localized: "You need to prove having control over the bank account for the deposit.", comment: ""),
+ reservePub: info.accountPub,
+ obtainStr: nil,
+ kycData: kycData,
+ validDetails: validDetails)
+ } else { ErrorView(stack.push(), title: kycErr, message: "No valid details") }
+ } else { ErrorView(stack.push(), title: kycErr, message: "No IBAN") }
+ } else { ErrorView(stack.push(), title: kycErr, message: "No KYC infos") }
} // body
}
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
@@ -15,27 +15,29 @@ struct ManualDetailsV: View {
let details: WithdrawalDetails
var body: some View {
- if let validDetails = details.exchangeCreditAccountDetails?.validDetails {
- WireTransferView(stack: stack.push(),
- title: String(localized: "The payment service is waiting for your wire-transfer.", comment: ""),
- isKYC: false,
- reservePub: details.reservePub,
- amountRaw: common.amountRaw,
- amountEffective: common.amountEffective, // only for withdrawal
- debitIBAN: nil, // only for deposit auth
- validDetails: validDetails)
+ if let accountDetails = details.exchangeCreditAccountDetails,
+ let validDetails = accountDetails.validDetails {
+ if let scope: ScopeInfo = common.scopes.first {
+ let obtainStr = common.amountEffective.formatted(scope , isNegative: false)
+ WireTransferView(stack: stack.push(),
+ title: String(localized: "The payment service is waiting for your wire-transfer.", comment: ""),
+ reservePub: details.reservePub,
+ obtainStr: obtainStr, // only for withdrawal
+ kycData: nil, // only for deposit auth
+ validDetails: validDetails)
+ } else { ErrorView(stack.push(), title: "Payment Error", message: "No scope") }
} else { ErrorView(stack.push(), title: "Payment Error", message: "No valid details") }
} // body
}
// MARK: -
+// called by ManualDetailsV (amount to transfer is chosen by user),
+// and by KYCauth (user chose the amount to deposit - but first they must transfer kycAmount to the Exchange to prove having control over the account)
struct WireTransferView: View {
let stack: CallStack
let title: String
- let isKYC: Bool
let reservePub: String
- let amountRaw: Amount
- let amountEffective: Amount? // only for withdrawal
- let debitIBAN: String? // only for deposit auth
+ let obtainStr: (String, String)? // only for withdrawal
+ let kycData: KycData? // only for deposit auth
let validDetails: AccountDetailsArray
@EnvironmentObject private var model: WalletModel
@@ -67,14 +69,20 @@ struct WireTransferView: View {
var body: some View {
let validCount = validDetails.count // guaranteed to be > 0
- let account = validDetails[validIndex]
+ // validDetails can shrink (e.g. when a conversion rate lookup starts failing)
+ // while validIndex still points to an account which is gone by now
+ let account = validDetails[min(validIndex, validCount - 1)]
let json = account.toJSON()
- if account.transferAmount != nil {
- if let transferOptions = account.transferOptions, let firstOption = transferOptions.first {
+ // transferAmount is the amount the user must wire - for an account which needs
+ // currency conversion that is in the external currency, and thus different from amountRaw
+ if kycData != nil || account.transferAmount != nil {
+ if let transferOptions = account.transferOptions,
+ // an option of type "uri" has neither paytoUri nor qrCodes - use one which has
+ let firstOption = transferOptions.first(where: { $0.paytoUri != nil }) ?? transferOptions.first {
let specs = account.currencySpecification
- let amountStr = amountRaw.formatted(specs: specs, isNegative: false)
- let amountValue = amountRaw.valueStr
- let obtainStr = amountEffective?.formatted(specs: specs, isNegative: false) // or nil
+ let plannedAmount = kycData?.kycAmount ?? account.transferAmount!
+ let amountStr = plannedAmount.formatted(specs: specs, isNegative: false)
+ let amountValue = plannedAmount.valueStr
// let _ = print(amountStr, " | ", obtainStr)
if !minimalistic {
Text(title)
@@ -114,8 +122,8 @@ struct WireTransferView: View {
restrictions: account.creditRestrictions,
amountValue: amountValue,
amountStr: amountStr,
- obtainStr: obtainStr, // only for withdrawal
- debitIBAN: debitIBAN // only for deposit auth
+ obtainStr: obtainStr, // only for withdrawal
+ debitIBAN: kycData?.debitIBAN // only for deposit auth
)
NavigationLink(destination: wireDetails) {
Text(minimalistic ? "Instructions"
@@ -126,7 +134,7 @@ struct WireTransferView: View {
Group {
if countA > 1 || countB > 1 {
qrCodesRow(transferOptions)
- } else if countA > 0 { // show the single QR directly
+ } else if countB > 0 { // show the single QR directly
Text("If your banking software runs on another device, you can scan this QR code:")
.listRowSeparator(.hidden)
QRcodeCopyShare(option: firstOption)
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsWireV.swift b/TalerWallet1/Views/Transactions/ManualDetailsWireV.swift
@@ -11,8 +11,8 @@ import taler_swift
struct TransferRestrictionsV: View {
let amountStr: (String, String)
- let obtainStr: (String, String)?
- let debitIBAN: String?
+ let obtainStr: (String, String)? // only for withdrawal
+ let debitIBAN: String? // only for deposit auth
let restrictions: [AccountRestriction]?
@AppStorage("minimalistic") var minimalistic: Bool = false
@@ -38,14 +38,14 @@ struct TransferRestrictionsV: View {
var body: some View {
VStack(alignment: .leading) {
- if let obtainStr {
+ if let obtainStr { // withdrawal
Text(minimalistic ? transferMini(amountStr.0)
: transferMaxi(amountStr.0, obtainStr.0))
.accessibilityLabel(minimalistic ? transferMini(amountStr.1)
: transferMaxi(amountStr.1, obtainStr.1))
.talerFont(.body)
.multilineTextAlignment(.leading)
- } else if let debitIBAN {
+ } else if let debitIBAN { // deposit auth
Text(minimalistic ? authMini(amountStr.0, debitIBAN)
: authMaxi(amountStr.0, debitIBAN))
.accessibilityLabel(minimalistic ? authMini(amountStr.1, debitIBAN)
@@ -280,6 +280,18 @@ struct ManualDetailsWireV: View {
: String(localized: "Finish the wire transfer of \(amountNBS) in your banking app or website, then this withdrawal will proceed automatically.") + "\n" + bePatient
}
+ /// The subject of the wire transfer
+ private var cryptoString: String {
+ // chQRr only consists of digits - no prefix possible
+ if let chQRr = payto.chQRr, !chQRr.isEmpty {
+ return chQRr
+ }
+// if let messageStr = payto.messageStr, !messageStr.isEmpty {
+// return messageStr
+// }
+ return debitIBAN != nil ? "KYC:" + reservePub : reservePub
+ }
+
// @ViewBuilder func cyclosCode() -> some View {
// HStack {
// VStack(alignment: .leading) {
@@ -309,7 +321,6 @@ struct ManualDetailsWireV: View {
var body: some View {
if let receiverStr = payto.receiver {
List {
- let cryptoString = debitIBAN == nil ? reservePub : "KYC:" + reservePub
let warningIcon = Image(systemName: WARNING)
let note = Text("**Note: Don't forget to copy and paste the code in Step 2.**")
let manda = debitIBAN == nil ? String(localized: "This is mandatory, otherwise your money will not arrive in this wallet.")