Buchungskalender: Verdrahtung absichern (Outbox, Shop-Webhook, Session)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a68baf10d9
commit
d7b5b98187
6 files changed
+45
-4
No files matched your search
@@ -2,6 +2,10 @@
|
||||
|
||||
Verbindlich für Strang 2 (Widget und Buchungsseite, bauen gegen die öffentliche Schnittstelle) und Strang 3 (Portal, baut gegen die Kunden-API). Task 6 bis 8 erfüllen ihn. Fehlerform überall `{ fehler: code, felder?: {feld: text} }`.
|
||||
|
||||
## Livegang-Reihenfolge
|
||||
|
||||
Vor dem Anheben von `UNTERKUNFT_ENABLED` über `off` zuerst `scripts/unterkunft-setup.mjs` (legt die Collections samt Index `idx_account` an) und das termine-Setup (Attribut `anhang` der Mail-Warteschlange) auf Produktion laufen lassen.
|
||||
|
||||
## Öffentlich (`/api/public/unterkunft/v1`)
|
||||
|
||||
- `GET /:siteKey` → `{ aktiv:true, anbieter:{ name, ort, datenschutzUrl }, akzentfarbe, formToken, unterkuenfte:[{ id, name, maxPersonen, mindestNaechte, checkIn, checkOut }] }` oder `{ aktiv:false }`.
|
||||
|
||||
@@ -43,7 +43,12 @@ async function verarbeite(event, now) {
|
||||
if (event.type === 'account.updated') {
|
||||
await kontoAktualisiert(accountId || objekt.id, objekt, now)
|
||||
// dasselbe Konto kann auch im Buchungskalender haengen
|
||||
await kontoAktualisiertUk(accountId || objekt.id, objekt, now)
|
||||
try {
|
||||
await kontoAktualisiertUk(accountId || objekt.id, objekt, now)
|
||||
} catch (err) {
|
||||
// ein Fehler im Buchungskalender darf die Antwort fuer den Shop nie beeinflussen (ohne Kundendaten im Log)
|
||||
console.warn(`[shop] Webhook account.updated: Buchungskalender nicht aktualisiert: ${err?.name}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!accountId) return
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Router } from 'express'
|
||||
import { requireSession } from '../middleware/session.js'
|
||||
|
||||
/**
|
||||
* Kunden-API des Buchungskalenders: /api/unterkunft (Session, Projekt per ?projectId=; Vertrag: docs/funktionen/unterkunft-api.md).
|
||||
@@ -6,5 +7,7 @@ import { Router } from 'express'
|
||||
* und Stripe; Task 11 die Anfrage-Funktion (/anfrage-funktion).
|
||||
*/
|
||||
const router = Router()
|
||||
// Jede Route hier braucht eine Sitzung (Task 8 darf das nicht vergessen); Kunden-Pruefung je Route wie in routes/shop.js
|
||||
router.use(requireSession)
|
||||
|
||||
export default router
|
||||
@@ -225,7 +225,8 @@ export async function einreihen(
|
||||
text: mail.text,
|
||||
ics: ics || '',
|
||||
icsMethod: ics ? icsMethod : '',
|
||||
anhang: anhang || '',
|
||||
// nur wenn gesetzt: das Attribut gibt es in Appwrite erst nach erneutem termine-setup, unbekannte Felder wuerden abgelehnt
|
||||
...(anhang ? { anhang } : {}),
|
||||
status: 'wartet',
|
||||
versuche: 0,
|
||||
naechsterVersuch: iso(nowMs),
|
||||
|
||||
@@ -1283,7 +1283,7 @@ test('einreihen: optionaler Anhang-Verweis wird gespeichert (Senden unverändert
|
||||
const mit = await einreihen(eintrag({ bookingId: 'b_anhang', anhang: 'uk-rechnung:r_123' }), JETZT)
|
||||
assert.equal((await lies(mit.$id)).anhang, 'uk-rechnung:r_123')
|
||||
const ohne = await einreihen(eintrag({ bookingId: 'b_ohne' }), JETZT)
|
||||
assert.equal((await lies(ohne.$id)).anhang, '')
|
||||
assert.equal(Object.hasOwn(await lies(ohne.$id), 'anhang'), false, 'ohne Angabe wird das Feld nicht geschrieben')
|
||||
await assert.rejects(() => einreihen(eintrag({ anhang: 'x'.repeat(501) }), JETZT), TypeError)
|
||||
await warteAufVersand()
|
||||
assert.equal(tr.senke.sent.length, 2, 'Versand nicht verändert')
|
||||
|
||||
@@ -29,7 +29,8 @@ const jobs = await import('../server/services/unterkunft/jobs.js')
|
||||
const { default: publicRoutes } = await import('../server/routes/publicUnterkunft.js')
|
||||
const { default: webhookRoutes } = await import('../server/routes/shopWebhook.js')
|
||||
const W = await import('../server/routes/unterkunftWebhook.js')
|
||||
const app = await startTestApp({ '/api/public/unterkunft/v1': publicRoutes, '/api/shop/stripe-webhook': webhookRoutes })
|
||||
const { default: ukRoutes } = await import('../server/routes/unterkunft.js')
|
||||
const app = await startTestApp({ '/api/unterkunft': ukRoutes, '/api/public/unterkunft/v1': publicRoutes, '/api/shop/stripe-webhook': webhookRoutes })
|
||||
after(async () => {
|
||||
await app.close()
|
||||
await aw.close()
|
||||
@@ -284,3 +285,30 @@ test('Webhook: account.updated speichert den Status auch in ukEinstellungen mit
|
||||
assert.equal(await W.kontoAktualisiertUk('acct_unbekannt', konto, T0), 0)
|
||||
assert.equal(await W.kontoAktualisiertUk('kein-konto', konto, T0), 0)
|
||||
})
|
||||
|
||||
test('Kunden-API: ohne Sitzung 401 (Router-weiter Guard)', async () => {
|
||||
assert.equal((await rawGet('/api/unterkunft/status')).status, 401)
|
||||
})
|
||||
|
||||
test('Webhook: Shop-Erstattung ohne Uk-Metadaten geht nicht an Uk', async () => {
|
||||
config.shop.connectWebhookSecret = SECRET
|
||||
const gesehen = []
|
||||
W.setzeUkEreignisHandlerFuerTests(async (event) => void gesehen.push(event.type))
|
||||
const r = await postWebhook(ereignis('charge.refunded', { id: 'ch_9', payment_intent: null, metadata: { webklar: 'shop' } }), { path: PFAD, secret: SECRET })
|
||||
assert.equal(r.status, 200)
|
||||
assert.deepEqual(gesehen, [])
|
||||
})
|
||||
|
||||
test('Webhook: ein Fehler im Uk-Teil von account.updated lässt den Shop-Teil gelten und die Antwort 200', async () => {
|
||||
await leere()
|
||||
config.shop.connectWebhookSecret = SECRET
|
||||
config.unterkunft.mode = 'on'
|
||||
aw.seed('shopConfig', { $id: 'f_shop1', customerId: KUNDE, projectId: PROJEKT, siteKey: SITE_KEY, stripeAccountId: ACCOUNT, stripeStatus: JSON.stringify({ charges_enabled: false }), einstellungen: '{}', rechtstexte: '{}', version: 1 })
|
||||
// Uk-Abfrage scheitert: Index fehlt (400) bzw. Collection-Fehler
|
||||
mock.method(console, 'warn', () => {})
|
||||
aw.failNext({ collection: config.collections.ukEinstellungen, method: 'GET', status: 500 })
|
||||
const konto = { id: ACCOUNT, charges_enabled: true, payouts_enabled: true, details_submitted: true, requirements: { currently_due: [], past_due: [], disabled_reason: null } }
|
||||
const r = await postWebhook(ereignis('account.updated', konto), { path: PFAD, secret: SECRET })
|
||||
assert.equal(r.status, 200)
|
||||
assert.equal(JSON.parse(aw.docs('shopConfig').get('f_shop1').stripeStatus).charges_enabled, true)
|
||||
})
|
||||
Reference in new issue
Block a user