taler-typescript-core

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

commit d45d8a7b52b4d3d0b3ec9caf9f8b72932abfb563
parent ef37c7b7569bf0fab38926d10ceeabe812a5fb02
Author: Florian Dold <dold@taler.net>
Date:   Wed, 22 Jul 2026 13:35:06 +0200

wallet: settle a push debit as done when its purse was merged

A conflict on the purse deletion means the purse was decided, which covers
both the recipient having merged it and the purse having been refunded.  The
purse itself distinguishes the two.  When the recipient has the money the
transaction completed, so it must not be reported as aborted and the coins
that paid for it must not be refreshed.

Diffstat:
Mpackages/taler-wallet-core/src/pay-peer-push-debit.ts | 29+++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts @@ -889,13 +889,30 @@ async function processPeerPushDebitAbortingDeletePurse( // Not found => Previous deletion succeeded. // FIXME: Look at response error code more closely break; - case HttpStatusCode.Conflict: - // FIXME: Unclear what we do here. - // Either the purse got merged or expired. - // If it expired, we must refresh. - // If not, unclear how we got here - // in the first place. + case HttpStatusCode.Conflict: { + // The purse was already decided, which the exchange reports the same way + // whether it was merged or refunded. Only the purse itself tells us + // which: if the recipient merged it, the money is theirs and the coins + // must not be refreshed. + const statusResp = await exchangeClient.getPurseStatusAtMerge(pursePub); + if (statusResp.case === "ok" && isPurseMerged(statusResp.body)) { + await wex.runWalletDbTx(async (tx) => { + const [rec, h] = await ctx.getRecordHandle(tx); + if (!rec) { + return; + } + if (rec.status !== PeerPushDebitStatus.AbortingDeletePurse) { + return; + } + // The abort did not happen, so its reason must not be reported. + delete rec.abortReason; + rec.status = PeerPushDebitStatus.Done; + await h.update(rec); + }); + return TaskRunResult.finished(); + } break; + } case HttpStatusCode.Forbidden: await ctx.failTransaction(peerPushInitiation.status, resp.detail); return TaskRunResult.finished();