taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit c043647f7a83fe1d2e05107924b3fea3a7ff7838
parent 404af52f6ca6936335ab84ae66b31a59b47df607
Author: Florian Dold <florian@dold.me>
Date:   Wed, 15 Jul 2026 15:20:44 +0200

wallet-core: actually exclude broken coin in reselection for payments

Diffstat:
Mpackages/taler-wallet-core/src/pay-merchant.ts | 15++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts @@ -1611,6 +1611,7 @@ function setCoinSel(rec: PurchaseRecord, coinSel: PayCoinSelection): void { async function reselectCoinsTx( tx: WalletIndexedDbTransaction, ctx: PayMerchantTransactionContext, + excludeCoinPub?: string, ): Promise<void> { const p = await tx.purchases.get(ctx.proposalId); if (!p) { @@ -1643,6 +1644,12 @@ async function reselectCoinsTx( if (payCoinSelection) { for (let i = 0; i < payCoinSelection.coinPubs.length; i++) { const coinPub = payCoinSelection.coinPubs[i]; + if (excludeCoinPub != null && coinPub === excludeCoinPub) { + // Exclude the coin that the exchange reported as broken + // (e.g. double-spent) so re-selection doesn't just pick the + // same failing coin again. + continue; + } const contrib = payCoinSelection.coinContributions[i]; prevPayCoins.push({ coinPub, @@ -1755,6 +1762,8 @@ async function handleInsufficientFunds( logger.trace(`got error details: ${j2s(err)}`); + let brokenCoinPub: string | undefined; + if ( err.code === TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS ) { @@ -1774,8 +1783,8 @@ async function handleInsufficientFunds( return TaskRunResult.progress(); } - const brokenCoinPub = (exchangeReply as any).coin_pub; - logger.trace(`excluded broken coin pub=${brokenCoinPub}`); + logger.trace(`excluding broken coin pub=${brokenCoinPub}`); + brokenCoinPub = (exchangeReply as any).coin_pub; if (!brokenCoinPub) { await ctx.failTransaction(proposal.purchaseStatus, { @@ -1809,7 +1818,7 @@ async function handleInsufficientFunds( // TODO: also do token re-selection. await wex.runLegacyWalletDbTx(async (tx) => { - await reselectCoinsTx(tx, ctx); + await reselectCoinsTx(tx, ctx, brokenCoinPub); }); wex.ws.notify({