Abo-API: Nachweis-ID nach Appwrite-Regeln, Webhook bei dauerhaften Fehlern, Tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Fable 5.1 committed 2026-09-28 10:19:05 +00:00
1 parent 7df0b72019
commit 887e1e54a0
10 files changed
+409 -107

No files matched your search

+18 -15
View File
@@ -1,44 +1,47 @@
import http from 'node:http'
import express from 'express'
import cookieParser from 'cookie-parser'
import signature from 'cookie-signature'
import Stripe from 'stripe'
import { config } from '../../server/config.js'
import { sessionMiddleware } from '../../server/middleware/session.js'
/**
* Express-App für Routen-Tests: wie server/index.js mit cookieParser(SESSION_SECRET) und
* express.json samt rawBody (Stripe-Webhook), aber nur mit den Routern, die der Test übergibt:
* Express-App für Routen-Tests: wie server/index.js mit sessionMiddleware() (cookieParser mit
* SESSION_SECRET) und express.json samt rawBody (Stripe-Webhook), aber nur mit den Routern, die der
* Test übergibt:
* const app = await startTestApp({ '/api/abo': aboRoutes, '/api/portal-admin': portalAdminRoutes })
*
* Reihenfolge im Test: Nachbauten starten, useTestEnv(), dann dieses Modul und die Server-Module
* dynamisch importieren (assertTestEnv() weigert sich sonst, weil config.js die Umgebung beim
* ersten Import liest). Die Anfrage-Helfer sprechen die zuletzt gestartete App an.
* dynamisch importieren. config.js liest die Umgebung beim ersten Import; zeigt sie nicht auf die
* Nachbauten, weigert sich dieses Modul beim Laden. Die Anfrage-Helfer sprechen die zuletzt
* gestartete App an.
*/
function assertTestEnv() {
for (const name of ['APPWRITE_ENDPOINT', 'STRIPE_API_BASE']) {
for (const [name, value] of [
['APPWRITE_ENDPOINT', config.appwrite.endpoint],
['STRIPE_API_BASE', config.stripe.apiBase],
]) {
let host = null
try {
host = new URL(process.env[name] || '').hostname
host = new URL(value || '').hostname
} catch {
host = null
}
if (host !== '127.0.0.1') {
throw new Error(`testApp: ${name} zeigt nicht auf 127.0.0.1 – useTestEnv() vor dem Import aufrufen`)
throw new Error(`testApp: ${name} zeigt nicht auf 127.0.0.1 – useTestEnv() muss vor dem ersten Import von config.js laufen`)
}
}
}
assertTestEnv()
const cookieName = () => process.env.SESSION_COOKIE_NAME || 'webklar_portal_session'
const sessionSecret = () => process.env.SESSION_SECRET || ''
/**
* Signierter Session-Cookie wie setPortalSession (cookie-parser: Praefix "s:" + cookie-signature).
* Kunde: { customerId, appwriteUserId }. Admin: { role: 'admin', appwriteUserId, name, email }.
*/
export function sessionCookie(session) {
const value = `s:${signature.sign(JSON.stringify(session), sessionSecret())}`
return `${cookieName()}=${encodeURIComponent(value)}`
const value = `s:${signature.sign(JSON.stringify(session), config.sessionSecret)}`
return `${config.cookieName}=${encodeURIComponent(value)}`
}
let baseUrl = null
@@ -47,7 +50,7 @@ let baseUrl = null
export async function startTestApp(routers = {}) {
const app = express()
app.set('trust proxy', 1)
app.use(cookieParser(sessionSecret()))
app.use(sessionMiddleware())
app.use(
express.json({
limit: '2mb',
@@ -110,7 +113,7 @@ export const post = (path, body, opts = {}) => request('POST', path, { ...opts,
const webhookSigner = new Stripe('sk_test_fake')
/** Stripe-Ereignis signiert an den Webhook schicken (F25) -> { status, body }. */
export function postWebhook(event, { path = '/api/abo/webhook', secret = process.env.STRIPE_WEBHOOK_SECRET || 'whsec_test' } = {}) {
export function postWebhook(event, { path = '/api/abo/webhook', secret = config.stripe.webhookSecret || 'whsec_test' } = {}) {
const payload = JSON.stringify(event)
const stripeSignature = webhookSigner.webhooks.generateTestHeaderString({ payload, secret })
return request('POST', path, { body: payload, headers: { 'stripe-signature': stripeSignature } })