Shop Review A M9: Mindestbetrag 0,50 EUR (Stripe) und Sitzungsende direkt vor dem Stripe-Aufruf nachfuehren

berechne lehnt Gesamtbetraege unter 0,50 EUR mit dem Code mindestbetrag und einem Kaeufertext ab. legeBestellungAn schiebt expires_at um die seit Beginn vergangenen ganzen Sekunden (Warten auf Sperren) weiter und fuehrt ablaufAt auf denselben Wert nach, damit Stripes 30-Minuten-Minimum sicher bleibt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 09:35:17 +00:00
1 parent cb6291410d
commit d4b9e6a5b9
5 files changed
+55 -4

No files matched your search

+1 -1
View File
@@ -466,7 +466,7 @@ const BESTELL_TOKEN_RE = /^[A-Za-z0-9_-]{16,128}$/
const GESCHLOSSEN_TEXT = 'Der Shop ist gerade geschlossen.'
const GESCHLOSSEN = () => fehlerMit(409, { _: GESCHLOSSEN_TEXT }, { code: 'geschlossen' })
// Fehlercodes aus berechne/legeBestellungAn, die Kaeufer sehen duerfen (Stripe-Fehler haben ebenfalls einen String-code)
const KAEUFER_CODES = new Set(['geschlossen', 'leer', 'nicht_verfuegbar', 'menge', 'variante', 'ausverkauft', 'lieferart', 'land', 'nur_abholung', 'mindestbestellwert', 'zu_viele', 'zu_viele_offen', 'zahlung_nicht_moeglich'])
const KAEUFER_CODES = new Set(['geschlossen', 'leer', 'nicht_verfuegbar', 'menge', 'variante', 'ausverkauft', 'lieferart', 'land', 'nur_abholung', 'mindestbestellwert', 'mindestbetrag', 'zu_viele', 'zu_viele_offen', 'zahlung_nicht_moeglich'])
const KONFLIKT_CODES = new Set(['ausverkauft', 'nicht_verfuegbar', 'nur_abholung'])
/** Artikel-Map fuer berechne: id => normalisiertes Produkt (aus dem JSON-Text), aktiv aus Spalte und JSON (wie bei legeBestellungAn). */
+13 -1
View File
@@ -257,6 +257,7 @@ export async function legeBestellungAn(ctx, eingabe, { now = new Date(), ipHash
const geschlossen = () => fehler(409, 'geschlossen', GESCHLOSSEN_TEXT)
if (!shop.stripeAccountId) throw geschlossen()
const jetzt = new Date(now)
const start = performance.now()
const { positionen, lieferart, land } = istObjekt(eingabe) ? eingabe : {}
// 1. Pruefen, rechnen und reservieren: alles unter einer Sperre je Shop, damit der letzte Artikel nur einmal weggeht
@@ -329,7 +330,18 @@ export async function legeBestellungAn(ctx, eingabe, { now = new Date(), ipHash
})
// 2. Kasse bei Stripe starten (ausserhalb der Sperre: die offene Bestellung haelt den Bestand schon)
const { orderId, token, bestellnummer, daten, ergebnis, produkte, sessionEnde } = angelegt
const { orderId, token, bestellnummer, daten, ergebnis, produkte } = angelegt
// Ende der Sitzung direkt vor dem Aufruf nachfuehren: die Zeit seit Beginn (Warten auf Sperren, Lesen) ganzer Sekunden schiebt es
// weiter, damit Stripes Minimum von 30 Minuten sicher bleibt (30 s Reserve decken den Rest); ablaufAt folgt demselben Wert
const sessionEnde = angelegt.sessionEnde + Math.floor((performance.now() - start) / 1000)
if (sessionEnde !== angelegt.sessionEnde) {
try {
await updateDocument(col(), orderId, { ablaufAt: new Date(sessionEnde * 1000).toISOString() })
} catch {
await storniere(orderId, daten, 'stripe_fehler', jetzt).catch(() => {})
throw fehler(503, 'zahlung_nicht_moeglich', ZAHLUNG_TEXT)
}
}
const rueck = pruefeRueckUrl(eingabe?.rueckUrl, ctx)
const metadata = { wkShop: '1', shopId: shop.id, orderId, bestellnummer }
const parameter = (zahlungsarten) => ({
+3
View File
@@ -11,6 +11,8 @@ const MAX_MENGE = 99
const KLEINUNTERNEHMER_HINWEIS = 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.'
const istObjekt = (wert) => wert !== null && typeof wert === 'object' && !Array.isArray(wert)
/** Stripe nimmt Zahlungen in Euro erst ab 0,50 EUR an. */
const MINDESTBETRAG_CENT = 50
const fehler = (code, text, produktId) => ({ ok: false, fehler: produktId === undefined ? { code, text } : { code, text, produktId } })
/** Enthaltene USt aus Brutto, kaufmaennisch (halb auf) gerundet: brutto * satz / (100 + satz), nur mit ganzen Zahlen. */
@@ -146,6 +148,7 @@ export function berechne({ produkte, positionen, lieferart, land, einstellungen,
versandCent = kostenlos || !Number.isInteger(kosten) || kosten < 0 ? 0 : kosten
}
const gesamtCent = zwischensummeCent + versandCent
if (gesamtCent < MINDESTBETRAG_CENT) return fehler('mindestbetrag', 'Der Bestellwert muss mindestens 0,50 € betragen.')
// 5. Steuer: je Satz ueber die Summe der Positionen, Versand anteilig nach Bruttowert (Rest auf den hoechsten Satz)
let steuer = []