aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-05-23 22:11:23 +0200
committerFlorian Dold <florian@dold.me>2024-05-23 22:11:23 +0200
commit274b72f6ea4ac92e334b97a9cc427d64b2307217 (patch)
treeb7fdda8443d2caba434440fb77bafad149967b6d /packages
parent5d7cad858d994489d8c79ec9bccbd03bb04d359c (diff)
downloadwallet-core-274b72f6ea4ac92e334b97a9cc427d64b2307217.tar.xz
wallet-core: implement acceptBankIntegratedWithdrawal via prepare/confirm step
This avoids duplication of some subtle logic.
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-util/src/wallet-types.ts4
-rw-r--r--packages/taler-wallet-core/src/versions.ts2
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts158
3 files changed, 65 insertions, 99 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index 9301a9723..dce811462 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -1865,7 +1865,7 @@ export interface PrepareBankIntegratedWithdrawalResponse {
export interface ConfirmWithdrawalRequest {
transactionId: string;
exchangeBaseUrl: string;
- amount: AmountString;
+ amount?: AmountString;
forcedDenomSel?: ForcedDenomSel;
restrictAge?: number;
}
@@ -1874,7 +1874,7 @@ export const codecForConfirmWithdrawalRequestRequest =
(): Codec<ConfirmWithdrawalRequest> =>
buildCodecForObject<ConfirmWithdrawalRequest>()
.property("transactionId", codecForString())
- .property("amount", codecForAmountString())
+ .property("amount", codecOptional(codecForAmountString()))
.property("exchangeBaseUrl", codecForCanonBaseUrl())
.property("forcedDenomSel", codecForAny())
.property("restrictAge", codecOptional(codecForNumber()))
diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts
index d33a23cdd..b89ff16f6 100644
--- a/packages/taler-wallet-core/src/versions.ts
+++ b/packages/taler-wallet-core/src/versions.ts
@@ -52,7 +52,7 @@ export const WALLET_BANK_CONVERSION_API_PROTOCOL_VERSION = "2:0:0";
/**
* Libtool version of the wallet-core API.
*/
-export const WALLET_CORE_API_PROTOCOL_VERSION = "5:0:0";
+export const WALLET_CORE_API_PROTOCOL_VERSION = "6:0:0";
/**
* Libtool rules:
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 1dc4e0999..7f43b4333 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -2953,6 +2953,7 @@ export async function prepareBankIntegratedWithdrawal(
},
},
reserveStatus: WithdrawalGroupStatus.DialogProposed,
+ amount: info.amount == null ? undefined : Amounts.parseOrThrow(info.amount),
});
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
@@ -2999,6 +3000,25 @@ export async function confirmWithdrawal(
const talerWithdrawUri = withdrawalGroup.wgInfo.bankInfo.talerWithdrawUri;
const confirmUrl = withdrawalGroup.wgInfo.bankInfo.confirmUrl;
+ let amount: AmountString;
+
+ if (withdrawalGroup.instructedAmount == null) {
+ if (req.amount == null) {
+ throw Error(
+ "neither the withdrawal group nor the request specifies an amount",
+ );
+ }
+ amount = req.amount;
+ } else {
+ if (
+ req.amount != null &&
+ Amounts.cmp(withdrawalGroup.instructedAmount, req.amount) != 0
+ ) {
+ throw Error("conflicting amount");
+ }
+ amount = withdrawalGroup.instructedAmount;
+ }
+
/**
* The only reason this could be undefined is because it is an old wallet
* database before adding the wireType field was added
@@ -3024,7 +3044,7 @@ export async function confirmWithdrawal(
wex,
{
exchange,
- instructedAmount: Amounts.parseOrThrow(req.amount),
+ instructedAmount: Amounts.parseOrThrow(amount),
},
wex.cancellationToken,
);
@@ -3036,7 +3056,7 @@ export async function confirmWithdrawal(
const initalDenoms = await getInitialDenomsSelection(
wex,
req.exchangeBaseUrl,
- Amounts.parseOrThrow(req.amount),
+ Amounts.parseOrThrow(amount),
req.forcedDenomSel,
);
@@ -3047,7 +3067,7 @@ export async function confirmWithdrawal(
switch (rec.status) {
case WithdrawalGroupStatus.DialogProposed: {
rec.exchangeBaseUrl = req.exchangeBaseUrl;
- rec.instructedAmount = req.amount;
+ rec.instructedAmount = amount;
rec.denomsSel = initalDenoms;
rec.rawWithdrawalAmount = initalDenoms.totalWithdrawCost;
rec.effectiveWithdrawalAmount = initalDenoms.totalCoinValue;
@@ -3074,6 +3094,11 @@ export async function confirmWithdrawal(
}
});
+ wex.ws.notify({
+ type: NotificationType.BalanceChange,
+ hintTransactionId: ctx.transactionId,
+ });
+
await wex.taskScheduler.resetTaskRetries(ctx.taskId);
}
@@ -3097,113 +3122,54 @@ export async function acceptWithdrawalFromUri(
amount?: AmountLike;
},
): Promise<AcceptWithdrawalResponse> {
- const selectedExchange = req.selectedExchange;
- logger.info(
- `accepting withdrawal via ${req.talerWithdrawUri}, canonicalized selected exchange ${selectedExchange}`,
- );
- const existingWithdrawalGroup = await wex.db.runReadOnlyTx(
- { storeNames: ["withdrawalGroups"] },
- async (tx) => {
- return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
- req.talerWithdrawUri,
- );
- },
- );
-
- if (existingWithdrawalGroup) {
- let url: string | undefined;
- if (
- existingWithdrawalGroup.wgInfo.withdrawalType ===
- WithdrawalRecordType.BankIntegrated
- ) {
- url = existingWithdrawalGroup.wgInfo.bankInfo.confirmUrl;
- }
- return {
- reservePub: existingWithdrawalGroup.reservePub,
- confirmTransferUrl: url,
- transactionId: constructTransactionIdentifier({
- tag: TransactionType.Withdrawal,
- withdrawalGroupId: existingWithdrawalGroup.withdrawalGroupId,
- }),
- };
- }
-
- const exchange = await fetchFreshExchange(wex, selectedExchange);
- const withdrawInfo = await getBankWithdrawalInfo(
- wex.http,
- req.talerWithdrawUri,
- );
- const exchangePaytoUri = await getExchangePaytoUri(
- wex,
- selectedExchange,
- withdrawInfo.wireTypes,
- );
+ const resp = await prepareBankIntegratedWithdrawal(wex, {
+ talerWithdrawUri: req.talerWithdrawUri,
+ selectedExchange: req.selectedExchange,
+ });
- let amount: AmountJson;
- if (withdrawInfo.amount == null) {
- if (req.amount == null) {
- throw Error(
- "amount required, as withdrawal operation has flexible amount",
- );
- }
- amount = Amounts.parseOrThrow(req.amount);
- } else {
- if (
- req.amount != null &&
- Amounts.cmp(req.amount, withdrawInfo.amount) != 0
- ) {
- throw Error(
- "mismatched amount, amount is fixed by bank but client provided different amount",
- );
- }
- amount = withdrawInfo.amount;
+ const transactionId = resp.transactionId;
+ if (!transactionId) {
+ throw Error("internal error, no transaction ID");
}
- const withdrawalAccountList = await fetchWithdrawalAccountInfo(
- wex,
- {
- exchange,
- instructedAmount: amount,
- },
- CancellationToken.CONTINUE,
- );
-
- const withdrawalGroup = await internalCreateWithdrawalGroup(wex, {
- amount,
+ await confirmWithdrawal(wex, {
+ transactionId,
+ amount: req.amount == null ? undefined : Amounts.stringify(req.amount),
exchangeBaseUrl: req.selectedExchange,
- wgInfo: {
- withdrawalType: WithdrawalRecordType.BankIntegrated,
- exchangeCreditAccounts: withdrawalAccountList,
- bankInfo: {
- exchangePaytoUri,
- talerWithdrawUri: req.talerWithdrawUri,
- confirmUrl: withdrawInfo.confirmTransferUrl,
- timestampBankConfirmed: undefined,
- timestampReserveInfoPosted: undefined,
- wireTypes: withdrawInfo.wireTypes,
- },
- },
- restrictAge: req.restrictAge,
forcedDenomSel: req.forcedDenomSel,
- reserveStatus: WithdrawalGroupStatus.PendingRegisteringBank,
+ restrictAge: req.restrictAge,
});
- const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
+ const parsedTx = parseTransactionIdentifier(transactionId);
+ checkLogicInvariant(parsedTx?.tag === TransactionType.Withdrawal);
- const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId);
+ const ctx = new WithdrawTransactionContext(wex, parsedTx.withdrawalGroupId);
- wex.ws.notify({
- type: NotificationType.BalanceChange,
- hintTransactionId: ctx.transactionId,
- });
+ await waitWithdrawalRegistered(wex, ctx);
- wex.taskScheduler.startShepherdTask(ctx.taskId);
+ const withdrawalGroup = await wex.db.runReadOnlyTx(
+ {
+ storeNames: ["withdrawalGroups"],
+ },
+ async (tx) => {
+ return tx.withdrawalGroups.get(parsedTx.withdrawalGroupId);
+ },
+ );
- await waitWithdrawalRegistered(wex, ctx);
+ if (!withdrawalGroup) {
+ throw Error("withdrawal group does not exist anymore");
+ }
+
+ if (
+ withdrawalGroup.wgInfo.withdrawalType !==
+ WithdrawalRecordType.BankIntegrated
+ ) {
+ throw Error("withdrawal group has unexpected withdrawal type");
+ }
return {
reservePub: withdrawalGroup.reservePub,
- confirmTransferUrl: withdrawInfo.confirmTransferUrl,
+ confirmTransferUrl: withdrawalGroup.wgInfo.bankInfo.confirmUrl,
transactionId: ctx.transactionId,
};
}