aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-05-30 13:16:15 -0300
committerSebastian <sebasjm@gmail.com>2024-06-06 13:03:13 -0300
commit60a4640a35be584ee004de5e362f21ed03fa239e (patch)
tree1df828dd3c5f5898434efb5df301d4f116196581 /packages/taler-wallet-core
parent920595aa3864615c07aba0ecfc233acf05f3e3e6 (diff)
downloadwallet-core-60a4640a35be584ee004de5e362f21ed03fa239e.tar.xz
working #8882
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r--packages/taler-wallet-core/src/balance.ts12
-rw-r--r--packages/taler-wallet-core/src/db.ts4
-rw-r--r--packages/taler-wallet-core/src/transactions.ts51
-rw-r--r--packages/taler-wallet-core/src/wallet.ts5
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts301
5 files changed, 228 insertions, 145 deletions
diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts
index 76e604324..4f06e3756 100644
--- a/packages/taler-wallet-core/src/balance.ts
+++ b/packages/taler-wallet-core/src/balance.ts
@@ -379,6 +379,10 @@ export async function getBalancesInsideTransaction(
wg.denomsSel !== undefined,
"wg in kyc state should have been initialized",
);
+ checkDbInvariant(
+ wg.exchangeBaseUrl !== undefined,
+ "wg in kyc state should have been initialized",
+ );
const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
await balanceStore.setFlagIncomingKyc(currency, wg.exchangeBaseUrl);
break;
@@ -389,6 +393,10 @@ export async function getBalancesInsideTransaction(
wg.denomsSel !== undefined,
"wg in aml state should have been initialized",
);
+ checkDbInvariant(
+ wg.exchangeBaseUrl !== undefined,
+ "wg in kyc state should have been initialized",
+ );
const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
await balanceStore.setFlagIncomingAml(currency, wg.exchangeBaseUrl);
break;
@@ -408,6 +416,10 @@ export async function getBalancesInsideTransaction(
wg.denomsSel !== undefined,
"wg in confirmed state should have been initialized",
);
+ checkDbInvariant(
+ wg.exchangeBaseUrl !== undefined,
+ "wg in kyc state should have been initialized",
+ );
const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
await balanceStore.setFlagIncomingConfirmation(
currency,
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index ad9b4f1cb..4ec83a783 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -395,6 +395,8 @@ export interface ReserveBankInfo {
timestampBankConfirmed: DbPreciseTimestamp | undefined;
wireTypes: string[] | undefined;
+
+ currency: string | undefined;
}
/**
@@ -1531,7 +1533,7 @@ export interface WithdrawalGroupRecord {
* The exchange base URL that we're withdrawing from.
* (Redundantly stored, as the reserve record also has this info.)
*/
- exchangeBaseUrl: string;
+ exchangeBaseUrl?: string;
/**
* When was the withdrawal operation started started?
diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts
index 72aff319a..bcf3fcaf6 100644
--- a/packages/taler-wallet-core/src/transactions.ts
+++ b/packages/taler-wallet-core/src/transactions.ts
@@ -243,11 +243,14 @@ export async function getTransactionById(
const opId = TaskIdentifiers.forWithdrawal(withdrawalGroupRecord);
const ort = await tx.operationRetries.get(opId);
- const exchangeDetails = await getExchangeWireDetailsInTx(
- tx,
- withdrawalGroupRecord.exchangeBaseUrl,
- );
- if (!exchangeDetails) throw Error("not exchange details");
+ const exchangeDetails =
+ withdrawalGroupRecord.exchangeBaseUrl === undefined
+ ? undefined
+ : await getExchangeWireDetailsInTx(
+ tx,
+ withdrawalGroupRecord.exchangeBaseUrl,
+ );
+ // if (!exchangeDetails) throw Error("not exchange details");
if (
withdrawalGroupRecord.wgInfo.withdrawalType ===
@@ -259,7 +262,10 @@ export async function getTransactionById(
ort,
);
}
-
+ checkDbInvariant(
+ exchangeDetails !== undefined,
+ "manual withdrawal without exchange",
+ );
return buildTransactionForManualWithdraw(
withdrawalGroupRecord,
exchangeDetails,
@@ -404,7 +410,10 @@ export async function getTransactionById(
const debit = await tx.peerPushDebit.get(parsedTx.pursePub);
if (!debit) throw Error("not found");
const ct = await tx.contractTerms.get(debit.contractTermsHash);
- checkDbInvariant(!!ct, `no contract terms for p2p push ${parsedTx.pursePub}`);
+ checkDbInvariant(
+ !!ct,
+ `no contract terms for p2p push ${parsedTx.pursePub}`,
+ );
return buildTransactionForPushPaymentDebit(
debit,
ct.contractTermsRaw,
@@ -428,7 +437,10 @@ export async function getTransactionById(
const pushInc = await tx.peerPushCredit.get(peerPushCreditId);
if (!pushInc) throw Error("not found");
const ct = await tx.contractTerms.get(pushInc.contractTermsHash);
- checkDbInvariant(!!ct, `no contract terms for p2p push ${peerPushCreditId}`);
+ checkDbInvariant(
+ !!ct,
+ `no contract terms for p2p push ${peerPushCreditId}`,
+ );
let wg: WithdrawalGroupRecord | undefined = undefined;
let wgOrt: OperationRetryRecord | undefined = undefined;
@@ -593,6 +605,7 @@ function buildTransactionForPeerPullCredit(
const txState = computePeerPullCreditTransactionState(pullCredit);
checkDbInvariant(wsr.instructedAmount !== undefined, "wg uninitialized");
checkDbInvariant(wsr.denomsSel !== undefined, "wg uninitialized");
+ checkDbInvariant(wsr.exchangeBaseUrl !== undefined, "wg uninitialized");
return {
type: TransactionType.PeerPullCredit,
txState,
@@ -667,6 +680,7 @@ function buildTransactionForPeerPushCredit(
}
checkDbInvariant(wg.instructedAmount !== undefined, "wg uninitialized");
checkDbInvariant(wg.denomsSel !== undefined, "wg uninitialized");
+ checkDbInvariant(wg.exchangeBaseUrl !== undefined, "wg uninitialized");
const txState = computePeerPushCreditTransactionState(pushInc);
return {
@@ -719,15 +733,17 @@ function buildTransactionForPeerPushCredit(
function buildTransactionForBankIntegratedWithdraw(
wg: WithdrawalGroupRecord,
- exchangeDetails: ExchangeWireDetails,
+ exchangeDetails: ExchangeWireDetails | undefined,
ort?: OperationRetryRecord,
): TransactionWithdrawal {
if (wg.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
throw Error("");
}
+ checkDbInvariant(wg.wgInfo.bankInfo.currency !== undefined, "wg uninitialized");
const txState = computeWithdrawalTransactionStatus(wg);
+
const zero = Amounts.stringify(
- Amounts.zeroOfCurrency(exchangeDetails.currency),
+ Amounts.zeroOfCurrency(wg.wgInfo.bankInfo.currency),
);
return {
type: TransactionType.Withdrawal,
@@ -784,6 +800,7 @@ function buildTransactionForManualWithdraw(
checkDbInvariant(wg.instructedAmount !== undefined, "wg uninitialized");
checkDbInvariant(wg.denomsSel !== undefined, "wg uninitialized");
+ checkDbInvariant(wg.exchangeBaseUrl !== undefined, "wg uninitialized");
const exchangePaytoUris = augmentPaytoUrisForWithdrawal(
plainPaytoUris,
wg.reservePub,
@@ -1034,8 +1051,14 @@ function buildTransactionForPurchase(
}));
const timestamp = purchaseRecord.timestampAccept;
- checkDbInvariant(!!timestamp, `purchase ${purchaseRecord.orderId} without accepted time`);
- checkDbInvariant(!!purchaseRecord.payInfo, `purchase ${purchaseRecord.orderId} without payinfo`);
+ checkDbInvariant(
+ !!timestamp,
+ `purchase ${purchaseRecord.orderId} without accepted time`,
+ );
+ checkDbInvariant(
+ !!purchaseRecord.payInfo,
+ `purchase ${purchaseRecord.orderId} without payinfo`,
+ );
const txState = computePayMerchantTransactionState(purchaseRecord);
return {
@@ -1089,6 +1112,10 @@ export async function getWithdrawalTransactionByUri(
if (!withdrawalGroupRecord) {
return undefined;
}
+ if (withdrawalGroupRecord.exchangeBaseUrl === undefined) {
+ // prepared and unconfirmed withdrawals are hidden
+ return undefined;
+ }
const opId = TaskIdentifiers.forWithdrawal(withdrawalGroupRecord);
const ort = await tx.operationRetries.get(opId);
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index d98106d1f..f1d53b7d5 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -1010,10 +1010,7 @@ async function dispatchRequestInternal(
case WalletApiOperation.PrepareBankIntegratedWithdrawal: {
const req =
codecForPrepareBankIntegratedWithdrawalRequest().decode(payload);
- return prepareBankIntegratedWithdrawal(wex, {
- talerWithdrawUri: req.talerWithdrawUri,
- selectedExchange: req.selectedExchange,
- });
+ return prepareBankIntegratedWithdrawal(wex, req);
}
case WalletApiOperation.GetExchangeTos: {
const req = codecForGetExchangeTosRequest().decode(payload);
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 24e2861e1..0e7ed144c 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -344,9 +344,11 @@ export class WithdrawTransactionContext implements TransactionContext {
"exchanges" as const,
"exchangeDetails" as const,
];
- let stores = opts.extraStores
+ const stores = opts.extraStores
? [...baseStores, ...opts.extraStores]
: baseStores;
+
+ let errorThrown: Error | undefined;
const transitionInfo = await this.wex.db.runReadWriteTx(
{ storeNames: stores },
async (tx) => {
@@ -359,7 +361,17 @@ export class WithdrawTransactionContext implements TransactionContext {
major: TransactionMajorState.None,
};
}
- const res = await f(wgRec, tx);
+ let res: TransitionResult<WithdrawalGroupRecord> | undefined;
+ try {
+ res = await f(wgRec, tx);
+ } catch (error) {
+ if (error instanceof Error) {
+ errorThrown = error;
+ }
+ return undefined;
+ }
+
+ // const res = await f(wgRec, tx);
switch (res.type) {
case TransitionResultType.Transition: {
await tx.withdrawalGroups.put(res.rec);
@@ -384,6 +396,9 @@ export class WithdrawTransactionContext implements TransactionContext {
}
},
);
+ if (errorThrown) {
+ throw errorThrown;
+ }
notifyTransition(this.wex, this.transactionId, transitionInfo);
return transitionInfo;
}
@@ -929,6 +944,10 @@ async function processPlanchetGenerate(
withdrawalGroup.denomsSel !== undefined,
"can't process uninitialized exchange",
);
+ checkDbInvariant(
+ withdrawalGroup.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
const exchangeBaseUrl = withdrawalGroup.exchangeBaseUrl;
let planchet = await wex.db.runReadOnlyTx(
{ storeNames: ["planchets"] },
@@ -1133,6 +1152,10 @@ async function processPlanchetExchangeBatchRequest(
logger.info(
`processing planchet exchange batch request ${withdrawalGroup.withdrawalGroupId}, start=${args.coinStartIndex}, len=${args.batchSize}`,
);
+ checkDbInvariant(
+ withdrawalGroup.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
const exchangeBaseUrl = withdrawalGroup.exchangeBaseUrl;
const batchReq: ExchangeBatchWithdrawRequest = { planchets: [] };
@@ -1268,6 +1291,10 @@ async function processPlanchetVerifyAndStoreCoin(
resp: ExchangeWithdrawResponse,
): Promise<void> {
const withdrawalGroup = wgContext.wgRecord;
+ checkDbInvariant(
+ withdrawalGroup.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
const exchangeBaseUrl = withdrawalGroup.exchangeBaseUrl;
logger.trace(`checking and storing planchet idx=${coinIdx}`);
@@ -1517,6 +1544,10 @@ async function processQueryReserve(
return TaskRunResult.backoff();
}
checkDbInvariant(
+ withdrawalGroup.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
+ checkDbInvariant(
withdrawalGroup.denomsSel !== undefined,
"can't process uninitialized exchange",
);
@@ -1752,6 +1783,10 @@ async function redenominateWithdrawal(
return;
}
checkDbInvariant(
+ wg.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
+ checkDbInvariant(
wg.denomsSel !== undefined,
"can't process uninitialized exchange",
);
@@ -1894,6 +1929,10 @@ async function processWithdrawalGroupPendingReady(
withdrawalGroup.denomsSel !== undefined,
"can't process uninitialized exchange",
);
+ checkDbInvariant(
+ withdrawalGroup.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
const exchangeBaseUrl = withdrawalGroup.exchangeBaseUrl;
await fetchFreshExchange(wex, withdrawalGroup.exchangeBaseUrl);
@@ -2321,6 +2360,10 @@ export async function getFundingPaytoUris(
const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId);
checkDbInvariant(!!withdrawalGroup, `no withdrawal for ${withdrawalGroupId}`);
checkDbInvariant(
+ withdrawalGroup.exchangeBaseUrl !== undefined,
+ "can't get funding uri from uninitialized wg",
+ );
+ checkDbInvariant(
withdrawalGroup.instructedAmount !== undefined,
"can't get funding uri from uninitialized wg",
);
@@ -2675,7 +2718,7 @@ export async function internalPrepareCreateWithdrawalGroup(
args: {
reserveStatus: WithdrawalGroupStatus;
amount?: AmountJson;
- exchangeBaseUrl: string;
+ exchangeBaseUrl: string | undefined;
forcedWithdrawalGroupId?: string;
forcedDenomSel?: ForcedDenomSel;
reserveKeyPair?: EddsaKeypair;
@@ -2716,7 +2759,7 @@ export async function internalPrepareCreateWithdrawalGroup(
let initialDenomSel: DenomSelectionState | undefined;
const denomSelUid = encodeCrock(getRandomBytes(16));
- if (amount !== undefined) {
+ if (amount !== undefined && exchangeBaseUrl !== undefined) {
initialDenomSel = await getInitialDenomsSelection(
wex,
exchangeBaseUrl,
@@ -2747,7 +2790,9 @@ export async function internalPrepareCreateWithdrawalGroup(
wgInfo: args.wgInfo,
};
- await fetchFreshExchange(wex, exchangeBaseUrl);
+ if (exchangeBaseUrl !== undefined) {
+ await fetchFreshExchange(wex, exchangeBaseUrl);
+ }
const transactionId = constructTransactionIdentifier({
tag: TransactionType.Withdrawal,
@@ -2757,12 +2802,13 @@ export async function internalPrepareCreateWithdrawalGroup(
return {
withdrawalGroup,
transactionId,
- creationInfo: !amount
- ? undefined
- : {
- amount,
- canonExchange: exchangeBaseUrl,
- },
+ creationInfo:
+ !amount || !exchangeBaseUrl
+ ? undefined
+ : {
+ amount,
+ canonExchange: exchangeBaseUrl,
+ },
};
}
@@ -2792,8 +2838,8 @@ export async function internalPerformCreateWithdrawalGroup(
if (existingWg) {
return {
withdrawalGroup: existingWg,
- exchangeNotif: undefined,
transitionInfo: undefined,
+ exchangeNotif: undefined,
};
}
await tx.withdrawalGroups.add(withdrawalGroup);
@@ -2809,7 +2855,21 @@ export async function internalPerformCreateWithdrawalGroup(
exchangeNotif: undefined,
};
}
- const exchange = await tx.exchanges.get(prep.creationInfo.canonExchange);
+ return internalPerformExchangeWasUsed(
+ wex,
+ tx,
+ prep.creationInfo.canonExchange,
+ withdrawalGroup,
+ );
+}
+
+export async function internalPerformExchangeWasUsed(
+ wex: WalletExecutionContext,
+ tx: WalletDbReadWriteTransaction<["exchanges"]>,
+ canonExchange: string,
+ withdrawalGroup: WithdrawalGroupRecord,
+): Promise<PerformCreateWithdrawalGroupResult> {
+ const exchange = await tx.exchanges.get(canonExchange);
if (exchange) {
exchange.lastWithdrawal = timestampPreciseToDb(TalerPreciseTimestamp.now());
await tx.exchanges.put(exchange);
@@ -2825,11 +2885,7 @@ export async function internalPerformCreateWithdrawalGroup(
newTxState,
};
- const exchangeUsedRes = await markExchangeUsed(
- wex,
- tx,
- prep.creationInfo.canonExchange,
- );
+ const exchangeUsedRes = await markExchangeUsed(wex, tx, canonExchange);
const ctx = new WithdrawTransactionContext(
wex,
@@ -2857,7 +2913,7 @@ export async function internalCreateWithdrawalGroup(
wex: WalletExecutionContext,
args: {
reserveStatus: WithdrawalGroupStatus;
- exchangeBaseUrl: string;
+ exchangeBaseUrl: string | undefined;
amount?: AmountJson;
forcedWithdrawalGroupId?: string;
forcedDenomSel?: ForcedDenomSel;
@@ -2903,7 +2959,6 @@ export async function prepareBankIntegratedWithdrawal(
wex: WalletExecutionContext,
req: {
talerWithdrawUri: string;
- selectedExchange?: string;
},
): Promise<PrepareBankIntegratedWithdrawalResponse> {
const existingWithdrawalGroup = await wex.db.runReadOnlyTx(
@@ -2932,12 +2987,6 @@ export async function prepareBankIntegratedWithdrawal(
const info = await getWithdrawalDetailsForUri(wex, req.talerWithdrawUri);
- const exchangeBaseUrl =
- req.selectedExchange ?? withdrawInfo.suggestedExchange;
- if (!exchangeBaseUrl) {
- return { info };
- }
-
/**
* Withdrawal group without exchange and amount
* this is an special case when the user haven't yet
@@ -2946,7 +2995,7 @@ export async function prepareBankIntegratedWithdrawal(
* same URI
*/
const withdrawalGroup = await internalCreateWithdrawalGroup(wex, {
- exchangeBaseUrl,
+ exchangeBaseUrl: undefined,
wgInfo: {
withdrawalType: WithdrawalRecordType.BankIntegrated,
bankInfo: {
@@ -2955,6 +3004,7 @@ export async function prepareBankIntegratedWithdrawal(
timestampBankConfirmed: undefined,
timestampReserveInfoPosted: undefined,
wireTypes: withdrawInfo.wireTypes,
+ currency: withdrawInfo.currency,
},
},
reserveStatus: WithdrawalGroupStatus.DialogProposed,
@@ -2977,6 +3027,9 @@ export async function confirmWithdrawal(
req: ConfirmWithdrawalRequest,
): Promise<void> {
const parsedTx = parseTransactionIdentifier(req.transactionId);
+ const selectedExchange = req.exchangeBaseUrl;
+ const instructedAmount = Amounts.parseOrThrow(req.amount);
+
if (parsedTx?.tag !== TransactionType.Withdrawal) {
throw Error("invalid withdrawal transaction ID");
}
@@ -2998,7 +3051,6 @@ export async function confirmWithdrawal(
throw Error("not a bank integrated withdrawal");
}
- const selectedExchange = req.exchangeBaseUrl;
const exchange = await fetchFreshExchange(wex, selectedExchange);
const talerWithdrawUri = withdrawalGroup.wgInfo.bankInfo.talerWithdrawUri;
@@ -3006,30 +3058,36 @@ export async function confirmWithdrawal(
/**
* The only reason this could be undefined is because it is an old wallet
- * database before adding the wireType field was added
+ * database before adding the prepareWithdrawal feature
*/
- let wtypes: string[];
- if (withdrawalGroup.wgInfo.bankInfo.wireTypes === undefined) {
+ let bankWireTypes: string[];
+ let bankCurrency: string;
+ if (
+ withdrawalGroup.wgInfo.bankInfo.wireTypes === undefined ||
+ withdrawalGroup.wgInfo.bankInfo.currency === undefined
+ ) {
const withdrawInfo = await getBankWithdrawalInfo(
wex.http,
talerWithdrawUri,
);
- wtypes = withdrawInfo.wireTypes;
+ bankWireTypes = withdrawInfo.wireTypes;
+ bankCurrency = withdrawInfo.currency;
} else {
- wtypes = withdrawalGroup.wgInfo.bankInfo.wireTypes;
+ bankWireTypes = withdrawalGroup.wgInfo.bankInfo.wireTypes;
+ bankCurrency = withdrawalGroup.wgInfo.bankInfo.currency;
}
const exchangePaytoUri = await getExchangePaytoUri(
wex,
selectedExchange,
- wtypes,
+ bankWireTypes,
);
const withdrawalAccountList = await fetchWithdrawalAccountInfo(
wex,
{
exchange,
- instructedAmount: Amounts.parseOrThrow(req.amount),
+ instructedAmount,
},
wex.cancellationToken,
);
@@ -3040,23 +3098,34 @@ export async function confirmWithdrawal(
);
const initalDenoms = await getInitialDenomsSelection(
wex,
- req.exchangeBaseUrl,
- Amounts.parseOrThrow(req.amount),
+ exchange.exchangeBaseUrl,
+ instructedAmount,
req.forcedDenomSel,
);
+ let pending = false;
await ctx.transition({}, async (rec) => {
if (!rec) {
return TransitionResult.stay();
}
switch (rec.status) {
+ case WithdrawalGroupStatus.PendingWaitConfirmBank: {
+ pending = true;
+ return TransitionResult.stay();
+ }
+ case WithdrawalGroupStatus.AbortedOtherWallet: {
+ throw TalerError.fromDetail(
+ TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
+ {},
+ );
+ }
case WithdrawalGroupStatus.DialogProposed: {
- rec.exchangeBaseUrl = req.exchangeBaseUrl;
+ rec.exchangeBaseUrl = exchange.exchangeBaseUrl;
rec.instructedAmount = req.amount;
+ rec.restrictAge = req.restrictAge;
rec.denomsSel = initalDenoms;
rec.rawWithdrawalAmount = initalDenoms.totalWithdrawCost;
rec.effectiveWithdrawalAmount = initalDenoms.totalCoinValue;
- rec.restrictAge = req.restrictAge;
rec.wgInfo = {
withdrawalType: WithdrawalRecordType.BankIntegrated,
@@ -3067,19 +3136,50 @@ export async function confirmWithdrawal(
confirmUrl: confirmUrl,
timestampBankConfirmed: undefined,
timestampReserveInfoPosted: undefined,
- wireTypes: wtypes,
+ wireTypes: bankWireTypes,
+ currency: bankCurrency,
},
};
-
+ pending = true;
rec.status = WithdrawalGroupStatus.PendingRegisteringBank;
return TransitionResult.transition(rec);
}
- default:
- throw Error("unable to confirm withdrawal in current state");
+ default: {
+ throw Error(
+ `unable to confirm withdrawal in current state: ${rec.status}`,
+ );
+ }
}
});
await wex.taskScheduler.resetTaskRetries(ctx.taskId);
+
+ wex.ws.notify({
+ type: NotificationType.BalanceChange,
+ hintTransactionId: ctx.transactionId,
+ });
+
+ const res = await wex.db.runReadWriteTx(
+ {
+ storeNames: ["exchanges"],
+ },
+ async (tx) => {
+ const r = await internalPerformExchangeWasUsed(
+ wex,
+ tx,
+ exchange.exchangeBaseUrl,
+ withdrawalGroup,
+ );
+ return r;
+ },
+ );
+ if (res.exchangeNotif) {
+ wex.ws.notify(res.exchangeNotif);
+ }
+
+ if (pending) {
+ await waitWithdrawalRegistered(wex, ctx);
+ }
}
/**
@@ -3104,112 +3204,57 @@ export async function acceptWithdrawalFromUri(
): 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,
- );
- },
+ `preparing withdrawal via ${req.talerWithdrawUri}, canonicalized selected exchange ${selectedExchange}`,
);
- 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 p = await prepareBankIntegratedWithdrawal(wex, {
+ talerWithdrawUri: req.talerWithdrawUri,
+ });
- let amount: AmountJson;
- if (withdrawInfo.amount == null) {
+ let amount: AmountString;
+ if (p.info.amount == null) {
if (req.amount == null) {
throw Error(
"amount required, as withdrawal operation has flexible amount",
);
}
- amount = Amounts.parseOrThrow(req.amount);
+ amount = req.amount as AmountString;
} else {
- if (
- req.amount != null &&
- Amounts.cmp(req.amount, withdrawInfo.amount) != 0
- ) {
+ if (req.amount != null && Amounts.cmp(req.amount, p.info.amount) != 0) {
throw Error(
"mismatched amount, amount is fixed by bank but client provided different amount",
);
}
- amount = withdrawInfo.amount;
+ amount = p.info.amount;
}
- const withdrawalAccountList = await fetchWithdrawalAccountInfo(
- wex,
- {
- exchange,
- instructedAmount: amount,
- },
- CancellationToken.CONTINUE,
- );
-
- const withdrawalGroup = await internalCreateWithdrawalGroup(wex, {
- 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,
- },
- },
+ logger.info(`confirming withdrawal with tx ${p.transactionId}`);
+ await confirmWithdrawal(wex, {
+ amount: Amounts.stringify(amount),
+ exchangeBaseUrl: selectedExchange,
+ transactionId: p.transactionId,
restrictAge: req.restrictAge,
forcedDenomSel: req.forcedDenomSel,
- reserveStatus: WithdrawalGroupStatus.PendingRegisteringBank,
- });
-
- const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
-
- const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId);
-
- wex.ws.notify({
- type: NotificationType.BalanceChange,
- hintTransactionId: ctx.transactionId,
});
- wex.taskScheduler.startShepherdTask(ctx.taskId);
+ const newWithdrawralGroup = await wex.db.runReadOnlyTx(
+ { storeNames: ["withdrawalGroups"] },
+ async (tx) => {
+ return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
+ req.talerWithdrawUri,
+ );
+ },
+ );
- await waitWithdrawalRegistered(wex, ctx);
+ checkDbInvariant(
+ newWithdrawralGroup !== undefined,
+ "withdrawal don't exist after confirm",
+ );
return {
- reservePub: withdrawalGroup.reservePub,
- confirmTransferUrl: withdrawInfo.confirmTransferUrl,
- transactionId: ctx.transactionId,
+ reservePub: newWithdrawralGroup.reservePub,
+ confirmTransferUrl: p.info.confirmTransferUrl,
+ transactionId: p.transactionId,
};
}
@@ -3434,7 +3479,7 @@ export async function createManualWithdrawal(
);
const withdrawalGroup = await internalCreateWithdrawalGroup(wex, {
- amount: Amounts.jsonifyAmount(req.amount),
+ amount: amount,
wgInfo: {
withdrawalType: WithdrawalRecordType.BankManual,
exchangeCreditAccounts: withdrawalAccountsList,