aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-06-10 15:25:51 -0300
committerSebastian <sebasjm@gmail.com>2024-06-10 15:46:23 -0300
commit689d58a5c79e98fec733c3cda31146620d8bde1d (patch)
tree6f37b175ffde2c8460ea35cb13d2f5496412c600 /packages
parent034d6bd1aa4fe155accc889831f711bc985cd280 (diff)
downloadwallet-core-689d58a5c79e98fec733c3cda31146620d8bde1d.tar.xz
fix #8886
Diffstat (limited to 'packages')
-rw-r--r--packages/bank-ui/src/hooks/preferences.ts2
-rw-r--r--packages/bank-ui/src/pages/OperationState/state.ts14
-rw-r--r--packages/bank-ui/src/pages/PaytoWireTransferForm.tsx73
-rw-r--r--packages/bank-ui/src/pages/WalletWithdrawForm.tsx13
-rw-r--r--packages/bank-ui/src/settings.json2
-rw-r--r--packages/taler-util/src/http-client/bank-integration.ts2
-rw-r--r--packages/taler-util/src/http-client/types.ts64
-rw-r--r--packages/taler-util/src/wallet-types.ts13
-rw-r--r--packages/taler-wallet-core/src/dbless.ts2
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts69
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/index.ts3
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/state.ts128
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/test.ts24
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx7
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts2
15 files changed, 320 insertions, 98 deletions
diff --git a/packages/bank-ui/src/hooks/preferences.ts b/packages/bank-ui/src/hooks/preferences.ts
index bb3dcb153..9c60456c7 100644
--- a/packages/bank-ui/src/hooks/preferences.ts
+++ b/packages/bank-ui/src/hooks/preferences.ts
@@ -104,7 +104,7 @@ export function getLabelForPreferences(
case "showInstallWallet":
return i18n.str`Show install wallet first`;
case "fastWithdrawal":
- return i18n.str`Use fast withdrawal form`;
+ return i18n.str`Set the withdrawal amount in the wallet`;
case "showDebugInfo":
return i18n.str`Show debug info`;
}
diff --git a/packages/bank-ui/src/pages/OperationState/state.ts b/packages/bank-ui/src/pages/OperationState/state.ts
index 19c097d18..32d4fea7a 100644
--- a/packages/bank-ui/src/pages/OperationState/state.ts
+++ b/packages/bank-ui/src/pages/OperationState/state.ts
@@ -18,6 +18,7 @@ import {
Amounts,
HttpStatusCode,
TalerCoreBankErrorsByMethod,
+ TalerCorebankApi,
TalerError,
assertUnreachable,
parsePaytoUri,
@@ -58,9 +59,16 @@ export function useComponentState({
// FIXME: if amount is not enough use balance
const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`);
if (!creds) return;
- const resp = await bank.createWithdrawal(creds, {
- amount: Amounts.stringify(parsedAmount),
- });
+ const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest =
+ settings.fastWithdrawal
+ ? {
+ suggested_amount: Amounts.stringify(parsedAmount),
+ }
+ : {
+ amount: Amounts.stringify(parsedAmount),
+ };
+
+ const resp = await bank.createWithdrawal(creds, params);
if (resp.type === "fail") {
setFailure(resp);
return;
diff --git a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
index 3bf891504..bb408e497 100644
--- a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -111,6 +111,17 @@ export function PaytoWireTransferForm({
? ("x-taler-bank" as const)
: ("iban" as const);
+ const wireFee =
+ config.wire_transfer_fees === undefined
+ ? Amounts.zeroOfCurrency(config.currency)
+ // Amounts.parseOrThrow("YEIN:2.5")
+ : Amounts.parseOrThrow(config.wire_transfer_fees);
+
+ const amountAfterFee =
+ !parsedAmount || Amounts.cmp(parsedAmount, wireFee) < 1
+ ? undefined
+ : Amounts.sub(parsedAmount, wireFee).amount;
+
const errorsWire = undefinedIfEmpty({
account: !account
? i18n.str`Required`
@@ -124,7 +135,7 @@ export function PaytoWireTransferForm({
? i18n.str`Required`
: !parsedAmount
? i18n.str`Not valid`
- : validateAmount(parsedAmount, limit, i18n),
+ : validateAmount(parsedAmount, limit, wireFee, i18n),
});
const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput);
@@ -134,7 +145,7 @@ export function PaytoWireTransferForm({
? i18n.str`Required`
: !parsed
? i18n.str`Does not follow the pattern`
- : validateRawPayto(parsed, limit, url.host, i18n, paytoType),
+ : validateRawPayto(parsed, limit, wireFee, url.host, i18n, paytoType),
});
async function doSend() {
@@ -479,9 +490,9 @@ export function PaytoWireTransferForm({
e.preventDefault();
}}
>
- <div class="p-4 sm:p-8">
+ <div class="m-4">
{!isRawPayto ? (
- <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 ">
{(() => {
switch (paytoType) {
case "x-taler-bank": {
@@ -623,6 +634,53 @@ export function PaytoWireTransferForm({
</div>
)}
</div>
+ <div class="px-4 my-4">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <div class="sm:col-span-6">
+ <dl class="mt-4 space-y-4">
+ {Amounts.isZero(wireFee) ? undefined : (
+ <Fragment>
+ <div class="flex items-center justify-between ">
+ <dt class="flex items-center text-sm text-gray-600">
+ <span>
+ <i18n.Translate>Fee</i18n.Translate>
+ </span>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ <RenderAmount
+ value={wireFee}
+ negative
+ withColor
+ spec={config.currency_specification}
+ />
+ </dd>
+ </div>
+ </Fragment>
+ )}{" "}
+ {!parsedAmount || !amountAfterFee ? undefined : (
+ <Fragment>
+ <div class="flex items-center justify-between border-t-2 afu pt-4">
+ <dt class="flex items-center text-sm text-gray-600">
+ <span>
+ <i18n.Translate>
+ Recipient will receive
+ </i18n.Translate>
+ </span>
+ </dt>
+ <dd class="text-sm text-gray-900">
+ <RenderAmount
+ value={amountAfterFee}
+ withColor
+ spec={config.currency_specification}
+ />
+ </dd>
+ </div>
+ </Fragment>
+ )}
+ </dl>
+ </div>
+ </div>
+ </div>
<div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
{routeCancel ? (
<a
@@ -768,6 +826,7 @@ export function RenderAmount({
function validateRawPayto(
parsed: PaytoUri,
limit: AmountJson,
+ fee: AmountJson,
host: string,
i18n: InternationalizationAPI,
type: "iban" | "x-taler-bank",
@@ -811,7 +870,7 @@ function validateRawPayto(
if (!amount) {
return i18n.str`The "amount" parameter is not valid`;
}
- result = validateAmount(amount, limit, i18n);
+ result = validateAmount(amount, limit, fee, i18n);
if (result) return result;
if (!parsed.params.message) {
@@ -827,6 +886,7 @@ function validateRawPayto(
function validateAmount(
amount: AmountJson,
limit: AmountJson,
+ fee: AmountJson,
i18n: InternationalizationAPI,
): TranslatedString | undefined {
if (amount.currency !== limit.currency) {
@@ -835,6 +895,9 @@ function validateAmount(
if (Amounts.isZero(amount)) {
return i18n.str`Can't transfer zero amount`;
}
+ if (Amounts.cmp(amount, fee) < 1) {
+ return i18n.str`Should be higher than fees`;
+ }
if (Amounts.cmp(limit, amount) === -1) {
return i18n.str`Balance is not enough`;
}
diff --git a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx
index a9c652643..39dea018f 100644
--- a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx
@@ -19,6 +19,7 @@ import {
AmountJson,
Amounts,
HttpStatusCode,
+ TalerCorebankApi,
TranslatedString,
assertUnreachable,
parseWithdrawUri,
@@ -141,9 +142,15 @@ function OldWithdrawalForm({
async function doStart() {
if (!parsedAmount || !creds) return;
await handleError(async () => {
- const resp = await api.createWithdrawal(creds, {
- amount: Amounts.stringify(parsedAmount),
- });
+ const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest =
+ settings.fastWithdrawal
+ ? {
+ suggested_amount: Amounts.stringify(parsedAmount),
+ }
+ : {
+ amount: Amounts.stringify(parsedAmount),
+ };
+ const resp = await api.createWithdrawal(creds, params);
if (resp.type === "ok") {
const uri = parseWithdrawUri(resp.body.taler_withdraw_uri);
if (!uri) {
diff --git a/packages/bank-ui/src/settings.json b/packages/bank-ui/src/settings.json
index b1df52568..df5fe75ce 100644
--- a/packages/bank-ui/src/settings.json
+++ b/packages/bank-ui/src/settings.json
@@ -1,5 +1,5 @@
{
- "backendBaseURL": "http://bank-fir.taler.test:1180/",
+ "backendBaseURL": "http://bank.taler.test:1180/",
"simplePasswordForRandomAccounts": true,
"allowRandomAccountCreation": true,
"bankName": "Taler DEVELOPMENT Bank",
diff --git a/packages/taler-util/src/http-client/bank-integration.ts b/packages/taler-util/src/http-client/bank-integration.ts
index 08dab58e2..e07b6c5fa 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -50,7 +50,7 @@ export type TalerBankIntegrationErrorsByMethod<
* The API is used by the wallets.
*/
export class TalerBankIntegrationHttpClient {
- public readonly PROTOCOL_VERSION = "2:0:2";
+ public readonly PROTOCOL_VERSION = "2:0:0";
httpLib: HttpRequestLibrary;
diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts
index 94f2c7518..f05bbc91a 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -1312,6 +1312,7 @@ export const codecForBankWithdrawalOperationStatus =
),
)
.property("amount", codecOptional(codecForAmountString()))
+ .property("suggested_amount", codecOptional(codecForAmountString()))
.property("card_fees", codecOptional(codecForAmountString()))
.property("sender_wire", codecOptional(codecForPaytoString()))
.property("suggested_exchange", codecOptional(codecForString()))
@@ -2029,12 +2030,23 @@ export namespace TalerBankIntegrationApi {
// confirmed: the transfer has been confirmed and registered by the bank
status: WithdrawalOperationStatus;
+ // Currency used for the withdrawal.
+ // MUST be present when amount is absent.
+ // @since v2, may become mandatory in the future.
+ currency?: string;
+
// Amount that will be withdrawn with this operation
// (raw amount without fee considerations). Only
// given once the amount is fixed and cannot be changed.
// Optional since **vC2EC**.
amount?: AmountString | undefined;
+ // Suggestion for the amount to be withdrawn with this
+ // operation. Given if a suggestion was made but the
+ // user may still change the amount.
+ // Optional since **vC2EC**.
+ suggested_amount?: AmountString | undefined;
+
// Maximum amount that the wallet can choose to withdraw.
// Only applicable when the amount is not fixed.
// @since **vC2EC**.
@@ -2046,17 +2058,13 @@ export namespace TalerBankIntegrationApi {
// @since **vC2EC**
card_fees?: AmountString | undefined;
- // Bank account of the customer that is withdrawing, as a
- // payto URI.
+ // Bank account of the customer that is debiting, as an
+ // RFC 8905 payto URI.
sender_wire?: PaytoString;
- // Suggestion for the amount to be withdrawn with this
- // operation. Given if a suggestion was made but the
- // user may still change the amount.
- // Optional since **vC2EC**.
- suggested_amount?: AmountString | undefined;
-
- // Suggestion for an exchange given by the bank.
+ // Base URL of the suggested exchange. The bank may have
+ // neither a suggestion nor a requirement for the exchange.
+ // This value is typically set in the bank's configuration.
suggested_exchange?: string;
// Base URL of an exchange that must be used. Optional,
@@ -2067,7 +2075,8 @@ export namespace TalerBankIntegrationApi {
// URL that the user needs to navigate to in order to
// complete some final confirmation (e.g. 2FA).
- // It may contain withdrawal operation id
+ // Only applicable when status is selected or pending.
+ // It may contain the withdrawal operation id.
confirm_transfer_url?: string;
// Wire transfer types supported by the bank.
@@ -2077,13 +2086,15 @@ export namespace TalerBankIntegrationApi {
// only non-null if status is selected or confirmed.
selected_reserve_pub?: string;
- // Exchange account selected by the wallet
+ // Exchange account selected by the wallet;
// only non-null if status is selected or confirmed.
+ // @since **v1**
selected_exchange_account?: string;
}
export interface BankWithdrawalOperationPostRequest {
- // Reserve public key.
+ // Reserve public key that should become the wire transfer
+ // subject to fund the withdrawal.
reserve_pub: string;
// Payto address of the exchange selected for the withdrawal.
@@ -2106,7 +2117,7 @@ export namespace TalerBankIntegrationApi {
// URL that the user needs to navigate to in order to
// complete some final confirmation (e.g. 2FA).
//
- // Only applicable when status is selected.
+ // Only applicable when status is selected or pending.
// It may contain withdrawal operation id
confirm_transfer_url?: string;
}
@@ -2181,12 +2192,31 @@ export namespace TalerCorebankApi {
// Default to 'iban' is missing
// @since v4, may become mandatory in the future.
wire_type: string;
+
+ // Wire transfer execution fees.
+ // @since v4, will become mandatory in the next version.
+ wire_transfer_fees?: AmountString;
}
export interface BankAccountCreateWithdrawalRequest {
- // Amount to withdraw.
- amount: AmountString;
+ // Amount to withdraw. If given, the wallet
+ // cannot change the amount.
+ // Optional since **vC2EC**.
+ amount?: AmountString;
+
+ // Suggested amount to withdraw. The wallet can
+ // still change the suggestion.
+ // @since **vC2EC**
+ suggested_amount?: AmountString;
+
+ // The non-Taler card fees the customer will have
+ // to pay to the account owner, bank and/or
+ // payment service provider
+ // they are using to make this withdrawal.
+ // @since **vC2EC**
+ card_fees?: AmountString;
}
+
export interface BankAccountCreateWithdrawalResponse {
// ID of the withdrawal, can be used to view/modify the withdrawal operation.
withdrawal_id: string;
@@ -2529,10 +2559,6 @@ export namespace TalerCorebankApi {
export interface CashoutInfo {
cashout_id: number;
- /**
- * @deprecated since 4, use new 2fa
- */
- status?: "pending" | "aborted" | "confirmed";
}
export interface GlobalCashouts {
// Every string represents a cash-out operation ID.
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index 66b1e9769..a7aa4f863 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -988,9 +988,12 @@ export interface BankWithdrawDetails {
status: WithdrawalOperationStatus;
currency: string;
amount: AmountJson | undefined;
+ editableAmount: boolean;
+ maxAmount: AmountJson | undefined;
wireFee: AmountJson | undefined;
senderWire?: string;
- suggestedExchange?: string;
+ exchange?: string;
+ editableExchange: boolean;
confirmTransferUrl?: string;
wireTypes: string[];
operationId: string;
@@ -2364,7 +2367,11 @@ export interface WithdrawUriInfoResponse {
confirmTransferUrl?: string;
currency: string;
amount: AmountString | undefined;
+ editableAmount: boolean;
+ maxAmount: AmountString | undefined;
+ wireFee: AmountString | undefined;
defaultExchangeBaseUrl?: string;
+ editableExchange: boolean;
possibleExchanges: ExchangeListItem[];
}
@@ -2383,7 +2390,11 @@ export const codecForWithdrawUriInfoResponse =
),
)
.property("amount", codecOptional(codecForAmountString()))
+ .property("maxAmount", codecOptional(codecForAmountString()))
+ .property("wireFee", codecOptional(codecForAmountString()))
.property("currency", codecForString())
+ .property("editableAmount", codecForBoolean())
+ .property("editableExchange", codecForBoolean())
.property("defaultExchangeBaseUrl", codecOptional(codecForCanonBaseUrl()))
.property("possibleExchanges", codecForList(codecForExchangeListItem()))
.build("WithdrawUriInfoResponse");
diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts
index d3085ecb4..ec9655e6f 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -123,7 +123,7 @@ export async function topupReserveWithBank(args: TopupReserveWithBankArgs) {
);
const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri);
const bankStatusUrl = getBankStatusUrl(wopi.taler_withdraw_uri);
- if (!bankInfo.suggestedExchange) {
+ if (!bankInfo.exchange) {
throw Error("no suggested exchange");
}
const plainPaytoUris =
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 6aa3b186a..0434aefc2 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -44,6 +44,7 @@ import {
Duration,
EddsaPrivateKeyString,
ExchangeBatchWithdrawRequest,
+ ExchangeListItem,
ExchangeUpdateStatus,
ExchangeWireAccount,
ExchangeWithdrawBatchResponse,
@@ -150,6 +151,7 @@ import {
getExchangePaytoUri,
getExchangeWireDetailsInTx,
listExchanges,
+ lookupExchangeByUri,
markExchangeUsed,
} from "./exchanges.js";
import { DbAccess } from "./query.js";
@@ -886,7 +888,7 @@ export async function getBankWithdrawalInfo(
TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE,
{
bankProtocolVersion: config.version,
- walletProtocolVersion: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
+ walletProtocolVersion: bankApi.PROTOCOL_VERSION,
},
"bank integration protocol version not compatible with wallet",
);
@@ -901,15 +903,36 @@ export async function getBankWithdrawalInfo(
}
const { body: status } = resp;
+ const maxAmount =
+ status.max_amount === undefined
+ ? undefined
+ : Amounts.parseOrThrow(status.max_amount);
+
let amount: AmountJson | undefined;
- if (status.amount) {
+ let editableAmount = false;
+ if (status.amount !== undefined) {
amount = Amounts.parseOrThrow(status.amount);
+ } else {
+ amount =
+ status.suggested_amount === undefined
+ ? undefined
+ : Amounts.parseOrThrow(status.suggested_amount);
+ editableAmount = true;
}
+
let wireFee: AmountJson | undefined;
if (status.card_fees) {
wireFee = Amounts.parseOrThrow(status.card_fees);
}
+ let exchange: string | undefined = undefined;
+ let editableExchange = false;
+ if (status.required_exchange !== undefined) {
+ exchange = status.required_exchange;
+ } else {
+ exchange = status.suggested_exchange;
+ editableExchange = true;
+ }
return {
operationId: uriResult.withdrawalOperationId,
apiBaseUrl: uriResult.bankIntegrationApiBaseUrl,
@@ -918,7 +941,10 @@ export async function getBankWithdrawalInfo(
wireFee,
confirmTransferUrl: status.confirm_transfer_url,
senderWire: status.sender_wire,
- suggestedExchange: status.suggested_exchange,
+ exchange,
+ editableAmount,
+ editableExchange,
+ maxAmount,
wireTypes: status.wire_types,
status: status.status,
};
@@ -2328,39 +2354,52 @@ export async function getWithdrawalDetailsForUri(
logger.trace(`getting withdrawal details for URI ${talerWithdrawUri}`);
const info = await getBankWithdrawalInfo(wex.http, talerWithdrawUri);
logger.trace(`got bank info`);
- if (info.suggestedExchange) {
+ if (info.exchange) {
try {
// If the exchange entry doesn't exist yet,
// it'll be created as an ephemeral entry.
- await fetchFreshExchange(wex, info.suggestedExchange);
+ await fetchFreshExchange(wex, info.exchange);
} catch (e) {
// We still continued if it failed, as other exchanges might be available.
// We don't want to fail if the bank-suggested exchange is broken/offline.
logger.trace(
- `querying bank-suggested exchange (${info.suggestedExchange}) failed`,
+ `querying bank-suggested exchange (${info.exchange}) failed`,
);
}
}
const currency = info.currency;
- const listExchangesResp = await listExchanges(wex);
- const possibleExchanges = listExchangesResp.exchanges.filter((x) => {
- return (
- x.currency === currency &&
- (x.exchangeUpdateStatus === ExchangeUpdateStatus.Ready ||
- x.exchangeUpdateStatus === ExchangeUpdateStatus.ReadyUpdate)
- );
- });
+ let possibleExchanges: ExchangeListItem[];
+ if (!info.editableExchange && info.exchange !== undefined) {
+ const ex: ExchangeListItem = await lookupExchangeByUri(wex, {
+ exchangeBaseUrl: info.exchange,
+ });
+ possibleExchanges = [ex];
+ } else {
+ const listExchangesResp = await listExchanges(wex);
+
+ possibleExchanges = listExchangesResp.exchanges.filter((x) => {
+ return (
+ x.currency === currency &&
+ (x.exchangeUpdateStatus === ExchangeUpdateStatus.Ready ||
+ x.exchangeUpdateStatus === ExchangeUpdateStatus.ReadyUpdate)
+ );
+ });
+ }
return {
operationId: info.operationId,
confirmTransferUrl: info.confirmTransferUrl,
status: info.status,
currency,
+ editableAmount: info.editableAmount,
+ editableExchange: info.editableExchange,
+ maxAmount: info.maxAmount ? Amounts.stringify(info.maxAmount) : undefined,
amount: info.amount ? Amounts.stringify(info.amount) : undefined,
- defaultExchangeBaseUrl: info.suggestedExchange,
+ defaultExchangeBaseUrl: info.exchange,
possibleExchanges,
+ wireFee: info.wireFee ? Amounts.stringify(info.wireFee) : undefined,
};
}
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
index 026a879df..af1ef213b 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
@@ -96,12 +96,15 @@ export namespace State {
currentExchange: ExchangeListItem;
amount: AmountFieldHandler;
+ editableAmount: boolean;
+ bankFee: AmountJson;
withdrawalFee: AmountJson;
toBeReceived: AmountJson;
doWithdrawal: ButtonHandler;
doSelectExchange: ButtonHandler;
+ editableExchange: boolean;
chooseCurrencies: string[];
selectedCurrency: string;
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index da3b1eeb2..f8e27e688 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -185,10 +185,16 @@ export function useComponentStateFromParams({
cancel,
onSuccess,
undefined,
- chosenAmount,
- chosenAmount.currency,
- exchangeList,
- exchangeByTalerUri,
+ {
+ amount: chosenAmount,
+ currency: chosenAmount.currency,
+ maxAmount: Amounts.zeroOfCurrency(chosenAmount.currency),
+ bankFee: Amounts.zeroOfCurrency(chosenAmount.currency),
+ editableAmount: true,
+ editableExchange: true,
+ exchange: exchangeByTalerUri,
+ exchangeList: exchangeList,
+ },
setUpdatedExchangeByUser,
);
}
@@ -215,13 +221,7 @@ export function useComponentStateFromURI({
WalletApiOperation.PrepareBankIntegratedWithdrawal,
{ talerWithdrawUri },
);
- const {
- amount,
- defaultExchangeBaseUrl,
- possibleExchanges,
- confirmTransferUrl,
- status,
- } = uriInfo.info;
+ const { status } = uriInfo.info;
const txInfo = await api.wallet.call(
WalletApiOperation.GetTransactionById,
{
@@ -232,12 +232,8 @@ export function useComponentStateFromURI({
talerWithdrawUri,
status,
transactionId: uriInfo.transactionId,
- currency: uriInfo.info.currency,
+ bankWithdrawalInfo: uriInfo.info,
txInfo: txInfo,
- confirmTransferUrl,
- amount: !amount ? undefined : Amounts.parseOrThrow(amount),
- thisExchange: defaultExchangeBaseUrl,
- exchanges: possibleExchanges,
};
});
@@ -277,9 +273,22 @@ export function useComponentStateFromURI({
const uri = uriInfoHook.response.talerWithdrawUri;
const txId = uriInfoHook.response.transactionId;
- const infoAmount = uriInfoHook.response.amount;
- const defaultExchange = uriInfoHook.response.thisExchange;
- const exchangeList = uriInfoHook.response.exchanges;
+ const bwi = uriInfoHook.response.bankWithdrawalInfo;
+
+ const amount =
+ bwi.amount === undefined
+ ? Amounts.zeroOfCurrency(bwi.currency)
+ : Amounts.parseOrThrow(bwi.amount);
+
+ const maxAmount =
+ bwi.maxAmount === undefined
+ ? Amounts.zeroOfCurrency(bwi.currency)
+ : Amounts.parseOrThrow(bwi.maxAmount);
+
+ const bankFee =
+ bwi.wireFee === undefined
+ ? Amounts.zeroOfCurrency(bwi.currency)
+ : Amounts.parseOrThrow(bwi.wireFee);
async function doManagedWithdraw(
exchange: string,
@@ -309,7 +318,7 @@ export function useComponentStateFromURI({
return {
status: "already-completed",
operationState: uriInfoHook.response.status,
- confirmTransferUrl: uriInfoHook.response.confirmTransferUrl,
+ confirmTransferUrl: bwi.confirmTransferUrl,
thisWallet: info.txState.major === TransactionMajorState.Pending,
redirectToTx: () => onSuccess(info.transactionId),
error: undefined,
@@ -322,15 +331,32 @@ export function useComponentStateFromURI({
cancel,
onSuccess,
uri,
- infoAmount,
- uriInfoHook.response.currency,
- exchangeList,
- defaultExchange,
+ {
+ amount,
+ bankFee,
+ maxAmount,
+ currency: bwi.currency,
+ editableAmount: bwi.editableAmount,
+ editableExchange: bwi.editableExchange,
+ exchange: bwi.defaultExchangeBaseUrl,
+ exchangeList: bwi.possibleExchanges,
+ },
setUpdatedExchangeByUser,
);
}, []);
}
+type WithdrawalInfo = {
+ currency: string;
+ amount: AmountJson;
+ bankFee: AmountJson;
+ maxAmount: AmountJson;
+ editableAmount: boolean;
+ exchange: string | undefined;
+ editableExchange: boolean;
+ exchangeList: ExchangeListItem[];
+};
+
type ManualOrManagedWithdrawFunction = (
exchange: string,
ageRestricted: number | undefined,
@@ -342,17 +368,14 @@ function exchangeSelectionState(
cancel: () => Promise<void>,
onSuccess: (txid: string) => Promise<void>,
talerWithdrawUri: string | undefined,
- infoAmount: AmountJson | undefined,
- currency: string,
- exchangeList: ExchangeListItem[],
- exchangeSuggestedByTheBank: string | undefined,
+ wInfo: WithdrawalInfo,
onExchangeUpdated: (ex: string) => void,
): RecursiveState<State> {
const api = useBackendContext();
const selectedExchange = useSelectedExchange({
- currency: currency,
- defaultExchange: exchangeSuggestedByTheBank,
- list: exchangeList,
+ currency: wInfo.currency,
+ defaultExchange: wInfo.exchange,
+ list: wInfo.exchangeList,
});
const current =
@@ -365,9 +388,9 @@ function exchangeSelectionState(
}
}, [current]);
- const safeAmount = !infoAmount
- ? Amounts.zeroOfCurrency(currency)
- : infoAmount;
+ const safeAmount = wInfo.amount
+ ? wInfo.amount
+ : Amounts.zeroOfCurrency(wInfo.currency);
const [choosenAmount, setChoosenAmount] = useState(safeAmount);
if (selectedExchange.status !== "ready") {
@@ -383,7 +406,9 @@ function exchangeSelectionState(
const [ageRestricted, setAgeRestricted] = useState(0);
const currentExchange = selectedExchange.selected;
- const [selectedCurrency, setSelectedCurrency] = useState<string>(currency);
+ const [selectedCurrency, setSelectedCurrency] = useState<string>(
+ wInfo.currency,
+ );
/**
* With the exchange and amount, ask the wallet the information
* about the withdrawal
@@ -456,6 +481,8 @@ function exchangeSelectionState(
).amount;
const toBeReceived = amountHook.response.amount.effective;
+ const bankFee = wInfo.amount;
+
const ageRestrictionOptions =
amountHook.response.ageRestrictionOptions?.reduce(
(p, c) => ({ ...p, [c]: i18n.str`under ${c}` }),
@@ -499,13 +526,26 @@ function exchangeSelectionState(
amount: Amounts.parseOrThrow(convAccount.transferAmount!),
};
+ const amountError = Amounts.isZero(choosenAmount)
+ ? i18n.str`should be greater than zero`
+ : Amounts.cmp(choosenAmount, wInfo.maxAmount) === -1
+ ? i18n.str`choose a lower value`
+ : undefined;
+
return {
status: "success",
error: undefined,
- doSelectExchange: selectedExchange.doSelect,
+ doSelectExchange: {
+ onClick: wInfo.editableExchange
+ ? selectedExchange.doSelect.onClick
+ : undefined,
+ },
+ editableAmount: wInfo.editableAmount,
+ editableExchange: wInfo.editableExchange,
currentExchange,
toBeReceived,
chooseCurrencies,
+ bankFee,
selectedCurrency,
changeCurrency: (s) => {
setSelectedCurrency(s);
@@ -514,16 +554,20 @@ function exchangeSelectionState(
withdrawalFee,
amount: {
value: choosenAmount,
- onInput: pushAlertOnError(async (v) => {
- setChoosenAmount(v);
- }),
+ onInput: wInfo.editableAmount
+ ? pushAlertOnError(async (v) => {
+ setChoosenAmount(v);
+ })
+ : undefined,
+ error: amountError,
},
talerWithdrawUri,
ageRestriction,
doWithdrawal: {
- onClick: doingWithdraw
- ? undefined
- : pushAlertOnError(doWithdrawAndCheckError),
+ onClick:
+ doingWithdraw && !amountError
+ ? undefined
+ : pushAlertOnError(doWithdrawAndCheckError),
},
cancel,
};
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index 5bbf5f6c8..bce5f71e3 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -119,7 +119,11 @@ describe("Withdraw CTA states", () => {
currency: "ARS",
amount: "EUR:2" as AmountString,
possibleExchanges: [],
- }
+ editableAmount: false,
+ editableExchange: false,
+ maxAmount: "ARS:1",
+ wireFee: "ARS:0",
+ },
},
);
@@ -162,7 +166,11 @@ describe("Withdraw CTA states", () => {
amount: "ARS:2" as AmountString,
possibleExchanges: exchanges,
defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl,
- }
+ editableAmount: false,
+ editableExchange: false,
+ maxAmount: "ARS:1",
+ wireFee: "ARS:0",
+ },
},
);
handler.addWalletCallResponse(
@@ -176,7 +184,7 @@ describe("Withdraw CTA states", () => {
scopeInfo: {
currency: "ARS",
type: ScopeType.Exchange,
- url: "http://asd"
+ url: "http://asd",
},
withdrawalAccountsList: [],
ageRestrictionOptions: [],
@@ -236,6 +244,10 @@ describe("Withdraw CTA states", () => {
amount: "ARS:2" as AmountString,
possibleExchanges: exchangeWithNewTos,
defaultExchangeBaseUrl: exchangeWithNewTos[0].exchangeBaseUrl,
+ editableAmount: false,
+ editableExchange: false,
+ maxAmount: "ARS:1",
+ wireFee: "ARS:0",
},
);
handler.addWalletCallResponse(
@@ -248,7 +260,7 @@ describe("Withdraw CTA states", () => {
scopeInfo: {
currency: "ARS",
type: ScopeType.Exchange,
- url: "http://asd"
+ url: "http://asd",
},
tosAccepted: false,
withdrawalAccountsList: [],
@@ -267,6 +279,10 @@ describe("Withdraw CTA states", () => {
amount: "ARS:2" as AmountString,
possibleExchanges: exchanges,
defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl,
+ editableAmount: false,
+ editableExchange: false,
+ maxAmount: "ARS:1",
+ wireFee: "ARS:0",
},
);
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index a4917446d..86d7248a4 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -19,6 +19,7 @@ import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { Amount } from "../../components/Amount.js";
import { AmountField } from "../../components/AmountField.js";
+import { EnabledBySettings } from "../../components/EnabledBySettings.js";
import { Part } from "../../components/Part.js";
import { QR } from "../../components/QR.js";
import { SelectList } from "../../components/SelectList.js";
@@ -38,7 +39,6 @@ import {
getAmountWithFee,
} from "../../wallet/Transaction.js";
import { State } from "./index.js";
-import { EnabledBySettings } from "../../components/EnabledBySettings.js";
export function FinalStateOperation(state: State.AlreadyCompleted): VNode {
const { i18n } = useTranslationContext();
@@ -174,6 +174,11 @@ export function SuccessView(state: State.Success): VNode {
kind="neutral"
big
/>
+ {state.editableAmount ? (
+ <Fragment>
+ <AmountField handler={state.amount} label={i18n.str`Amount`} />
+ </Fragment>
+ ) : undefined}
{state.chooseCurrencies.length > 0 ? (
<Fragment>
<p>
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index 4394a982f..47b466fcd 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -55,7 +55,7 @@ import { WalletActivityTrack } from "./wxBackend.js";
const logger = new Logger("wxApi");
-export const WALLET_CORE_SUPPORTED_VERSION = "4:0:0"
+export const WALLET_CORE_SUPPORTED_VERSION = "5:0:0"
export interface ExtendedPermissionsResponse {
newValue: boolean;