Buchungskalender: Abschluss – Kleinigkeiten Server (Betrag/Waehrung, Erstattungs-Webhook, Obergrenze, Zaehler, Cache, Kommentare, Setup-Check)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 15:29:55 +00:00
1 parent 6d35674c1e
commit 1d9515823c
13 files changed
+123 -14

No files matched your search

+3 -1
View File
@@ -331,8 +331,10 @@ router.post(
const pruefung = pruefeFormToken(siteKey, b.formToken, now)
if (pruefung === 'zu_schnell') return { status: 201, body: { nummer: scheinNummer() } }
if (pruefung !== 'ok') throw fehlerMit(400, 'neu_laden')
if (!zaehle(`uk-anfrage:${siteKey}`, config.unterkunft.anfragenJeSiteStunde, STUNDE_MS, now)) throw zuViele(3600)
if (!ID_RE.test(text(b.unterkunft))) throw ungueltig({ unterkunft: 'Ungültige Unterkunft.' })
if (!istTag(b.von) || !istTag(b.bis)) throw ungueltig({ zeitraum: 'Ungültige Daten.' })
// je Website erst nach der Feldpruefung zaehlen: fehlerhafte Formulare verbrauchen das Stundenkontingent nicht
if (!zaehle(`uk-anfrage:${siteKey}`, config.unterkunft.anfragenJeSiteStunde, STUNDE_MS, now)) throw zuViele(3600)
const personen = typeof b.personen === 'string' && /^\d{1,3}$/.test(b.personen) ? Number(b.personen) : b.personen
const anfrage = await legeAnfrageAn(
ctx,
+3 -1
View File
@@ -12,7 +12,7 @@ import { istFrei } from '../services/unterkunft/belegung.js'
import { istTag, naechte } from '../services/unterkunft/datum.js'
import { anfrageAnsicht as funktionAnfrageAnsicht, stelleAnfrage, zieheZurueck } from '../services/unterkunft/anfrageFunktion.js'
import { UkFehler } from '../services/unterkunft/fehler.js'
import { ladeFuerProjekt } from '../services/unterkunft/kontext.js'
import { ladeFuerProjekt, leereCache } from '../services/unterkunft/kontext.js'
import { berechnePreis, preiseVollstaendig, summiere } from '../services/unterkunft/preis.js'
import { unterkunftFuerKunde } from '../services/unterkunft/schalter.js'
import * as sp from '../services/unterkunft/speicher.js'
@@ -330,6 +330,8 @@ router.put('/einstellungen', ...schreib, route(async (req, res) => {
if (rechnungAuftrag === false) werte.rechnungAuftragAt = null
return sp.speichereEinstellungen(ctx.featureId, werte)
})
// die oeffentliche Schnittstelle (Farbe, Firma, Online-Zahlung) merkt sich den Kontext 30 s: sofort frisch
if (ctx.siteKey) leereCache(ctx.siteKey)
res.json(await einstellungenAntwort(ctx, neu))
}))
+9 -7
View File
@@ -6,12 +6,12 @@ import { statusAusKonto } from '../services/shop/connect.js'
import { ladeFuerProjekt } from '../services/unterkunft/kontext.js'
import { unterkunftModus } from '../services/unterkunft/schalter.js'
import { aktualisiereAnfrage, ladeAnfrage, ladeEinstellungen } from '../services/unterkunft/speicher.js'
import { verbucheZahlung } from '../services/unterkunft/zahlung.js'
import { erstatteUeberzaehligeZahlung, verbucheZahlung, zahlungPasst } from '../services/unterkunft/zahlung.js'
/**
* Stripe-Ereignisse des Buchungskalenders. Kein eigener Endpunkt: routes/shopWebhook.js (Connect-Webhook, ein Endpunkt je Konto)
* leitet Ereignisse mit metadata.wkUk === '1' (Checkout-Sitzungen) oder metadata.webklar === 'unterkunft' (Erstattungen) hierher
* und ruft bei account.updated zusaetzlich kontoAktualisiertUk. Alles idempotent; Fehler => 500 (Stripe wiederholt).
* und ruft bei account.updated zusaetzlich kontoAktualisiertUk. Alles idempotent; ein Fehler bei Checkout- und Erstattungsereignissen => 500 (Stripe wiederholt), bei account.updated nur ein Log-Eintrag (shopWebhook.js).
*/
const ACCOUNT_RE = /^acct_[A-Za-z0-9]+$/
@@ -48,11 +48,12 @@ async function zahlungEingegangen(event, session, now) {
const treffer = await anfrageZu(event, session.metadata?.anfrageId)
if (!treffer) return
const { anfrage, ctx } = treffer
const brutto = anfrage.summen?.bruttoCent
if (Number.isInteger(session.amount_total) && Number.isInteger(brutto) && session.amount_total !== brutto) {
console.warn(`[unterkunft] Webhook: Betrag weicht ab (Buchung ${anfrage.nummer})`)
}
const pi = typeof session.payment_intent === 'string' ? session.payment_intent : session.payment_intent?.id
if (!zahlungPasst(anfrage, session)) {
console.warn(`[unterkunft] Webhook: Betrag oder Währung weicht ab (Buchung ${anfrage.nummer}), Zahlung wird erstattet`)
await erstatteUeberzaehligeZahlung(ctx, pi)
return
}
await verbucheZahlung(ctx, anfrage.$id, pi, { now })
}
@@ -60,7 +61,8 @@ async function erstattungGebucht(event, charge) {
const treffer = await anfrageZu(event, charge.metadata?.anfrageId)
if (!treffer) return
const { anfrage } = treffer
if (anfrage.stripePaymentIntentId && typeof charge.payment_intent === 'string' && charge.payment_intent !== anfrage.stripePaymentIntentId) return
// nur die Zahlung, die diese Buchung bezahlt hat (fremde oder ueberzaehlige Zahlungen mit gleicher anfrageId zaehlen nicht)
if (!anfrage.stripePaymentIntentId || typeof charge.payment_intent !== 'string' || charge.payment_intent !== anfrage.stripePaymentIntentId) return
const stand = charge.amount_refunded
if (!Number.isInteger(stand) || stand <= 0) return
await withLocks(['unterkunft:' + anfrage.unterkunftId], async () => {