diff --git a/apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx b/apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx index 86d74d795e..0997b4655b 100644 --- a/apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx +++ b/apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx @@ -11,7 +11,7 @@ import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat"; import { BookingRepository } from "@calcom/lib/server/repository/booking"; import prisma from "@calcom/prisma"; -import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { customInputSchema, eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router"; import type { inferSSRProps } from "@lib/types/inferSSRProps"; @@ -129,7 +129,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { ...eventTypeRaw, periodStartDate: eventTypeRaw.periodStartDate?.toString() ?? null, periodEndDate: eventTypeRaw.periodEndDate?.toString() ?? null, - metadata: EventTypeMetaDataSchema.parse(eventTypeRaw.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeRaw.metadata), recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent), customInputs: customInputSchema.array().parse(eventTypeRaw.customInputs), bookingFields: eventTypeRaw.bookingFields.map((field) => { diff --git a/apps/web/modules/bookings/views/bookings-single-view.tsx b/apps/web/modules/bookings/views/bookings-single-view.tsx index 0577227482..9e3a3a6492 100644 --- a/apps/web/modules/bookings/views/bookings-single-view.tsx +++ b/apps/web/modules/bookings/views/bookings-single-view.tsx @@ -42,6 +42,7 @@ import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; import useTheme from "@calcom/lib/hooks/useTheme"; import isSmsCalEmail from "@calcom/lib/isSmsCalEmail"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; +import { RefundPolicy } from "@calcom/lib/payment/types"; import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat"; import { CURRENT_TIMEZONE } from "@calcom/lib/timezoneConstants"; @@ -507,7 +508,37 @@ export default function Success(props: PageProps) { t("booking_with_payment_cancelled")} {props.paymentStatus.success && !props.paymentStatus.refunded && - t("booking_with_payment_cancelled_already_paid")} + (() => { + const refundPolicy = eventType?.metadata?.apps?.stripe?.refundPolicy; + const refundDaysCount = eventType?.metadata?.apps?.stripe?.refundDaysCount; + + // Handle missing team or event type owner (same in processPaymentRefund.ts) + if (!eventType?.teamId && !eventType?.owner) { + return t("booking_with_payment_cancelled_no_refund"); + } + + // Handle DAYS policy with expired refund window + else if (refundPolicy === RefundPolicy.DAYS && refundDaysCount) { + const startTime = new Date(bookingInfo.startTime); + const cancelTime = new Date(); + const daysDiff = Math.floor( + (cancelTime.getTime() - startTime.getTime()) / (1000 * 60 * 60 * 24) + ); + + if (daysDiff > refundDaysCount) { + return t("booking_with_payment_cancelled_refund_window_expired"); + } + } + // Handle NEVER policy + else if (refundPolicy === RefundPolicy.NEVER) { + return t("booking_with_payment_cancelled_no_refund"); + } + + // Handle ALWAYS policy + else { + return t("booking_with_payment_cancelled_already_paid"); + } + })()} {props.paymentStatus.refunded && t("booking_with_payment_cancelled_refunded")} )} diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 3798415cc1..5d1ecc39d8 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -1988,6 +1988,8 @@ "booking_with_payment_cancelled": "Paying for this event is no longer possible", "booking_with_payment_cancelled_already_paid": "A refund for this booking payment is on its way.", "booking_with_payment_cancelled_refunded": "This booking payment has been refunded.", + "booking_with_payment_cancelled_no_refund": "As per the event's refund policy, the booking payment is non-refundable.", + "booking_with_payment_cancelled_refund_window_expired": "The event's refund window has expired. Hence, booking payment is non-refundable.", "booking_confirmation_failed": "Booking confirmation failed", "not_enough_seats": "Not enough seats", "form_builder_field_already_exists": "A field with this name already exists",