- webhook-routes.js: POST /api/webhooks/payment — signature-verified, idempotent, always 200
once verified (a forged/invalid signature is a clean quiet 400)
- integration-routes.js: POST /api/integrations/registrations/:id/agreements (closes Phase 8's
gap — agreement acceptance now on the public registration contract), POST .../:id/checkout
(server-to-server, amount always server-side), GET .../:id/status (minimal safe status lookup
for the website's success page — no PII, no amount, no provider reference)
- public-offerings-routes.js: price_amount/price_currency/requires_payment in the public serializer
- metrics.js: checkout/payment/webhook/conversion/seat-released counters, all low-cardinality
- notifications.js: listForRegistration now unions by the registration's own enrollment_id too,
so the post-conversion activation notification shows up in the registration's communication
history (previously only pre-conversion notifications were visible)