From 95cbd473d078132e9db6ae471a1174f42ac236f7 Mon Sep 17 00:00:00 2001 From: Kenso Grimm Date: Wed, 7 Oct 2026 14:53:36 +0000 Subject: [PATCH] =?UTF-8?q?Buchungskalender:=20Abgleich=20=E2=80=93=20glei?= =?UTF-8?q?chzeitige=20Zweitzahlung=20wird=20erstattet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- server/services/unterkunft/zahlung.js | 6 +++++- test/unterkunftBuchung.test.js | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/server/services/unterkunft/zahlung.js b/server/services/unterkunft/zahlung.js index 7a9916a..cc092a6 100644 --- a/server/services/unterkunft/zahlung.js +++ b/server/services/unterkunft/zahlung.js @@ -111,7 +111,11 @@ export async function verbucheZahlung(ctx, anfrageId, paymentIntentId, { now = n return vorher } try { - return await markiereBezahlt(ctx, anfrageId, { via: 'stripe', paymentIntentId: pi, now }) + const bezahlt = await markiereBezahlt(ctx, anfrageId, { via: 'stripe', paymentIntentId: pi, now }) + // gleichzeitige Zweitzahlung: zwischen dem Lesen oben und der Sperre hat eine andere Zahlung gebucht (markiereBezahlt gibt dann + // die bezahlte Anfrage ohne Fehler zurueck) -> die Zahlung, die nicht gebucht wurde, voll erstatten + if (pi && bezahlt?.stripePaymentIntentId !== pi) await erstatteUeberzaehligeZahlung(ctx, pi) + return bezahlt } catch (err) { if (!(err instanceof UkFehler) || err.status !== 409) throw err const jetzt = await ladeAnfrage(anfrageId) diff --git a/test/unterkunftBuchung.test.js b/test/unterkunftBuchung.test.js index 7a9fc60..0073f39 100644 --- a/test/unterkunftBuchung.test.js +++ b/test/unterkunftBuchung.test.js @@ -384,6 +384,20 @@ test('Zahlung nach Frist-Ablauf der Sitzung/Storno ueber Rueckkehr-Abgleich: ebe assert.equal(erstattungen('pi_sp2').length, 1) }) +test('Gleichzeitige Zweitzahlung (beide lesen noch gebucht): genau eine wird gebucht, die andere erstattet', async () => { + const f = await onlineGebucht('2026-11-10', '2026-11-12') + st.setPaymentIntent(ACCOUNT, 'pi_x', { amount: 20000 }) + st.setPaymentIntent(ACCOUNT, 'pi_y', { amount: 20000 }) + await Promise.all([zahlung.verbucheZahlung(f.ctx, f.id, 'pi_x', { now: NOW }), zahlung.verbucheZahlung(f.ctx, f.id, 'pi_y', { now: NOW })]) + const a = await sp.ladeAnfrage(f.id) + assert.equal(a.status, 'bezahlt') + const gebucht = a.stripePaymentIntentId + const andere = gebucht === 'pi_x' ? 'pi_y' : 'pi_x' + assert.equal(erstattungen(gebucht).length, 0) + assert.equal(erstattungen(andere).length, 1) + assert.equal(erstattungen(andere)[0].metadata?.webklar ?? 'unterkunft', 'unterkunft') +}) + test('Zweite Zahlung auf bereits bezahlte Buchung (andere Zahlung): automatisch erstattet; dieselbe Zahlung nicht', async () => { const f = await onlineGebucht('2026-11-10', '2026-11-12') st.setPaymentIntent(ACCOUNT, 'pi_a', { amount: 20000 })