// 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)), } }