From e227f29bcd49e841fed128b68a7ee7c79baef622 Mon Sep 17 00:00:00 2001 From: Jennie Robinson Faber Date: Mon, 20 Apr 2026 19:34:04 +0100 Subject: [PATCH] feat(events): block self-cancel of paid registrations, add refunds policy Self-cancel endpoint now rejects paid registrations (public, series_pass, or paid member tickets) with a 403 pointing to /policies/refunds. Free and $0-member registrations still self-cancel as before. Adds the refunds policy page referenced in the error message. --- app/pages/policies/refunds.vue | 116 ++++++++++++++++++ .../events/[id]/cancel-registration.post.js | 17 +++ 2 files changed, 133 insertions(+) create mode 100644 app/pages/policies/refunds.vue diff --git a/app/pages/policies/refunds.vue b/app/pages/policies/refunds.vue new file mode 100644 index 0000000..2e89fa7 --- /dev/null +++ b/app/pages/policies/refunds.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/server/api/events/[id]/cancel-registration.post.js b/server/api/events/[id]/cancel-registration.post.js index 4138f0f..87a9276 100644 --- a/server/api/events/[id]/cancel-registration.post.js +++ b/server/api/events/[id]/cancel-registration.post.js @@ -42,6 +42,23 @@ export default defineEventHandler(async (event) => { }); } + const existingRegistration = eventDoc.registrations[registrationIndex]; + const ticketType = existingRegistration.ticketType; + const amountPaid = existingRegistration.amountPaid || 0; + // member tickets can be free (default) or paid via circle overrides — gate on amountPaid + const isPaidRegistration = + ticketType === "public" || + ticketType === "series_pass" || + (ticketType === "member" && amountPaid > 0); + + if (isPaidRegistration) { + throw createError({ + statusCode: 403, + statusMessage: + "Paid registrations can't be self-cancelled. Email us for a refund — see /policies/refunds.", + }); + } + // Store registration data before removing (convert to plain object) const registration = { name: eventDoc.registrations[registrationIndex].name,