commit 100c07c84536394fd234136cdb297f91a7a927fa
parent eaccc56c2251a0e97242f5ee5c7850d98ad736b5
Author: Florian Dold <dold@taler.net>
Date: Thu, 3 Sep 2026 15:59:03 +0200
wallet-core: drop a refused refund handoff in a melt conflict
A refund the exchange refuses for good was retried on every melt
conflict, so the history-based recovery behind it was never reached
and the refresh stayed pending.
Diffstat:
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts
@@ -1183,13 +1183,33 @@ async function handleRefreshMeltConflict(
delete rg.refundRequests[coinIndex];
await tx.upsertRefreshGroup(rg);
});
- } else {
- // FIXME: Store the error somewhere in the DB?
+ return;
+ }
+ if (
+ refundResp.case === HttpStatusCode.RequestTimeout ||
+ refundResp.case === HttpStatusCode.TooManyRequests ||
+ refundResp.case >= HttpStatusCode.InternalServerError
+ ) {
logger.warn(
`Refund request during refresh failed: ${j2s(refundResp.detail)}`,
);
+ return;
}
- return;
+ // The exchange refused the refund for good (it never saw the deposit,
+ // or already paid it out), so the refund cannot explain the conflict
+ // and would fail the same way on every retry. Drop the handoff and
+ // let the coin history decide what is left to melt.
+ logger.warn(
+ `Refund handoff during refresh refused: ${j2s(refundResp.detail)}`,
+ );
+ await ctx.wex.runWalletDbTx(async (tx) => {
+ const rg = await tx.getRefreshGroup(refreshGroup.refreshGroupId);
+ if (!rg || rg.operationStatus != RefreshOperationStatus.Pending) {
+ return;
+ }
+ delete rg.refundRequests[coinIndex];
+ await tx.upsertRefreshGroup(rg);
+ });
}
const historySig = await ctx.wex.cryptoApi.signCoinHistoryRequest({