Funktionen: Stripe-Sitzungs-ID bis 128 Zeichen, keine Rohmeldungen beim Bestellen

Live-Fehler: cs_live-Sitzungs-IDs (~66 Zeichen) passten nicht in das Feld
portalFeatures.stripeSessionId (64), die Bestellung brach nach dem Anlegen
der Stripe-Sitzung ab und der Kunde sah die Appwrite-Meldung.
- Setup-Skript: Feld 128 Zeichen, bestehendes Feld wird per ensureStringSize vergroessert
- Fehler von Appwrite oder unerwartete Fehler: Log + verstaendlicher Hinweis

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-09-30 17:58:44 +00:00
1 parent 4b7f73abba
commit b45108c1de
4 files changed
+54 -7

No files matched your search

+21 -1
View File
@@ -63,6 +63,24 @@ async function waitForAttributes(collectionId, keys, maxSec = 60) {
const col = (id) => `/databases/${databaseId}/collections/${id}`
/**
* Vergroessert ein bestehendes String-Attribut, falls es kleiner als size ist (ensureCollection
* ueberspringt vorhandene Attribute und prueft ihre Groesse nicht). Verkleinern tut es nie.
*/
async function ensureStringSize(collectionId, key, size) {
const attr = await aw(`${col(collectionId)}/attributes/${key}`)
if (attr.size >= size) {
console.log(`= ${collectionId}.${key} Groesse ${attr.size} (ok)`)
return
}
await aw(`${col(collectionId)}/attributes/string/${key}`, {
method: 'PATCH',
body: { required: Boolean(attr.required), default: attr.default ?? null, size },
})
await waitForAttributes(collectionId, [key])
console.log(`+ ${collectionId}.${key} Groesse ${attr.size} -> ${size}`)
}
async function ensureCollection(id, name, attributes, indexes) {
// Keine Dokument-Permissions: Zugriff ausschliesslich ueber den Server-API-Key
await ensure(`Collection ${id}`, () =>
@@ -260,7 +278,7 @@ await ensureCollection(
{ type: 'string', key: 'state', size: 16 },
{ type: 'string', key: 'siteKey', size: 16 },
{ type: 'string', key: 'ticketId', size: 64 },
{ type: 'string', key: 'stripeSessionId', size: 64 },
{ type: 'string', key: 'stripeSessionId', size: 128 },
{ type: 'string', key: 'stripeInvoiceId', size: 64 },
{ type: 'string', key: 'invoiceUrl', size: 500 },
{ type: 'datetime', key: 'orderedAt' },
@@ -279,6 +297,8 @@ await ensureCollection(
{ key: 'idx_session', type: 'key', attributes: ['stripeSessionId'] },
]
)
// Live-Fehler 30.09.2026: cs_live-Sitzungs-IDs (~66 Zeichen) passten nicht in 64
await ensureStringSize('portalFeatures', 'stripeSessionId', 128)
// News-Pinnwand: Beitraege je Projekt
await ensureCollection(
+11 -6
View File
@@ -8,7 +8,8 @@ import {
DISABLED_MESSAGE,
assertOwnProject,
backfillInvoiceLinks,
getFeatureDoc, listCustomerFeatures,
getFeatureDoc,
listCustomerFeatures,
markOrdered,
runActivation,
} from '../services/siteFeatures.js'
@@ -16,9 +17,13 @@ import {
const router = Router()
const DAY_MS = 24 * 60 * 60 * 1000
function fail(res, err) {
const status = err.status >= 400 && err.status < 600 ? err.status : 500
return res.status(status).json({ error: err.message || 'Interner Serverfehler' })
// Eigene Fehler (httpError) tragen einen Text fuer den Kunden. Fehler von Appwrite (err.type) oder
// unerwartete Fehler werden nur geloggt; der Kunde sieht einen verstaendlichen Hinweis statt der Rohmeldung.
function fail(res, err, fallback) {
const own = err.status >= 400 && err.status < 500 && !err.type
if (own) return res.status(err.status).json({ error: err.message })
console.error('[funktionen]', err.status || '', err.type || '', err.message)
return res.status(500).json({ error: fallback })
}
// Zustaende der Funktionen je Projekt; geschlossene Einrichtungs-Tickets dieses Kunden werden vorher freigeschaltet
@@ -34,7 +39,7 @@ router.get('/', requireSession, async (req, res) => {
consentText: config.funktionen.consentText,
})
} catch (err) {
fail(res, err)
fail(res, err, 'Die Funktionen konnten gerade nicht geladen werden. Bitte lade die Seite gleich noch einmal.')
}
})
@@ -101,7 +106,7 @@ router.post('/news/checkout', requireSession, async (req, res) => {
})
res.json({ url: session.url })
} catch (err) {
fail(res, err)
fail(res, err, 'Die Bestellung konnte gerade nicht gestartet werden. Bitte versuch es später noch einmal oder schreib uns im Chat.')
}
})
+10
View File
@@ -395,3 +395,13 @@ test('ticket_details_nennen_die_anleitung', () => {
const doc = [...aw.docs('workorders').values()].find((t) => String(t.topic).startsWith('News-Pinnwand einbauen'))
assert.ok(doc.details.includes('Anleitung: docs/funktionen/news-einbau.md (Repo Webklar-Kundenbereich)'))
})
// Live-Fehler 30.09.2026: Appwrite lehnte die (zu lange) Sitzungs-ID ab, der Kunde sah die rohe Meldung
test('checkout_datenbankfehler_ohne_rohe_meldung', async () => {
aw.seed('websiteProjects', { $id: 'p-db', customerId: 'c1', projectName: 'Eisdiele' })
aw.failNext({ collection: 'portalFeatures', method: 'PATCH', status: 400, message: 'Invalid document structure: Attribute "stripeSessionId" has invalid type.' })
const res = await checkout('p-db')
assert.equal(res.status, 500, JSON.stringify(res.body))
assert.doesNotMatch(res.body.error, /Invalid document structure|stripeSessionId/)
assert.match(res.body.error, /Bestellung konnte gerade nicht gestartet werden/)
})
+12
View File
@@ -299,3 +299,15 @@ test('schema_funktionen_news', async () => {
assert.ok(news.includes(k), `newsPosts.${k}`)
}
})
// Stripe-Checkout-IDs im Live-Modus sind ~66 Zeichen lang (cs_live_ + 58); 64 war zu knapp
test('schema_stripe_session_id_lang_genug', async () => {
const { readFileSync } = await import('node:fs')
const src = readFileSync(new URL('../scripts/setup-abo-collections.mjs', import.meta.url), 'utf8')
const block = src.slice(src.indexOf("'portalFeatures',"))
const m = block.match(/key: 'stripeSessionId', size: (\d+)/)
assert.ok(m, 'stripeSessionId im portalFeatures-Block')
assert.ok(Number(m[1]) >= 128, `stripeSessionId size ${m[1]} < 128`)
// bestehendes Feld in Produktion wird per Groessen-Update angehoben
assert.match(src, /ensureStringSize\('portalFeatures', 'stripeSessionId', 128\)/)
})