Checkout je Funktion: Zustimmungsnachweis vor der Weiterleitung, Zustaende aus dem Register

Rest der Route POST /api/funktionen/:key/checkout unter der Sperre feature-order:<Kunde>:<Projekt>:
Zustand aus dem Register (setup/active, disabled, ordered mit offener Sitzung, "Zahlung fortsetzen"),
Stripe-Sitzung mit der Zeit des Klicks, Zustimmungsnachweis (Serverfassungen und -texte) vor markOrdered
und vor der URL. Standard-Haken belehrung mit BELEHRUNG_PLATZHALTER fuer den Terminplaner.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-04 12:38:50 +00:00
1 parent 1f6ae0f7e0
commit 7fcb268644
5 files changed
+534 -43

No files matched your search

+37 -1
View File
@@ -8,7 +8,7 @@ import { schemaKeys } from './support/schema.js'
// websiteProjects, workorders und customers legt das Setup-Skript nicht an: ohne Schema im Nachbau
const aw = await startAppwriteMock({
schema: { portalFeatures: schemaKeys('portalFeatures'), subscriptions: schemaKeys('subscriptions') },
schema: { portalFeatures: schemaKeys('portalFeatures'), subscriptions: schemaKeys('subscriptions'), featureConsents: schemaKeys('featureConsents') },
})
const st = await startFakeStripe()
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
@@ -410,3 +410,39 @@ test('checkout_datenbankfehler_ohne_rohe_meldung', async () => {
assert.doesNotMatch(res.body.error, /Invalid document structure|stripeSessionId/)
assert.match(res.body.error, /Bestellung konnte gerade nicht gestartet werden/)
})
// Task B7: auch die News-Bestellung schreibt vor der Weiterleitung einen Zustimmungsnachweis (Texte und Fassungen aus dem
// Register config.features.news statt Literale: Block F2 hebt die Fassung ohne Testbruch an)
test('news_schreibt_auch_einen_nachweis', async () => {
aw.seed('websiteProjects', { $id: 'pn1', customerId: 'c1', projectName: 'Nachweis-Laden' })
const news = config.features.news
const vor = new Date().toISOString()
const res = await checkout('pn1')
const nach = new Date().toISOString()
assert.equal(res.status, 200)
const session = sessions().at(-1)
const nachweise = [...aw.docs('featureConsents').values()].filter((d) => d.projectId === 'pn1')
assert.equal(nachweise.length, 1)
const [n] = nachweise
assert.equal(n.stripeSessionId, session.id)
assert.equal(n.featureKey, 'news')
assert.deepEqual(JSON.parse(n.texte), [
...news.consent.items.map((item) => ({ id: item.id, text: item.text, haken: Boolean(item.haken) })),
{ id: 'knopf', text: news.consent.knopf, haken: false },
])
assert.deepEqual(JSON.parse(n.fassungen), { zustimmung: news.consent.version, leistungsbeschreibung: news.leistungsbeschreibung.version })
assert.ok(n.ip && n.userAgent)
assert.equal(n.konto, 'Kunde Eins <k@example.invalid>')
// ein Zeitpunkt: Nachweis, Stripe-Metadaten und Dokument; das Dokument behaelt den Wortlaut wie bisher
const doc = await sf.getFeatureDoc('c1', 'pn1', 'news')
assert.equal(doc.consentText, config.funktionen.consentText)
assert.equal(n.acceptedAt, doc.consentAt)
assert.equal(n.acceptedAt, session.metadata.consentAt)
assert.ok(vor <= n.acceptedAt && n.acceptedAt <= nach)
// Widerrufs-Anker: nach der Zahlung zaehlt die Zeit des Klicks aus dem Nachweis, nicht die Zeit des Dokuments
aw.seed('portalFeatures', { ...aw.docs('portalFeatures').get(doc.$id), orderedAt: '2026-01-01T00:00:00.000Z', consentAt: '2026-01-02T00:00:00.000Z' })
assert.equal((await postWebhook(event('checkout.session.completed', paidSession(session.id, 'pn1')))).status, 200)
const order = (await sf.paidFeatureOrders('c1')).find((o) => o.projekt === 'Nachweis-Laden')
assert.equal(order.featureKey, 'news')
assert.equal(order.vertragAt, n.acceptedAt)
})