aboStatus.js: laeuft(customerId) (Wartungs-Abo, productKey || 'wartung', Status in ACTIVE_STATUSES), bestellbar(customerId) mit den Gruenden kein_abo, gekuendigt, nicht_aktiv in dieser Pruefreihenfolge und aboMemo() (je Aufruf und Kunde nur eine Lesung). Fail-closed: ein Lesefehler wirft, nie false oder ok:false. termine/fragebogen.js: Platzhalter bestellpruefung() mit der endgueltigen Signatur, liefert []; Block C ersetzt ihn. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
58 lines
2.6 KiB
JavaScript
58 lines
2.6 KiB
JavaScript
// KERNVERSION (Block B) – Block E baut aus
|
||
import { ACTIVE_STATUSES, getCurrentSubscription } from './stripeBilling.js'
|
||
|
||
// Abo-Baustein fuer Funktionen, die ein laufendes Wartungs-Abo voraussetzen (Terminplaner; die Analyse zieht in
|
||
// Block E nach). Fail-closed: ein Lesefehler wirft, er wird nie zu false bzw. ok:false. Wer die Fehler abfaengt,
|
||
// entscheidet selbst, was dann gilt (Bestellung ablehnen, Kunde im Listenlauf ueberspringen, nie abschalten).
|
||
|
||
// Wartungs-Abo: Altbestand ohne productKey gilt als Wartung (wie routes/abo.js productKeyOf), ein anderer
|
||
// Schluessel (hosting, mailStorage, unbekannt) nicht. Status wie im Portal: active, trialing, past_due.
|
||
function laeuftAus(sub) {
|
||
return Boolean(sub) && (sub.productKey || 'wartung') === 'wartung' && ACTIVE_STATUSES.has(sub.status)
|
||
}
|
||
|
||
// Die Gruende gelten in dieser Reihenfolge fuer alle Bloecke: kein laufendes Wartungs-Abo => kein_abo; Kuendigung
|
||
// vorgemerkt (cancelAtPeriodEnd oder cancelAt, auch bei trialing und past_due) => gekuendigt; Status nicht active
|
||
// (trialing, past_due) => nicht_aktiv.
|
||
function bestellbarAus(sub) {
|
||
if (!laeuftAus(sub)) return { ok: false, grund: 'kein_abo' }
|
||
if (sub.cancelAtPeriodEnd || sub.cancelAt) return { ok: false, grund: 'gekuendigt' }
|
||
if (sub.status !== 'active') return { ok: false, grund: 'nicht_aktiv' }
|
||
return { ok: true }
|
||
}
|
||
|
||
/** Hat der Kunde ein laufendes Wartungs-Abo? Wirft bei Lesefehler. */
|
||
export async function laeuft(customerId) {
|
||
return laeuftAus(await getCurrentSubscription(customerId))
|
||
}
|
||
|
||
/** Darf der Kunde jetzt bestellen? { ok: true } oder { ok: false, grund: 'kein_abo' | 'gekuendigt' | 'nicht_aktiv' }. Wirft bei Lesefehler. */
|
||
export async function bestellbar(customerId) {
|
||
return bestellbarAus(await getCurrentSubscription(customerId))
|
||
}
|
||
|
||
/**
|
||
* Zwischenspeicher fuer einen Lauf (eine Anfrage, ein Listenlauf): { laeuft, bestellbar } mit denselben Antworten
|
||
* wie oben, aber das Abo jedes Kunden wird je aboMemo()-Aufruf nur einmal gelesen (auch bei gleichzeitigen Fragen,
|
||
* und fuer laeuft und bestellbar gemeinsam). Ein Lesefehler wird nicht gemerkt: der naechste Aufruf liest neu.
|
||
*/
|
||
export function aboMemo() {
|
||
const gelesen = new Map()
|
||
const lade = (customerId) => {
|
||
if (!gelesen.has(customerId)) {
|
||
gelesen.set(
|
||
customerId,
|
||
getCurrentSubscription(customerId).catch((err) => {
|
||
gelesen.delete(customerId)
|
||
throw err
|
||
})
|
||
)
|
||
}
|
||
return gelesen.get(customerId)
|
||
}
|
||
return {
|
||
laeuft: async (customerId) => laeuftAus(await lade(customerId)),
|
||
bestellbar: async (customerId) => bestellbarAus(await lade(customerId)),
|
||
}
|
||
}
|