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

+32
View File
@@ -18,6 +18,7 @@ const {
listOpenStorageTasks,
completeContractEvent,
updateContractEvent,
eventIdFor,
} = await import('../server/services/contractEvents.js')
test('Ereignis speichern und lesen (details als JSON)', async () => {
@@ -163,3 +164,34 @@ test('Speicher-Verringerung ohne Zustimmung (consent: null) speichert keine Zust
assert.equal(doc.consentUserAgent ?? null, null)
assert.equal(doc.termsVersion ?? null, null)
})
// F05 (Fix 1): feste Ereignis-IDs muessen Appwrites Regeln fuer Dokument-IDs einhalten (hoechstens
// 36 Zeichen) - 'abschluss-' + eine echte Stripe-Abo-ID (sub_ + 24) waeren 38. Deshalb
// '<typ>-' + sha256 des Schluessels, auf 36 Zeichen gekuerzt; gleicher Schluessel -> gleiche ID.
test('eventIdFor: 36 Zeichen nach Appwrite-Regeln, deterministisch', () => {
const subId = 'sub_1SD6vPRxAbCdEfGhIjKlMnOp'
const id = eventIdFor('abschluss', subId)
assert.equal(id.length, 36)
assert.match(id, /^abschluss-[0-9a-f]{26}$/)
assert.equal(id, eventIdFor('abschluss', subId))
assert.notEqual(id, eventIdFor('abschluss', 'sub_1SD6vPRxAbCdEfGhIjKlMnOq'))
// Laengerer Typ -> kuerzerer Hash, immer 36 Zeichen
assert.equal(eventIdFor('kuendigung', subId).length, 36)
assert.match(eventIdFor('kuendigung', subId), /^kuendigung-[0-9a-f]{25}$/)
assert.throws(() => eventIdFor('abschluss', ''), /Schl/)
assert.throws(() => eventIdFor('Abschluss!', subId), /Typ/)
})
test('recordContractEvent lehnt IDs ab, die Appwrite nicht annimmt', async () => {
const base = { customerId: 'c6', subscriptionId: 'sub_6', type: 'abschluss', productKey: 'hosting' }
await assert.rejects(
recordContractEvent({ ...base, id: 'abschluss-sub_1SD6vPRxAbCdEfGhIjKlMnOp' }),
(e) => e.status === 400 && /36/.test(e.message)
)
await assert.rejects(recordContractEvent({ ...base, id: '-abschluss' }), (e) => e.status === 400)
await assert.rejects(recordContractEvent({ ...base, id: 'abschluss/1' }), (e) => e.status === 400)
assert.equal((await listContractEvents('c6')).length, 0)
// Gueltige feste ID (aus eventIdFor) wird gespeichert
const ok = await recordContractEvent({ ...base, id: eventIdFor('abschluss', 'sub_1SD6vPRxAbCdEfGhIjKlMnOp') })
assert.equal(ok.$id.length, 36)
})