diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx index 2e9ab9faa..ab67751d3 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(confirmation)/booking-confirmation/page.tsx @@ -1,3 +1,5 @@ +import { notFound } from "next/navigation" + import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" import BookingConfirmation from "@/components/HotelReservation/BookingConfirmation" @@ -6,9 +8,14 @@ import type { LangParams, PageArgs } from "@/types/params" export default async function BookingConfirmationPage({ searchParams, -}: PageArgs) { - void getBookingConfirmation(searchParams.confirmationNumber) - return ( - - ) +}: PageArgs) { + const refId = searchParams.RefId + + if (!refId) { + notFound() + } + + void getBookingConfirmation(refId) + + return } diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/gla-payment-callback/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/gla-payment-callback/page.tsx index 571a44d89..2fa2619c8 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/gla-payment-callback/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/gla-payment-callback/page.tsx @@ -19,8 +19,8 @@ export default async function GuaranteePaymentCallbackPage({ }: PageArgs< LangParams, { - status: PaymentCallbackStatusEnum - RefId: string + status?: PaymentCallbackStatusEnum + RefId?: string confirmationNumber?: string ancillary?: string } @@ -30,7 +30,7 @@ export default async function GuaranteePaymentCallbackPage({ const status = searchParams.status const confirmationNumber = searchParams.confirmationNumber const refId = searchParams.RefId - if (!refId) { + if (!status || !confirmationNumber || !refId) { notFound() } const isAncillaryFlow = searchParams.ancillary @@ -43,6 +43,7 @@ export default async function GuaranteePaymentCallbackPage({ return ( @@ -54,10 +55,10 @@ export default async function GuaranteePaymentCallbackPage({ let errorMessage = undefined - if (confirmationNumber) { + if (refId) { try { const bookingStatus = await serverClient().booking.status({ - confirmationNumber, + refId, }) const error = bookingStatus.errors.find((e) => e.errorCode) diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/page.tsx index 74984d9e0..d2be84449 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/(payment-callback)/payment-callback/page.tsx @@ -1,5 +1,6 @@ +import { notFound } from "next/navigation" + import { - BOOKING_CONFIRMATION_NUMBER, BookingErrorCodeEnum, PaymentCallbackStatusEnum, } from "@/constants/booking" @@ -8,7 +9,10 @@ import { details, } from "@/constants/routes/hotelReservation" import { serverClient } from "@/lib/trpc/server" +import { getBooking } from "@/server/routers/booking/utils" +import { getServiceToken } from "@/server/tokenManager" +import { auth } from "@/auth" import HandleErrorCallback from "@/components/HotelReservation/EnterDetails/Payment/PaymentCallback/HandleErrorCallback" import HandleSuccessCallback from "@/components/HotelReservation/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback" @@ -20,7 +24,7 @@ export default async function PaymentCallbackPage({ }: PageArgs< LangParams, { - status: PaymentCallbackStatusEnum + status?: PaymentCallbackStatusEnum confirmationNumber?: string hotel?: string } @@ -30,15 +34,42 @@ export default async function PaymentCallbackPage({ const status = searchParams.status const confirmationNumber = searchParams.confirmationNumber + if (!status || !confirmationNumber) { + notFound() + } + + let token = "" + const session = await auth() + if (session) { + token = session.token.access_token + } else { + const serviceToken = await getServiceToken() + if (serviceToken) { + token = serviceToken.access_token + } + } + + if (!token) { + notFound() + } + + const booking = await getBooking(confirmationNumber, params.lang, token) + + if (!booking) { + notFound() + } + + const { refId } = booking + if (status === PaymentCallbackStatusEnum.Success && confirmationNumber) { - const confirmationUrl = `${bookingConfirmation(lang)}?${BOOKING_CONFIRMATION_NUMBER}=${confirmationNumber}` + const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(refId)}` console.log( `[payment-callback] rendering success callback with confirmation number: ${confirmationNumber}` ) return ( ) @@ -49,10 +80,10 @@ export default async function PaymentCallbackPage({ let errorMessage = undefined - if (confirmationNumber) { + if (refId) { try { const bookingStatus = await serverClient().booking.status({ - confirmationNumber, + refId, }) // TODO: how to handle errors for multiple rooms? diff --git a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx index 03b459fc7..a738ebf02 100644 --- a/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx +++ b/apps/scandic-web/app/[lang]/(live)/(public)/hotelreservation/my-stay/page.tsx @@ -14,7 +14,6 @@ import { getProfileSafely, getSavedPaymentCardsSafely, } from "@/lib/trpc/memoizedRequests" -import { decrypt } from "@/server/routers/utils/encryption" import { auth } from "@/auth" import AdditionalInfoForm from "@/components/HotelReservation/FindMyBooking/AdditionalInfoForm" @@ -35,6 +34,7 @@ import Image from "@/components/Image" import { getIntl } from "@/i18n" import { setLang } from "@/i18n/serverContext" import MyStayProvider from "@/providers/MyStay" +import { parseRefId } from "@/utils/refId" import { isValidSession } from "@/utils/session" import { getCurrentWebUrl } from "@/utils/url" @@ -54,19 +54,18 @@ export default async function MyStay({ notFound() } - const value = decrypt(refId) - if (!value) { + const { confirmationNumber, lastName } = parseRefId(refId) + if (!confirmationNumber) { return notFound() } const session = await auth() const isLoggedIn = isValidSession(session) - const [confirmationNumber, lastName] = value.split(",") const bv = cookies().get("bv")?.value let bookingConfirmation if (isLoggedIn) { - bookingConfirmation = await getBookingConfirmation(confirmationNumber) + bookingConfirmation = await getBookingConfirmation(refId) } else if (bv) { const params = new URLSearchParams(bv) const firstName = params.get("firstName") @@ -113,9 +112,7 @@ export default async function MyStay({ const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD") const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD") - const linkedReservationsPromise = getLinkedReservations({ - rooms: booking.linkedReservations, - }) + const linkedReservationsPromise = getLinkedReservations(booking.refId) const ancillariesInput = { fromDate, @@ -187,7 +184,7 @@ export default async function MyStay({ breakfastPackages={breakfastPackages} lang={params.lang} linkedReservationsPromise={linkedReservationsPromise} - refId={refId} + refId={booking.refId} roomCategories={roomCategories} savedCreditCards={savedCreditCards} > @@ -215,7 +212,6 @@ export default async function MyStay({ packages={breakfastPackages} user={user} savedCreditCards={savedCreditCards} - refId={refId} /> )} diff --git a/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx b/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx index 2bb1e870c..85e53d3f9 100644 --- a/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx +++ b/apps/scandic-web/app/[lang]/webview/hotelreservation/my-stay/page.tsx @@ -14,7 +14,6 @@ import { getProfileSafely, getSavedPaymentCardsSafely, } from "@/lib/trpc/memoizedRequests" -import { decrypt } from "@/server/routers/utils/encryption" import { auth } from "@/auth" import AdditionalInfoForm from "@/components/HotelReservation/FindMyBooking/AdditionalInfoForm" @@ -35,6 +34,7 @@ import Image from "@/components/Image" import { getIntl } from "@/i18n" import { setLang } from "@/i18n/serverContext" import MyStayProvider from "@/providers/MyStay" +import { parseRefId } from "@/utils/refId" import { isValidSession } from "@/utils/session" import { getCurrentWebUrl } from "@/utils/url" @@ -54,18 +54,19 @@ export default async function MyStay({ notFound() } - const value = decrypt(refId) - if (!value) { + const { confirmationNumber, lastName } = parseRefId(refId) + + if (!confirmationNumber) { return notFound() } + const session = await auth() const isLoggedIn = isValidSession(session) - const [confirmationNumber, lastName] = value.split(",") const bv = cookies().get("bv")?.value let bookingConfirmation if (isLoggedIn) { - bookingConfirmation = await getBookingConfirmation(confirmationNumber) + bookingConfirmation = await getBookingConfirmation(refId) } else if (bv) { const params = new URLSearchParams(bv) const firstName = params.get("firstName") @@ -110,9 +111,7 @@ export default async function MyStay({ const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD") const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD") - const linkedReservationsPromise = getLinkedReservations({ - rooms: booking.linkedReservations, - }) + const linkedReservationsPromise = getLinkedReservations(booking.refId) const ancillariesInput = { fromDate, @@ -184,7 +183,7 @@ export default async function MyStay({ breakfastPackages={breakfastPackages} lang={params.lang} linkedReservationsPromise={linkedReservationsPromise} - refId={refId} + refId={booking.refId} roomCategories={roomCategories} savedCreditCards={savedCreditCards} > @@ -212,7 +211,6 @@ export default async function MyStay({ packages={breakfastPackages} user={user} savedCreditCards={savedCreditCards} - refId={refId} /> )} diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx index 48e6751cf..f18a35333 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Header/index.tsx @@ -52,7 +52,7 @@ export default function Header({ url: hotel.contactInformation.websiteUrl, } - const bookingUrlPath = `${myStay[lang]}?RefId=${refId}` + const bookingUrlPath = `${myStay[lang]}?RefId=${encodeURIComponent(refId)}` return (
diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/LinkedReservation/index.tsx b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/LinkedReservation/index.tsx index 757fa8ef4..a17c19da7 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/LinkedReservation/index.tsx +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/LinkedReservation/index.tsx @@ -20,13 +20,13 @@ import { CurrencyEnum } from "@/types/enums/currency" export function LinkedReservation({ checkInTime, checkOutTime, - confirmationNumber, + refId, roomIndex, roomNumber, }: LinkedReservationProps) { const lang = useLang() const { data, refetch, isLoading } = trpc.booking.get.useQuery({ - confirmationNumber, + refId, lang, }) const { diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/Room/RoomDetailsSidePeek.tsx b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/Room/RoomDetailsSidePeek.tsx index 32a8c7df4..3023ce021 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/Room/RoomDetailsSidePeek.tsx +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/Room/RoomDetailsSidePeek.tsx @@ -8,12 +8,12 @@ import { Typography } from "@scandic-hotels/design-system/Typography" import { BookingStatusEnum } from "@/constants/booking" import { trpc } from "@/lib/trpc/client" -import { getBookedHotelRoom } from "@/server/routers/booking/utils" import { useBookingConfirmationStore } from "@/stores/booking-confirmation" import { convertToChildType } from "@/components/HotelReservation/utils/convertToChildType" import { getPriceType } from "@/components/HotelReservation/utils/getPriceType" import BookedRoomSidePeek from "@/components/SidePeeks/BookedRoomSidePeek" +import { getBookedHotelRoom } from "@/utils/booking" import styles from "./sidePeek.module.css" diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/index.tsx b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/index.tsx index 8ae1bb9b1..0f3badf16 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/index.tsx +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/Rooms/index.tsx @@ -14,14 +14,13 @@ export default async function Rooms({ checkInTime, checkOutTime, mainRoom, - linkedReservations, }: BookingConfirmationRoomsProps) { const intl = await getIntl() return (
- {linkedReservations.length ? ( + {booking.linkedReservations.length ? (

{intl.formatMessage( @@ -42,7 +41,7 @@ export default async function Rooms({ />

- {linkedReservations.map((reservation, idx) => ( + {booking.linkedReservations.map((reservation, idx) => (

@@ -57,7 +56,7 @@ export default async function Rooms({ diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/index.tsx b/apps/scandic-web/components/HotelReservation/BookingConfirmation/index.tsx index 8a8b32614..cc24ba09a 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/index.tsx +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/index.tsx @@ -1,7 +1,6 @@ import { notFound } from "next/navigation" import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests" -import { encrypt } from "@/server/routers/utils/encryption" import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails" import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails" @@ -23,22 +22,20 @@ import styles from "./bookingConfirmation.module.css" import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation" export default async function BookingConfirmation({ - confirmationNumber, + refId, }: BookingConfirmationProps) { - const bookingConfirmation = await getBookingConfirmation(confirmationNumber) + const bookingConfirmation = await getBookingConfirmation(refId) if (!bookingConfirmation) { return notFound() } + const { booking, hotel, room, roomCategories } = bookingConfirmation + if (!room) { return notFound() } - const refId = encrypt( - `${booking.confirmationNumber},${booking.guest.lastName}` - ) - const intl = await getIntl() return ( diff --git a/apps/scandic-web/components/HotelReservation/BookingConfirmation/utils.ts b/apps/scandic-web/components/HotelReservation/BookingConfirmation/utils.ts index 2e8428983..d920cf0ae 100644 --- a/apps/scandic-web/components/HotelReservation/BookingConfirmation/utils.ts +++ b/apps/scandic-web/components/HotelReservation/BookingConfirmation/utils.ts @@ -64,6 +64,7 @@ export function mapRoomState( name: room.name, packages: booking.packages, rateDefinition: booking.rateDefinition, + refId: booking.refId, roomFeatures: booking.packages.filter((p) => p.type === "RoomFeature"), roomPoints: booking.roomPoints, roomPrice: booking.roomPrice, diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback.tsx index 20445bb6e..e7407e860 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback.tsx @@ -18,12 +18,12 @@ const validBookingStatuses = [ ] interface HandleStatusPollingProps { - confirmationNumber: string + refId: string successRedirectUrl: string } export default function HandleSuccessCallback({ - confirmationNumber, + refId, successRedirectUrl, }: HandleStatusPollingProps) { const router = useRouter() @@ -33,7 +33,7 @@ export default function HandleSuccessCallback({ error, isTimeout, } = useHandleBookingStatus({ - confirmationNumber, + refId, expectedStatuses: validBookingStatuses, maxRetries: 10, retryInterval: 2000, diff --git a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx index c7cd67342..031f1e51b 100644 --- a/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx +++ b/apps/scandic-web/components/HotelReservation/EnterDetails/Payment/PaymentClient.tsx @@ -12,7 +12,6 @@ import { Button } from "@scandic-hotels/design-system/Button" import { Typography } from "@scandic-hotels/design-system/Typography" import { - BOOKING_CONFIRMATION_NUMBER, BookingStatusEnum, PAYMENT_METHOD_TITLES, PaymentMethodEnum, @@ -111,7 +110,7 @@ export default function PaymentClient({ (state) => state.actions.setIsSubmittingDisabled ) - const [bookingNumber, setBookingNumber] = useState("") + const [refId, setRefId] = useState("") const [isPollingForBookingStatus, setIsPollingForBookingStatus] = useState(false) @@ -156,13 +155,15 @@ export default function PaymentClient({ return } + const mainRoom = result.rooms[0] + if (result.reservationStatus == BookingStatusEnum.BookingCompleted) { - const confirmationUrl = `${bookingConfirmation(lang)}?${BOOKING_CONFIRMATION_NUMBER}=${result.id}` + const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(mainRoom.refId)}` router.push(confirmationUrl) return } - setBookingNumber(result.id) + setRefId(mainRoom.refId) const hasPriceChange = result.rooms.some((r) => r.priceChangedMetadata) if (hasPriceChange) { @@ -200,7 +201,7 @@ export default function PaymentClient({ }) const bookingStatus = useHandleBookingStatus({ - confirmationNumber: bookingNumber, + refId, expectedStatuses: [BookingStatusEnum.BookingCompleted], maxRetries, retryInterval, @@ -263,7 +264,8 @@ export default function PaymentClient({ bookingStatus?.data?.reservationStatus === BookingStatusEnum.BookingCompleted ) { - const confirmationUrl = `${bookingConfirmation(lang)}?${BOOKING_CONFIRMATION_NUMBER}=${bookingStatus?.data?.id}` + const mainRoom = bookingStatus.data.rooms[0] + const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(mainRoom.refId)}` router.push(confirmationUrl) } else if (bookingStatus.isTimeout) { handlePaymentError("Timeout") @@ -633,9 +635,7 @@ export default function PaymentClient({ : "" router.push(`${selectRate(lang)}${allSearchParams}`) }} - onAccept={() => - priceChange.mutate({ confirmationNumber: bookingNumber }) - } + onAccept={() => priceChange.mutate({ refId })} /> ) : null}

diff --git a/apps/scandic-web/components/HotelReservation/FindMyBooking/index.tsx b/apps/scandic-web/components/HotelReservation/FindMyBooking/index.tsx index b50859a48..9547c390d 100644 --- a/apps/scandic-web/components/HotelReservation/FindMyBooking/index.tsx +++ b/apps/scandic-web/components/HotelReservation/FindMyBooking/index.tsx @@ -44,7 +44,7 @@ export default function FindMyBooking() { const values = form.getValues() const value = new URLSearchParams(values).toString() document.cookie = `bv=${encodeURIComponent(value)}; Path=/; Max-Age=600; Secure; SameSite=Strict` - router.push(`${myStay[lang]}?RefId=${result.refId}`) + router.push(`${myStay[lang]}?RefId=${encodeURIComponent(result.refId)}`) }, onError: (error) => { console.error("Failed to create ref id", error) diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx index 51510f14e..0b45f8d94 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddAncillaryFlow/AddAncillaryFlowModal/index.tsx @@ -57,7 +57,6 @@ export default function AddAncillaryFlowModal({ packages, user, savedCreditCards, - refId, }: AddAncillaryFlowModalProps) { const { currentStep, @@ -123,7 +122,7 @@ export default function AddAncillaryFlowModal({ const addAncillary = trpc.booking.packages.useMutation() const { guaranteeBooking, isLoading, handleGuaranteeError } = - useGuaranteeBooking(booking.confirmationNumber, true, booking.hotelId) + useGuaranteeBooking(booking.refId, true, booking.hotelId) function validateTermsAndConditions(data: AncillaryFormData): boolean { if (!data.termsAndConditions) { @@ -145,7 +144,7 @@ export default function AddAncillaryFlowModal({ ) { addAncillary.mutate( { - confirmationNumber: booking.confirmationNumber, + refId: booking.refId, ancillaryComment: data.optionalText, ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime ? data.deliveryTime @@ -176,7 +175,7 @@ export default function AddAncillaryFlowModal({ clearAncillarySessionData() closeModal() utils.booking.get.invalidate({ - confirmationNumber: booking.confirmationNumber, + refId: booking.refId, }) router.refresh() } else { @@ -202,7 +201,7 @@ export default function AddAncillaryFlowModal({ selectedAncillary, data.deliveryTime ) - if (booking.confirmationNumber) { + if (booking.refId) { const card = savedCreditCard ? { alias: savedCreditCard.alias, @@ -211,12 +210,12 @@ export default function AddAncillaryFlowModal({ } : undefined guaranteeBooking.mutate({ - confirmationNumber: booking.confirmationNumber, + refId: booking.refId, language: lang, ...(card && { card }), - success: `${guaranteeRedirectUrl}?status=success&RefId=${encodeURIComponent(refId)}&ancillary=1`, - error: `${guaranteeRedirectUrl}?status=error&RefId=${encodeURIComponent(refId)}&ancillary=1`, - cancel: `${guaranteeRedirectUrl}?status=cancel&RefId=${encodeURIComponent(refId)}&ancillary=1`, + success: `${guaranteeRedirectUrl}?status=success&ancillary=1&RefId=${encodeURIComponent(booking.refId)}`, + error: `${guaranteeRedirectUrl}?status=error&ancillary=1&RefId=${encodeURIComponent(booking.refId)}`, + cancel: `${guaranteeRedirectUrl}?status=cancel&ancillary=1&RefId=${encodeURIComponent(booking.refId)}`, }) } else { handleGuaranteeError("No confirmation number") diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/RemoveButton.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/RemoveButton.tsx index 9d346df62..75300af24 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/RemoveButton.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/RemoveButton.tsx @@ -10,12 +10,12 @@ import { toast } from "@/components/TempDesignSystem/Toasts" import useLang from "@/hooks/useLang" export default function RemoveButton({ - confirmationNumber, + refId, codes, title, onSuccess, }: { - confirmationNumber: string + refId: string codes: string[] title?: string onSuccess: () => void @@ -51,7 +51,7 @@ export default function RemoveButton({ removePackage.mutate( { language: lang, - confirmationNumber, + refId, codes, }, { diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/index.tsx index b8328768f..08f8349df 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/AddedAncillaries/index.tsx @@ -126,7 +126,7 @@ export function AddedAncillaries({ {booking.confirmationNumber && ancillary.code ? (
} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx index 2ce63ec90..33edf262d 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Ancillaries/index.tsx @@ -83,7 +83,6 @@ export function Ancillaries({ packages, user, savedCreditCards, - refId, }: AncillariesProps) { const intl = useIntl() const ancillaries = use(ancillariesPromise) @@ -221,7 +220,6 @@ export function Ancillaries({ user={user} booking={booking} packages={packages} - refId={refId} savedCreditCards={savedCreditCards} /> diff --git a/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx index 2013e3626..3b8be970f 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/GuestDetails/index.tsx @@ -31,14 +31,14 @@ import type { SafeUser } from "@/types/user" import type { Guest } from "@/server/routers/booking/output" interface GuestDetailsProps { - confirmationNumber: string + refId: string guest: Guest isCancelled: boolean user: SafeUser } export default function GuestDetails({ - confirmationNumber, + refId, guest, isCancelled, user, @@ -74,7 +74,7 @@ export default function GuestDetails({ onSuccess: (data) => { if (data) { utils.booking.get.invalidate({ - confirmationNumber: data.confirmationNumber, + refId: data.refId, }) toast.success( @@ -106,7 +106,7 @@ export default function GuestDetails({ async function onSubmit(data: ModifyContactSchema) { updateGuest.mutate({ - confirmationNumber, + refId, guest: { email: data.email, phoneNumber: data.phoneNumber, diff --git a/apps/scandic-web/components/HotelReservation/MyStay/PriceType/Points.tsx b/apps/scandic-web/components/HotelReservation/MyStay/PriceType/Points.tsx index da37f1f92..0fd3b8554 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/PriceType/Points.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/PriceType/Points.tsx @@ -3,8 +3,6 @@ import { useIntl } from "react-intl" import { Typography } from "@scandic-hotels/design-system/Typography" -import { useMyStayStore } from "@/stores/my-stay" - import SkeletonShimmer from "@/components/SkeletonShimmer" import { formatPrice } from "@/utils/numberFormatting" @@ -14,13 +12,14 @@ export default function Points({ isCancelled, points, price, + currencyCode, }: { isCancelled: boolean points: number price: number + currencyCode: CurrencyEnum }) { const intl = useIntl() - const currency = useMyStayStore((state) => state.bookedRoom.currencyCode) if (!points) { return @@ -31,7 +30,7 @@ export default function Points({ points, CurrencyEnum.POINTS, price, - currency + currencyCode ) return ( diff --git a/apps/scandic-web/components/HotelReservation/MyStay/PriceType/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/PriceType/index.tsx index 04a7ed7bb..1bb47609d 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/PriceType/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/PriceType/index.tsx @@ -11,7 +11,12 @@ import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmat interface PriceTypeProps extends Pick< BookingConfirmation["booking"], - "cheques" | "rateDefinition" | "roomPoints" | "totalPrice" | "vouchers" + | "cheques" + | "currencyCode" + | "rateDefinition" + | "roomPoints" + | "totalPrice" + | "vouchers" > { formattedTotalPrice: string isCancelled: boolean @@ -22,6 +27,7 @@ export default function PriceType({ cheques, formattedTotalPrice, isCancelled, + currencyCode, priceType, rateDefinition, roomPoints, @@ -51,6 +57,7 @@ export default function PriceType({ isCancelled={isCancelled} points={roomPoints} price={totalPrice} + currencyCode={currencyCode} /> ) case PriceTypeEnum.voucher: diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx index 420648db7..beee4b0ad 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Receipt/index.tsx @@ -11,10 +11,10 @@ import { getBookingConfirmation, getProfileSafely, } from "@/lib/trpc/memoizedRequests" -import { decrypt } from "@/server/routers/utils/encryption" import { auth } from "@/auth" import { getIntl } from "@/i18n" +import { parseRefId } from "@/utils/refId" import { isValidSession } from "@/utils/session" import AdditionalInfoForm from "../../FindMyBooking/AdditionalInfoForm" @@ -32,18 +32,19 @@ import styles from "./receipt.module.css" import { CurrencyEnum } from "@/types/enums/currency" export async function Receipt({ refId }: { refId: string }) { - const value = decrypt(refId) - if (!value) { + const { confirmationNumber, lastName } = parseRefId(refId) + + if (!confirmationNumber) { return notFound() } + const session = await auth() const isLoggedIn = isValidSession(session) - const [confirmationNumber, lastName] = value.split(",") const bv = cookies().get("bv")?.value let bookingConfirmation if (isLoggedIn) { - bookingConfirmation = await getBookingConfirmation(confirmationNumber) + bookingConfirmation = await getBookingConfirmation(refId) } else if (bv) { const params = new URLSearchParams(bv) const firstName = params.get("firstName") diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/CancelStayPriceContainer.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/CancelStayPriceContainer.tsx index 5b9affdb8..0e1097652 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/CancelStayPriceContainer.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/CancelStayPriceContainer.tsx @@ -29,9 +29,7 @@ export default function CancelStayPriceContainer() { const { totalAdults, totalChildren } = formRooms.reduce( (total, formRoom) => { if (formRoom.checked) { - const room = rooms.find( - (r) => r.confirmationNumber === formRoom.confirmationNumber - ) + const room = rooms.find((r) => r.refId === formRoom.refId) if (room) { total.totalAdults = total.totalAdults + room.adults if (room.childrenInRoom.length) { diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/FinalConfirmation/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/FinalConfirmation/index.tsx index 9b441100c..3f8197023 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/FinalConfirmation/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/FinalConfirmation/index.tsx @@ -57,7 +57,7 @@ export default function FinalConfirmation({ ) } else { const cancelledRooms = rooms.filter((r) => - variables.confirmationNumbers.includes(r.confirmationNumber) + variables.refIds.includes(r.refId) ) for (const cancelledRoom of cancelledRooms) { toast.success( @@ -94,11 +94,11 @@ export default function FinalConfirmation({ } utils.booking.get.invalidate({ - confirmationNumber: bookedRoom.confirmationNumber, + refId: bookedRoom.refId, }) utils.booking.linkedReservations.invalidate({ lang, - rooms: bookedRoom.linkedReservations, + refId: bookedRoom.refId, }) closeModal() }, @@ -113,12 +113,12 @@ export default function FinalConfirmation({ function cancelBooking() { if (Array.isArray(formRooms)) { - const confirmationNumbersToCancel = formRooms + const refIdsToCancel = formRooms .filter((r) => r.checked) - .map((r) => r.confirmationNumber) - if (confirmationNumbersToCancel.length) { + .map((r) => r.refId) + if (refIdsToCancel.length) { cancelBookingsMutation.mutate({ - confirmationNumbers: confirmationNumbersToCancel, + refIds: refIdsToCancel, language: lang, }) } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/index.tsx index c0b110c32..5d9d944ec 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/CancelStay/Steps/index.tsx @@ -29,7 +29,7 @@ export default function Steps({ closeModal }: StepsProps) { rooms: rooms.map((room, idx) => ({ // Single room booking checked: rooms.length === 1, - confirmationNumber: room.confirmationNumber, + refId: room.refId, id: idx + 1, })), }, diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/Steps/Confirmation/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/Steps/Confirmation/index.tsx index b59a02140..40b7c1c5f 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/Steps/Confirmation/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/ChangeDates/Steps/Confirmation/index.tsx @@ -57,7 +57,7 @@ export default function Confirmation({ onSuccess: (updatedBooking) => { if (updatedBooking) { utils.booking.get.invalidate({ - confirmationNumber: updatedBooking.confirmationNumber, + refId: updatedBooking.refId, }) toast.success( @@ -86,7 +86,7 @@ export default function Confirmation({ function handleModifyStay() { updateBooking.mutate({ - confirmationNumber: bookedRoom.confirmationNumber, + refId: bookedRoom.refId, checkInDate, checkOutDate, }) diff --git a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/Form/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/Form/index.tsx index e7b12c8fb..70d4ee1eb 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/Form/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/ReferenceCard/Actions/NotCancelled/ManageStay/Actions/GuaranteeLateArrival/Form/index.tsx @@ -41,7 +41,7 @@ export default function Form() { confirmationNumber: state.bookedRoom.confirmationNumber, currencyCode: state.bookedRoom.currencyCode, hotelId: state.bookedRoom.hotelId, - refId: state.refId, + refId: state.bookedRoom.refId, savedCreditCards: state.savedCreditCards, })) @@ -85,7 +85,7 @@ export default function Form() { : undefined writeGlaToSessionStorage("yes", hotelId) guaranteeBooking.mutate({ - confirmationNumber, + refId, language: lang, ...(card && { card }), success: `${guaranteeRedirectUrl}?status=success&RefId=${encodeURIComponent(refId)}`, diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/MultiRoom/Room.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/MultiRoom/Room.tsx index 3012a6072..98a88ebbf 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/MultiRoom/Room.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/MultiRoom/Room.tsx @@ -276,6 +276,7 @@ export default function Room({ booking, roomNr, user }: RoomProps) { cheques={cheques} formattedTotalPrice={formattedTotalPrice} isCancelled={isCancelled} + currencyCode={currencyCode} priceType={priceType} rateDefinition={rateDefinition} roomPoints={roomPoints} diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/BookingInformation/PriceDetails/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/BookingInformation/PriceDetails/index.tsx index 058dc85f3..595a0315c 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/BookingInformation/PriceDetails/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/BookingInformation/PriceDetails/index.tsx @@ -16,6 +16,7 @@ export default function PriceDetails() { cheques: state.bookedRoom.cheques, formattedTotalPrice: state.totalPrice, isCancelled: state.bookedRoom.isCancelled, + currencyCode: state.bookedRoom.currencyCode, priceType: state.bookedRoom.priceType, rateDefinition: state.bookedRoom.rateDefinition, roomPoints: state.bookedRoom.roomPoints, diff --git a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/index.tsx b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/index.tsx index 459bf2e02..d2945219d 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/index.tsx +++ b/apps/scandic-web/components/HotelReservation/MyStay/Rooms/SingleRoom/index.tsx @@ -23,21 +23,15 @@ interface RoomProps { } export default function SingleRoom({ user }: RoomProps) { - const { - confirmationNumber, - guest, - isCancelled, - isMultiRoom, - roomName, - roomNumber, - } = useMyStayStore((state) => ({ - confirmationNumber: state.bookedRoom.confirmationNumber, - guest: state.bookedRoom.guest, - isCancelled: state.bookedRoom.isCancelled, - isMultiRoom: state.rooms.length > 1, - roomName: state.bookedRoom.roomName, - roomNumber: state.bookedRoom.roomNumber, - })) + const { refId, guest, isCancelled, isMultiRoom, roomName, roomNumber } = + useMyStayStore((state) => ({ + refId: state.bookedRoom.refId, + guest: state.bookedRoom.guest, + isCancelled: state.bookedRoom.isCancelled, + isMultiRoom: state.rooms.length > 1, + roomName: state.bookedRoom.roomName, + roomNumber: state.bookedRoom.roomNumber, + })) if (isMultiRoom) { return null @@ -70,7 +64,7 @@ export default function SingleRoom({ user }: RoomProps) {
{ describe("for logged in booking", () => { it("should enable access if all is provided", () => { - expect(accessBooking(loggedIn, "Booking", user)).toBe(ACCESS_GRANTED) + expect(accessBooking(loggedInGuest, "Booking", authenticatedUser)).toBe( + ACCESS_GRANTED + ) }) it("should enable access if all is provided and be case-insensitive", () => { - expect(accessBooking(loggedIn, "BoOkInG", user)).toBe(ACCESS_GRANTED) + expect(accessBooking(loggedInGuest, "BoOkInG", authenticatedUser)).toBe( + ACCESS_GRANTED + ) }) - it("should prompt to login", () => { - expect(accessBooking(loggedIn, "Booking", null)).toBe(ERROR_UNAUTHORIZED) + it("should prompt to login without user", () => { + expect(accessBooking(loggedInGuest, "Booking", null)).toBe( + ERROR_UNAUTHORIZED + ) }) - it("should deny access", () => { - expect(accessBooking(loggedIn, "NotBooking", user)).toBe(ERROR_NOT_FOUND) + it("should prompt to login if user mismatch", () => { + expect( + accessBooking(loggedInGuest, "Booking", badAuthenticatedUser) + ).toBe(ERROR_UNAUTHORIZED) + }) + it("should deny access if refId mismatch", () => { + expect( + accessBooking(loggedInGuest, "NotBooking", authenticatedUser) + ).toBe(ERROR_UNAUTHORIZED) }) }) + describe("for anonymous booking", () => { it("should enable access if all is provided", () => { const cookieString = new URLSearchParams({ @@ -34,7 +48,7 @@ describe("Access booking", () => { lastName: "Booking", email: "logged+out@scandichotels.com", }).toString() - expect(accessBooking(loggedOut, "Booking", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe( ACCESS_GRANTED ) }) @@ -45,7 +59,7 @@ describe("Access booking", () => { lastName: "Booking", email: "logged+out@scandichotels.com", }).toString() - expect(accessBooking(loggedOut, "Booking", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe( ACCESS_GRANTED ) }) @@ -56,7 +70,7 @@ describe("Access booking", () => { lastName: "Booking", email: "logged+out@scandichotels.com", }).toString() - expect(accessBooking(loggedOut, "BoOkInG", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "BoOkInG", null, cookieString)).toBe( ACCESS_GRANTED ) }) @@ -67,7 +81,7 @@ describe("Access booking", () => { lastName: "Booking", email: "LOGGED+out@scandichotels.com", }).toString() - expect(accessBooking(loggedOut, "Booking", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe( ACCESS_GRANTED ) }) @@ -78,9 +92,14 @@ describe("Access booking", () => { lastName: "Booking", email: "logged+out@scandichotels.com", }).toString() - expect(accessBooking(loggedOut, "Booking", user, cookieString)).toBe( - ERROR_FORBIDDEN - ) + expect( + accessBooking( + loggedOutGuest, + "Booking", + authenticatedUser, + cookieString + ) + ).toBe(ERROR_FORBIDDEN) }) it("should prompt for more if first name is missing", () => { const cookieString = new URLSearchParams({ @@ -88,7 +107,7 @@ describe("Access booking", () => { lastName: "Booking", email: "logged+out@scandichotels.com", }).toString() - expect(accessBooking(loggedOut, "Booking", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe( ERROR_BAD_REQUEST ) }) @@ -98,23 +117,25 @@ describe("Access booking", () => { firstName: "Anonymous", lastName: "Booking", }).toString() - expect(accessBooking(loggedOut, "Booking", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe( ERROR_BAD_REQUEST ) }) it("should prompt for more if cookie is invalid", () => { const cookieString = new URLSearchParams({}).toString() - expect(accessBooking(loggedOut, "Booking", null, cookieString)).toBe( + expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe( ERROR_BAD_REQUEST ) }) - it("should deny access", () => { - expect(accessBooking(loggedOut, "NotBooking", null)).toBe(ERROR_NOT_FOUND) + it("should deny access if refId mismatch", () => { + expect(accessBooking(loggedOutGuest, "NotBooking", null)).toBe( + ERROR_NOT_FOUND + ) }) }) }) -const user: SafeUser = { +const authenticatedUser: SafeUser = { address: { city: undefined, country: "Sweden", @@ -124,10 +145,10 @@ const user: SafeUser = { }, dateOfBirth: "", email: "", - firstName: "", + firstName: "Authenticated", language: undefined, - lastName: "", - membershipNumber: "", + lastName: "Booking", + membershipNumber: "01234567890123", membership: undefined, loyalty: { memberships: [], @@ -145,7 +166,38 @@ const user: SafeUser = { profileId: "", } -const loggedOut: Guest = { +const badAuthenticatedUser: SafeUser = { + address: { + city: undefined, + country: "Sweden", + countryCode: "SE", + streetAddress: undefined, + zipCode: undefined, + }, + dateOfBirth: "", + email: "", + firstName: "Authenticated", + language: undefined, + lastName: `Bad name ${Math.random()}`, + membershipNumber: "0987654321", + membership: undefined, + loyalty: { + memberships: [], + pointExpirations: [], + points: { + earned: 0, + spent: 0, + spendable: 0, + }, + tier: "L1", + tierExpires: "", + }, + name: "", + phoneNumber: undefined, + profileId: "", +} + +const loggedOutGuest: Guest = { email: "logged+out@scandichotels.com", firstName: "Anonymous", lastName: "Booking", @@ -154,7 +206,7 @@ const loggedOut: Guest = { countryCode: "SE", } -const loggedIn: Guest = { +const loggedInGuest: Guest = { email: "logged+in@scandichotels.com", firstName: "Authenticated", lastName: "Booking", diff --git a/apps/scandic-web/components/HotelReservation/MyStay/accessBooking.ts b/apps/scandic-web/components/HotelReservation/MyStay/accessBooking.ts index 7fc3f9c72..0076ba447 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/accessBooking.ts +++ b/apps/scandic-web/components/HotelReservation/MyStay/accessBooking.ts @@ -21,22 +21,20 @@ function accessBooking( ) { if (guest.membershipNumber) { if (user) { - if (lastName.toLowerCase() === guest.lastName?.toLowerCase()) { + if ( + user.membershipNumber === guest.membershipNumber && + user.lastName.toLowerCase() === lastName.toLowerCase() && + lastName.toLowerCase() === guest.lastName?.toLowerCase() + ) { return ACCESS_GRANTED } - } else { - console.warn( - "Access to booking not granted due to anonymous user attempting accessing to logged in booking" - ) - return ERROR_UNAUTHORIZED } + + return ERROR_UNAUTHORIZED } if (guest.lastName?.toLowerCase() === lastName.toLowerCase()) { if (user) { - console.warn( - "Access to booking not granted due to logged in user attempting access to anonymous booking" - ) return ERROR_FORBIDDEN } else { const params = new URLSearchParams(cookie) @@ -47,17 +45,11 @@ function accessBooking( ) { return ACCESS_GRANTED } else { - console.warn( - "Access to booking not granted due to incorrect cookie values" - ) return ERROR_BAD_REQUEST } } } - console.warn( - "Access to booking not granted due to anonymous user attempting access with incorrect lastname" - ) return ERROR_NOT_FOUND } diff --git a/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts b/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts index 7c5097a01..377f3f7be 100644 --- a/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts +++ b/apps/scandic-web/components/HotelReservation/MyStay/utils/mapRoomDetails.ts @@ -143,6 +143,7 @@ export function mapRoomDetails({ priceType, rate, rateDefinition: booking.rateDefinition, + refId: booking.refId, reservationStatus: booking.reservationStatus, room, roomName: room?.name ?? "", diff --git a/apps/scandic-web/components/SidePeeks/BookedRoomSidePeek/index.tsx b/apps/scandic-web/components/SidePeeks/BookedRoomSidePeek/index.tsx index cd1706770..a33fabeda 100644 --- a/apps/scandic-web/components/SidePeeks/BookedRoomSidePeek/index.tsx +++ b/apps/scandic-web/components/SidePeeks/BookedRoomSidePeek/index.tsx @@ -45,6 +45,7 @@ export type Room = Pick< | "checkInDate" | "cheques" | "confirmationNumber" + | "refId" | "currencyCode" | "guest" | "rateDefinition" @@ -87,6 +88,7 @@ export default function BookedRoomSidePeek({ cheques, childrenInRoom, confirmationNumber, + refId, currencyCode, guest, isCancelled, @@ -377,6 +379,7 @@ export default function BookedRoomSidePeek({ formattedTotalPrice={formattedTotalPrice} isCancelled={isCancelled} priceType={priceType} + currencyCode={currencyCode} rateDefinition={rateDefinition} roomPoints={roomPoints} totalPrice={totalPrice} @@ -409,7 +412,7 @@ export default function BookedRoomSidePeek({ )} { if (result) { + const mainRoom = result.rooms[0] if (result.reservationStatus == BookingStatusEnum.BookingCompleted) { - utils.booking.get.invalidate({ confirmationNumber }) + utils.booking.get.invalidate({ refId: mainRoom.refId }) } else { setIsPollingForBookingStatus(true) - utils.booking.status.invalidate({ confirmationNumber }) + utils.booking.status.invalidate({ refId: mainRoom.refId }) } } else { handleGuaranteeError() @@ -66,7 +67,7 @@ export function useGuaranteeBooking( }) const bookingStatus = useHandleBookingStatus({ - confirmationNumber, + refId, expectedStatuses: [BookingStatusEnum.BookingCompleted], maxRetries, retryInterval, @@ -76,7 +77,7 @@ export function useGuaranteeBooking( useEffect(() => { if (bookingStatus?.data?.paymentUrl && isPollingForBookingStatus) { router.push(bookingStatus.data.paymentUrl) - utils.booking.get.invalidate({ confirmationNumber }) + utils.booking.get.invalidate({ refId }) setIsPollingForBookingStatus(false) } else if (bookingStatus.isTimeout) { handleGuaranteeError("Timeout") @@ -87,7 +88,7 @@ export function useGuaranteeBooking( handleGuaranteeError, setIsPollingForBookingStatus, isPollingForBookingStatus, - confirmationNumber, + refId, utils.booking.get, ]) diff --git a/apps/scandic-web/hooks/booking/useHandleBookingStatus.ts b/apps/scandic-web/hooks/booking/useHandleBookingStatus.ts index c47d01148..4b0d7b4a6 100644 --- a/apps/scandic-web/hooks/booking/useHandleBookingStatus.ts +++ b/apps/scandic-web/hooks/booking/useHandleBookingStatus.ts @@ -7,13 +7,13 @@ import { trpc } from "@/lib/trpc/client" import type { BookingStatusEnum } from "@/constants/booking" export function useHandleBookingStatus({ - confirmationNumber, + refId, expectedStatuses, maxRetries, retryInterval, enabled, }: { - confirmationNumber: string | null + refId: string | null expectedStatuses: BookingStatusEnum[] maxRetries: number retryInterval: number @@ -22,7 +22,7 @@ export function useHandleBookingStatus({ const retries = useRef(0) const query = trpc.booking.status.useQuery( - { confirmationNumber: confirmationNumber ?? "" }, + { refId: refId ?? "" }, { enabled, refetchInterval: (query) => { diff --git a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts index 05040e27c..e4d4e6938 100644 --- a/apps/scandic-web/lib/trpc/memoizedRequests/index.ts +++ b/apps/scandic-web/lib/trpc/memoizedRequests/index.ts @@ -17,7 +17,6 @@ import type { HotelInput, } from "@/types/trpc/routers/hotel/hotel" import type { Lang } from "@/constants/languages" -import type { LinkedReservationsInput } from "@/server/routers/booking/input" import type { GetHotelsByCSFilterInput } from "@/server/routers/hotels/input" import type { GetSavedPaymentCardsInput } from "@/server/routers/user/input" @@ -136,8 +135,8 @@ export const getPackages = cache(async function getMemoizedPackages( }) export const getBookingConfirmation = cache( - async function getMemoizedBookingConfirmation(confirmationNumber: string) { - return serverClient().booking.get({ confirmationNumber }) + async function getMemoizedBookingConfirmation(refId: string) { + return serverClient().booking.get({ refId }) } ) @@ -156,8 +155,10 @@ export const findBooking = cache(async function getMemoizedFindBooking( }) export const getLinkedReservations = cache( - async function getMemoizedLinkedReservations(input: LinkedReservationsInput) { - return serverClient().booking.linkedReservations(input) + async function getMemoizedLinkedReservations(refId: string) { + return serverClient().booking.linkedReservations({ + refId, + }) } ) diff --git a/apps/scandic-web/providers/MyStay.tsx b/apps/scandic-web/providers/MyStay.tsx index 6a3fa6e2c..e11c73543 100644 --- a/apps/scandic-web/providers/MyStay.tsx +++ b/apps/scandic-web/providers/MyStay.tsx @@ -45,7 +45,7 @@ export default function MyStayProvider({ const { data, error, isFetching, isFetchedAfterMount } = trpc.booking.get.useQuery( { - confirmationNumber: bookingConfirmation.booking.confirmationNumber, + refId: bookingConfirmation.booking.refId, lang, }, { @@ -68,7 +68,7 @@ export default function MyStayProvider({ } = trpc.booking.linkedReservations.useQuery( { lang, - rooms: bookingConfirmation.booking.linkedReservations, + refId: bookingConfirmation.booking.refId, }, { initialData: linkedReservationsResponses, diff --git a/apps/scandic-web/server/plugins/refIdToConfirmationNumber.ts b/apps/scandic-web/server/plugins/refIdToConfirmationNumber.ts new file mode 100644 index 000000000..4746b2a35 --- /dev/null +++ b/apps/scandic-web/server/plugins/refIdToConfirmationNumber.ts @@ -0,0 +1,46 @@ +import { initTRPC } from "@trpc/server" +import { z } from "zod" + +import { parseRefId } from "@/utils/refId" + +import type { Meta } from "@/types/trpc/meta" +import type { Context } from "../context" + +export function createRefIdPlugin() { + const t = initTRPC.context().meta().create() + + return { + toConfirmationNumber: t.procedure + .input( + z.object({ + refId: z.string(), + }) + ) + .use(({ input, next }) => { + const { confirmationNumber } = parseRefId(input.refId) + return next({ + ctx: { + confirmationNumber, + }, + }) + }), + toConfirmationNumbers: t.procedure + .input( + z.object({ + refIds: z.array(z.string()), + }) + ) + .use(({ input, next }) => { + const confirmationNumbers = input.refIds.map((refId) => { + const { confirmationNumber } = parseRefId(refId) + return confirmationNumber + }) + + return next({ + ctx: { + confirmationNumbers, + }, + }) + }), + } +} diff --git a/apps/scandic-web/server/routers/booking/input.ts b/apps/scandic-web/server/routers/booking/input.ts index 50bcdec28..79a46afc5 100644 --- a/apps/scandic-web/server/routers/booking/input.ts +++ b/apps/scandic-web/server/routers/booking/input.ts @@ -103,7 +103,6 @@ export const createBookingInput = z.object({ }) export const addPackageInput = z.object({ - confirmationNumber: z.string(), ancillaryComment: z.string(), ancillaryDeliveryTime: z.string().nullish(), packages: z.array( @@ -117,22 +116,15 @@ export const addPackageInput = z.object({ }) export const removePackageInput = z.object({ - confirmationNumber: z.string(), codes: z.array(z.string()), language: z.nativeEnum(Lang).transform((val) => langToApiLang[val]), }) -export const priceChangeInput = z.object({ - confirmationNumber: z.string(), -}) - export const cancelBookingsInput = z.object({ - confirmationNumbers: z.array(z.string()), language: z.nativeEnum(Lang), }) export const guaranteeBookingInput = z.object({ - confirmationNumber: z.string(), card: z .object({ alias: z.string(), @@ -156,7 +148,6 @@ export const createRefIdInput = z.object({ }) export const updateBookingInput = z.object({ - confirmationNumber: z.string(), checkInDate: z.string().optional(), checkOutDate: z.string().optional(), guest: z @@ -169,19 +160,13 @@ export const updateBookingInput = z.object({ }) // Query -const confirmationNumberInput = z.object({ - confirmationNumber: z.string(), + +export const getBookingInput = z.object({ lang: z.nativeEnum(Lang).optional(), }) -export const getBookingInput = confirmationNumberInput export const getLinkedReservationsInput = z.object({ lang: z.nativeEnum(Lang).optional(), - rooms: z.array( - z.object({ - confirmationNumber: z.string(), - }) - ), }) export const findBookingInput = z.object({ @@ -194,4 +179,6 @@ export const findBookingInput = z.object({ export type LinkedReservationsInput = z.input -export const getBookingStatusInput = confirmationNumberInput +export const getBookingStatusInput = z.object({ + lang: z.nativeEnum(Lang).optional(), +}) diff --git a/apps/scandic-web/server/routers/booking/mutation.ts b/apps/scandic-web/server/routers/booking/mutation.ts index ce648e7ec..09c75c9bd 100644 --- a/apps/scandic-web/server/routers/booking/mutation.ts +++ b/apps/scandic-web/server/routers/booking/mutation.ts @@ -1,4 +1,5 @@ import * as api from "@/lib/api" +import { createRefIdPlugin } from "@/server/plugins/refIdToConfirmationNumber" import { getMembershipNumber } from "@/server/routers/user/utils" import { createCounter } from "@/server/telemetry" import { router, safeProtectedServiceProcedure } from "@/server/trpc" @@ -8,13 +9,14 @@ import { cancelBookingsInput, createBookingInput, guaranteeBookingInput, - priceChangeInput, removePackageInput, updateBookingInput, } from "./input" import { bookingConfirmationSchema, createBookingSchema } from "./output" import { cancelBooking } from "./utils" +const refIdPlugin = createRefIdPlugin() + export const bookingMutationRouter = router({ create: safeProtectedServiceProcedure .input(createBookingInput) @@ -72,9 +74,9 @@ export const bookingMutationRouter = router({ return verifiedData.data }), priceChange: safeProtectedServiceProcedure - .input(priceChangeInput) - .mutation(async function ({ ctx, input }) { - const { confirmationNumber } = input + .concat(refIdPlugin.toConfirmationNumber) + .mutation(async function ({ ctx }) { + const { confirmationNumber } = ctx const priceChangeCounter = createCounter("trpc.booking", "price-change") const metricsPriceChange = priceChangeCounter.init({ confirmationNumber }) @@ -91,7 +93,6 @@ export const bookingMutationRouter = router({ api.endpoints.v1.Booking.priceChange(confirmationNumber), { headers, - body: input, } ) @@ -113,9 +114,11 @@ export const bookingMutationRouter = router({ }), cancel: safeProtectedServiceProcedure .input(cancelBookingsInput) + .concat(refIdPlugin.toConfirmationNumbers) .mutation(async function ({ ctx, input }) { const token = ctx.session?.token.access_token ?? ctx.serviceToken - const { confirmationNumbers, language } = input + const { confirmationNumbers } = ctx + const { language } = input const responses = await Promise.allSettled( confirmationNumbers.map((confirmationNumber) => @@ -144,9 +147,11 @@ export const bookingMutationRouter = router({ }), packages: safeProtectedServiceProcedure .input(addPackageInput) + .concat(refIdPlugin.toConfirmationNumber) .mutation(async function ({ ctx, input }) { const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken - const { confirmationNumber, ...body } = input + const { confirmationNumber } = ctx + const { refId, ...body } = input const addPackageCounter = createCounter("trpc.booking", "package.add") const metricsAddPackage = addPackageCounter.init({ confirmationNumber }) @@ -183,9 +188,11 @@ export const bookingMutationRouter = router({ }), guarantee: safeProtectedServiceProcedure .input(guaranteeBookingInput) + .concat(refIdPlugin.toConfirmationNumber) .mutation(async function ({ ctx, input }) { const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken - const { confirmationNumber, language, ...body } = input + const { confirmationNumber } = ctx + const { refId, language, ...body } = input const guaranteeBookingCounter = createCounter("trpc.booking", "guarantee") const metricsGuaranteeBooking = guaranteeBookingCounter.init({ @@ -225,9 +232,11 @@ export const bookingMutationRouter = router({ }), update: safeProtectedServiceProcedure .input(updateBookingInput) + .concat(refIdPlugin.toConfirmationNumber) .mutation(async function ({ ctx, input }) { const accessToken = ctx.session?.token.access_token || ctx.serviceToken - const { confirmationNumber, ...body } = input + const { confirmationNumber } = ctx + const { refId, ...body } = input const updateBookingCounter = createCounter("trpc.booking", "update") const metricsUpdateBooking = updateBookingCounter.init({ @@ -265,9 +274,11 @@ export const bookingMutationRouter = router({ }), removePackage: safeProtectedServiceProcedure .input(removePackageInput) + .concat(refIdPlugin.toConfirmationNumber) .mutation(async function ({ ctx, input }) { const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken - const { confirmationNumber, codes, language } = input + const { confirmationNumber } = ctx + const { codes, language } = input const removePackageCounter = createCounter( "trpc.booking", diff --git a/apps/scandic-web/server/routers/booking/output.ts b/apps/scandic-web/server/routers/booking/output.ts index 072b2681b..b03cbc081 100644 --- a/apps/scandic-web/server/routers/booking/output.ts +++ b/apps/scandic-web/server/routers/booking/output.ts @@ -2,6 +2,7 @@ import { z } from "zod" import { BookingStatusEnum, ChildBedTypeEnum } from "@/constants/booking" +import { calculateRefId } from "@/utils/refId" import { nullableArrayObjectValidator } from "@/utils/zod/arrayValidator" import { nullableIntValidator } from "@/utils/zod/numberValidator" import { @@ -78,7 +79,13 @@ export const createBookingSchema = z type: d.data.type, reservationStatus: d.data.attributes.reservationStatus, paymentUrl: d.data.attributes.paymentUrl, - rooms: d.data.attributes.rooms, + rooms: d.data.attributes.rooms.map((room) => { + const lastName = d.data.attributes.guest?.lastName ?? "" + return { + ...room, + refId: calculateRefId(room.confirmationNumber, lastName), + } + }), errors: d.data.attributes.errors, guest: d.data.attributes.guest, })) @@ -248,6 +255,31 @@ export const bookingConfirmationSchema = z }) .transform(({ data }) => ({ ...data.attributes, + refId: calculateRefId( + data.attributes.confirmationNumber, + data.attributes.guest.lastName + ), + linkedReservations: data.attributes.linkedReservations.map( + (linkedReservation) => { + /** + * We lazy load linked reservations in the client. + * The problem is that we need to load the reservation in order to + * calculate the refId for the reservation as the refId uses the guest's + * lastname in it. Ideally we should pass a promise to the React + * component that uses `use()` to resolve it. But right now we use tRPC + * in the client. That tRPC endpoint only uses the confirmationNumber + * from the refId. So that means we can pass whatever as the lastname + * here, because it is actually never read. We should change this ASAP. + */ + return { + ...linkedReservation, + refId: calculateRefId( + linkedReservation.confirmationNumber, + "" // TODO: Empty lastname here, see comment above + ), + } + } + ), packages: data.attributes.packages.filter((p) => p.type !== "Ancillary"), ancillaries: data.attributes.packages.filter((p) => p.type === "Ancillary"), extraBedTypes: data.attributes.childBedPreferences, diff --git a/apps/scandic-web/server/routers/booking/query.ts b/apps/scandic-web/server/routers/booking/query.ts index 2c8a518d3..66cd08f06 100644 --- a/apps/scandic-web/server/routers/booking/query.ts +++ b/apps/scandic-web/server/routers/booking/query.ts @@ -1,5 +1,6 @@ import * as api from "@/lib/api" import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc" +import { createRefIdPlugin } from "@/server/plugins/refIdToConfirmationNumber" import { createCounter } from "@/server/telemetry" import { router, @@ -7,8 +8,10 @@ import { serviceProcedure, } from "@/server/trpc" +import { getBookedHotelRoom } from "@/utils/booking" + +import { encrypt } from "../../../utils/encryption" import { getHotel } from "../hotels/utils" -import { encrypt } from "../utils/encryption" import { createRefIdInput, findBookingInput, @@ -17,28 +20,31 @@ import { getLinkedReservationsInput, } from "./input" import { createBookingSchema } from "./output" -import { findBooking, getBookedHotelRoom, getBooking } from "./utils" +import { findBooking, getBooking } from "./utils" + +const refIdPlugin = createRefIdPlugin() export const bookingQueryRouter = router({ get: safeProtectedServiceProcedure .input(getBookingInput) + .concat(refIdPlugin.toConfirmationNumber) .use(async ({ ctx, input, next }) => { const lang = input.lang ?? ctx.lang - const token = ctx.session?.token.access_token ?? ctx.serviceToken return next({ ctx: { lang, - token, }, }) }) - .query(async function ({ ctx, input: { confirmationNumber } }) { + .query(async function ({ ctx }) { + const { confirmationNumber, lang, serviceToken } = ctx + const getBookingCounter = createCounter("trpc.booking", "get") const metricsGetBooking = getBookingCounter.init({ confirmationNumber }) metricsGetBooking.start() - const booking = await getBooking(confirmationNumber, ctx.lang, ctx.token) + const booking = await getBooking(confirmationNumber, lang, serviceToken) if (!booking) { metricsGetBooking.dataError( @@ -52,9 +58,9 @@ export const bookingQueryRouter = router({ { hotelId: booking.hotelId, isCardOnlyPayment: false, - language: ctx.lang, + language: lang, }, - ctx.serviceToken + serviceToken ) if (!hotelData) { @@ -141,6 +147,7 @@ export const bookingQueryRouter = router({ }), linkedReservations: safeProtectedServiceProcedure .input(getLinkedReservationsInput) + .concat(refIdPlugin.toConfirmationNumber) .use(async ({ ctx, input, next }) => { const lang = input.lang ?? ctx.lang const token = ctx.session?.token.access_token ?? ctx.serviceToken @@ -151,27 +158,36 @@ export const bookingQueryRouter = router({ }, }) }) - .query(async function ({ ctx, input: { rooms } }) { + .query(async function ({ ctx }) { + const { confirmationNumber, lang, token } = ctx + const getLinkedReservationsCounter = createCounter( "trpc.booking", "linkedReservations" ) const metricsGetLinkedReservations = getLinkedReservationsCounter.init({ - confirmationNumbers: rooms, + confirmationNumber, }) metricsGetLinkedReservations.start() - const linkedReservationsResult = await Promise.allSettled( - rooms.map((room) => - getBooking(room.confirmationNumber, ctx.lang, ctx.token) + const booking = await getBooking(confirmationNumber, lang, token) + + if (!booking) { + return [] + } + + const linkedReservationsResults = await Promise.allSettled( + booking.linkedReservations.map((linkedReservation) => + getBooking(linkedReservation.confirmationNumber, lang, token) ) ) + const linkedReservations = [] - for (const booking of linkedReservationsResult) { - if (booking.status === "fulfilled") { - if (booking.value) { - linkedReservations.push(booking.value) + for (const linkedReservationsResult of linkedReservationsResults) { + if (linkedReservationsResult.status === "fulfilled") { + if (linkedReservationsResult.value) { + linkedReservations.push(linkedReservationsResult.value) } else { metricsGetLinkedReservations.dataError( `Unexpected value for linked reservation` @@ -188,44 +204,44 @@ export const bookingQueryRouter = router({ return linkedReservations }), - status: serviceProcedure.input(getBookingStatusInput).query(async function ({ - ctx, - input, - }) { - const { confirmationNumber } = input + status: serviceProcedure + .input(getBookingStatusInput) + .concat(refIdPlugin.toConfirmationNumber) + .query(async function ({ ctx }) { + const { confirmationNumber } = ctx - const getBookingStatusCounter = createCounter("trpc.booking", "status") - const metricsGetBookingStatus = getBookingStatusCounter.init({ - confirmationNumber, - }) + const getBookingStatusCounter = createCounter("trpc.booking", "status") + const metricsGetBookingStatus = getBookingStatusCounter.init({ + confirmationNumber, + }) - metricsGetBookingStatus.start() + metricsGetBookingStatus.start() - const apiResponse = await api.get( - api.endpoints.v1.Booking.status(confirmationNumber), - { - headers: { - Authorization: `Bearer ${ctx.serviceToken}`, - }, + const apiResponse = await api.get( + api.endpoints.v1.Booking.status(confirmationNumber), + { + headers: { + Authorization: `Bearer ${ctx.serviceToken}`, + }, + } + ) + + if (!apiResponse.ok) { + await metricsGetBookingStatus.httpError(apiResponse) + throw serverErrorByStatus(apiResponse.status, apiResponse) } - ) - if (!apiResponse.ok) { - await metricsGetBookingStatus.httpError(apiResponse) - throw serverErrorByStatus(apiResponse.status, apiResponse) - } + const apiJson = await apiResponse.json() + const verifiedData = createBookingSchema.safeParse(apiJson) + if (!verifiedData.success) { + metricsGetBookingStatus.validationError(verifiedData.error) + throw badRequestError() + } - const apiJson = await apiResponse.json() - const verifiedData = createBookingSchema.safeParse(apiJson) - if (!verifiedData.success) { - metricsGetBookingStatus.validationError(verifiedData.error) - throw badRequestError() - } + metricsGetBookingStatus.success() - metricsGetBookingStatus.success() - - return verifiedData.data - }), + return verifiedData.data + }), createRefId: serviceProcedure .input(createRefIdInput) .mutation(async function ({ input }) { diff --git a/apps/scandic-web/server/routers/booking/utils.ts b/apps/scandic-web/server/routers/booking/utils.ts index 2d9e581a0..414fe84ed 100644 --- a/apps/scandic-web/server/routers/booking/utils.ts +++ b/apps/scandic-web/server/routers/booking/utils.ts @@ -5,35 +5,8 @@ import { toApiLang } from "@/server/utils" import { bookingConfirmationSchema, createBookingSchema } from "./output" -import type { Room } from "@/types/hotel" -import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" import type { Lang } from "@/constants/languages" -export function getBookedHotelRoom( - rooms: Room[], - roomTypeCode: BookingConfirmation["booking"]["roomTypeCode"] -) { - if (!rooms.length || !roomTypeCode) { - return null - } - const room = rooms.find((r) => { - return r.roomTypes.find((roomType) => roomType.code === roomTypeCode) - }) - if (!room) { - return null - } - const bedType = room.roomTypes.find( - (roomType) => roomType.code === roomTypeCode - ) - if (!bedType) { - return null - } - return { - ...room, - bedType, - } -} - export async function getBooking( confirmationNumber: string, lang: Lang, diff --git a/apps/scandic-web/server/routers/user/utils.ts b/apps/scandic-web/server/routers/user/utils.ts index 369519cbd..da9f02311 100644 --- a/apps/scandic-web/server/routers/user/utils.ts +++ b/apps/scandic-web/server/routers/user/utils.ts @@ -4,10 +4,10 @@ import { myStay } from "@/constants/routes/myStay" import { env } from "@/env/server" import * as api from "@/lib/api" import { dt } from "@/lib/dt" -import { encrypt } from "@/server/routers/utils/encryption" import { createCounter } from "@/server/telemetry" import { cache } from "@/utils/cache" +import { encrypt } from "@/utils/encryption" import * as maskValue from "@/utils/maskValue" import { isValidSession } from "@/utils/session" import { getCurrentWebUrl } from "@/utils/url" diff --git a/apps/scandic-web/stores/my-stay/index.ts b/apps/scandic-web/stores/my-stay/index.ts index fd6544ebc..80fe957b8 100644 --- a/apps/scandic-web/stores/my-stay/index.ts +++ b/apps/scandic-web/stores/my-stay/index.ts @@ -3,10 +3,9 @@ import { produce } from "immer" import { useContext } from "react" import { create, useStore } from "zustand" -import { getBookedHotelRoom } from "@/server/routers/booking/utils" - import { mapRoomDetails } from "@/components/HotelReservation/MyStay/utils/mapRoomDetails" import { MyStayContext } from "@/contexts/MyStay" +import { getBookedHotelRoom } from "@/utils/booking" import { calculateTotalPoints, diff --git a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts index 609fe53d2..05e8d198a 100644 --- a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts +++ b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/bookingConfirmation.ts @@ -5,7 +5,7 @@ import type { } from "@/types/trpc/routers/booking/confirmation" export interface BookingConfirmationProps { - confirmationNumber: string + refId: string } export interface BookingConfirmationRoom extends Room { diff --git a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts index 1c29fc0d3..a6ce40f75 100644 --- a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts +++ b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms.ts @@ -1,11 +1,5 @@ -import type { z } from "zod" - import type { Room } from "@/types/hotel" import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" -import type { linkedReservationSchema } from "@/server/routers/booking/output" - -export interface LinkedReservationSchema - extends z.output {} export interface BookingConfirmationRoomsProps extends Pick { @@ -14,5 +8,4 @@ export interface BookingConfirmationRoomsProps } checkInTime: string checkOutTime: string - linkedReservations: LinkedReservationSchema[] } diff --git a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms/linkedReservation.ts b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms/linkedReservation.ts index 37aff477a..bd6a1aae8 100644 --- a/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms/linkedReservation.ts +++ b/apps/scandic-web/types/components/hotelReservation/bookingConfirmation/rooms/linkedReservation.ts @@ -1,7 +1,7 @@ export interface LinkedReservationProps { checkInTime: string checkOutTime: string - confirmationNumber: string + refId: string roomIndex: number roomNumber: number } diff --git a/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts b/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts index d65e7d00b..e1af10f26 100644 --- a/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts +++ b/apps/scandic-web/types/components/hotelReservation/myStay/cancelStay.ts @@ -6,7 +6,7 @@ export const cancelStaySchema = z.object({ rooms: z.array( z.object({ checked: z.boolean(), - confirmationNumber: z.string(), + refId: z.string(), }) ), }) diff --git a/apps/scandic-web/types/components/myPages/myStay/ancillaries.ts b/apps/scandic-web/types/components/myPages/myStay/ancillaries.ts index 81eb1dfe2..24ec3b354 100644 --- a/apps/scandic-web/types/components/myPages/myStay/ancillaries.ts +++ b/apps/scandic-web/types/components/myPages/myStay/ancillaries.ts @@ -17,7 +17,6 @@ export interface AncillariesProps extends Pick { packages: Packages | null user: User | null savedCreditCards: CreditCard[] | null - refId: string } export interface AddedAncillariesProps { @@ -41,7 +40,6 @@ export interface AncillaryGridModalProps { export interface AddAncillaryFlowModalProps extends Pick { packages: Packages | null - refId: string user: User | null savedCreditCards: CreditCard[] | null } diff --git a/apps/scandic-web/types/stores/booking-confirmation.ts b/apps/scandic-web/types/stores/booking-confirmation.ts index 28cbfa7c7..b87c71226 100644 --- a/apps/scandic-web/types/stores/booking-confirmation.ts +++ b/apps/scandic-web/types/stores/booking-confirmation.ts @@ -27,6 +27,7 @@ export interface Room { packages: BookingConfirmation["booking"]["packages"] formattedRoomCost: string rateDefinition: BookingConfirmation["booking"]["rateDefinition"] + refId: string roomFeatures?: PackageSchema[] | null roomPoints: number roomPrice: number diff --git a/apps/scandic-web/types/stores/my-stay.ts b/apps/scandic-web/types/stores/my-stay.ts index a9492ec2b..1f527a85e 100644 --- a/apps/scandic-web/types/stores/my-stay.ts +++ b/apps/scandic-web/types/stores/my-stay.ts @@ -30,6 +30,7 @@ export type Room = Pick< | "linkedReservations" | "multiRoom" | "rateDefinition" + | "refId" | "reservationStatus" | "roomPoints" | "roomTypeCode" diff --git a/apps/scandic-web/utils/booking.ts b/apps/scandic-web/utils/booking.ts new file mode 100644 index 000000000..b4f3f6c34 --- /dev/null +++ b/apps/scandic-web/utils/booking.ts @@ -0,0 +1,27 @@ +import type { Room } from "@/types/hotel" +import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation" + +export function getBookedHotelRoom( + rooms: Room[], + roomTypeCode: BookingConfirmation["booking"]["roomTypeCode"] +) { + if (!rooms.length || !roomTypeCode) { + return null + } + const room = rooms.find((r) => { + return r.roomTypes.find((roomType) => roomType.code === roomTypeCode) + }) + if (!room) { + return null + } + const bedType = room.roomTypes.find( + (roomType) => roomType.code === roomTypeCode + ) + if (!bedType) { + return null + } + return { + ...room, + bedType, + } +} diff --git a/apps/scandic-web/server/routers/utils/encryption.ts b/apps/scandic-web/utils/encryption.ts similarity index 92% rename from apps/scandic-web/server/routers/utils/encryption.ts rename to apps/scandic-web/utils/encryption.ts index 09341158e..e194a672a 100644 --- a/apps/scandic-web/server/routers/utils/encryption.ts +++ b/apps/scandic-web/utils/encryption.ts @@ -4,13 +4,11 @@ import crypto from "crypto" import { env } from "@/env/server" -export { decrypt, encrypt } - const algorithm = "DES-ECB" const encryptionKey = env.BOOKING_ENCRYPTION_KEY const bufferKey = Buffer.from(encryptionKey, "utf8") -function encrypt(originalString: string) { +export function encrypt(originalString: string) { try { const cipher = crypto.createCipheriv(algorithm, bufferKey, null) cipher.setAutoPadding(false) @@ -32,7 +30,7 @@ function encrypt(originalString: string) { } } -function decrypt(encryptedString: string) { +export function decrypt(encryptedString: string) { try { const decipher = crypto.createDecipheriv(algorithm, bufferKey, null) decipher.setAutoPadding(false) diff --git a/apps/scandic-web/utils/refId.ts b/apps/scandic-web/utils/refId.ts new file mode 100644 index 000000000..95a0aef05 --- /dev/null +++ b/apps/scandic-web/utils/refId.ts @@ -0,0 +1,21 @@ +import "server-only" + +import { decrypt, encrypt } from "./encryption" + +export function calculateRefId(confirmationNumber: string, lastName: string) { + const encryptedRefId = encrypt(`${confirmationNumber},${lastName}`) + + return encryptedRefId +} + +export function parseRefId(refId: string) { + const data = decrypt(refId) + const parts = data.split(",") + if (parts.length !== 2) { + throw new Error("Invalid refId format") + } + return { + confirmationNumber: parts[0], + lastName: parts[1], + } +}