import { myStay } from "@scandic-hotels/common/constants/routes/myStay" import { dt } from "@scandic-hotels/common/dt" import { createCounter } from "@scandic-hotels/common/telemetry" import * as api from "@scandic-hotels/trpc/api" import { countries } from "@scandic-hotels/trpc/constants/countries" import { getFriendsMembership } from "@scandic-hotels/trpc/routers/user/helpers" import { creditCardsSchema } from "@scandic-hotels/trpc/routers/user/output" import { getVerifiedUser } from "@scandic-hotels/trpc/routers/user/utils" import { toApiLang } from "@scandic-hotels/trpc/utils" import { myBookingPath } from "@/constants/myBooking" import { env } from "@/env/server" 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" import { type FriendTransaction, getStaysSchema, type Stay } from "./output" import type { Lang } from "@scandic-hotels/common/constants/language" import type { User } from "@scandic-hotels/trpc/types/user" import type { Session } from "next-auth" export async function getMembershipNumber( session: Session | null ): Promise { if (!isValidSession(session)) return undefined const verifiedUser = await getVerifiedUser({ session }) if (!verifiedUser || "error" in verifiedUser) { return undefined } return verifiedUser.data.membershipNumber } export async function getPreviousStays( accessToken: string, limit: number = 10, language: Lang, cursor?: string ) { const getPreviousStaysCounter = createCounter("user", "getPreviousStays") const metricsGetPreviousStays = getPreviousStaysCounter.init({ limit, cursor, language, }) metricsGetPreviousStays.start() const params: Record = { limit: String(limit), language: toApiLang(language), } if (cursor) { params.offset = cursor } const apiResponse = await api.get( api.endpoints.v1.Booking.Stays.past, { headers: { Authorization: `Bearer ${accessToken}`, }, }, params ) if (!apiResponse.ok) { await metricsGetPreviousStays.httpError(apiResponse) return null } const apiJson = await apiResponse.json() const verifiedData = getStaysSchema.safeParse(apiJson) if (!verifiedData.success) { metricsGetPreviousStays.validationError(verifiedData.error) return null } metricsGetPreviousStays.success() return verifiedData.data } export async function getUpcomingStays( accessToken: string, limit: number = 10, language: Lang, cursor?: string ) { const getUpcomingStaysCounter = createCounter("user", "getUpcomingStays") const metricsGetUpcomingStays = getUpcomingStaysCounter.init({ limit, cursor, language, }) metricsGetUpcomingStays.start() const params: Record = { limit: String(limit), language: toApiLang(language), } if (cursor) { params.offset = cursor } const apiResponse = await api.get( api.endpoints.v1.Booking.Stays.future, { headers: { Authorization: `Bearer ${accessToken}`, }, }, params ) if (!apiResponse.ok) { await metricsGetUpcomingStays.httpError(apiResponse) return null } const apiJson = await apiResponse.json() const verifiedData = getStaysSchema.safeParse(apiJson) if (!verifiedData.success) { metricsGetUpcomingStays.validationError(verifiedData.error) return null } metricsGetUpcomingStays.success() return verifiedData.data } export function parsedUser(data: User, isMFA: boolean) { const country = countries.find((c) => c.code === data.address?.countryCode) const user = { address: { city: data.address?.city, country: country?.name ?? "", countryCode: data.address?.countryCode, streetAddress: data.address?.streetAddress, zipCode: data.address?.zipCode, }, dateOfBirth: data.dateOfBirth, email: data.email, firstName: data.firstName, language: data.language, lastName: data.lastName, membershipNumber: data.membershipNumber, membership: data.loyalty ? getFriendsMembership(data.loyalty) : null, loyalty: data.loyalty, name: `${data.firstName} ${data.lastName}`, phoneNumber: data.phoneNumber, profileId: data.profileId, } if (!isMFA) { if (user.address.city) { user.address.city = maskValue.text(user.address.city) } if (user.address.streetAddress) { user.address.streetAddress = maskValue.text(user.address.streetAddress) } user.address.zipCode = data.address?.zipCode ? maskValue.text(data.address.zipCode) : "" user.dateOfBirth = maskValue.all(user.dateOfBirth) user.email = maskValue.email(user.email) user.phoneNumber = user.phoneNumber ? maskValue.phone(user.phoneNumber) : "" } return user } export const getCreditCards = cache( async ({ session, onlyNonExpired, }: { session: Session onlyNonExpired?: boolean }) => { const getCreditCardsCounter = createCounter("user", "getCreditCards") const metricsGetCreditCards = getCreditCardsCounter.init({ onlyNonExpired, }) metricsGetCreditCards.start() const apiResponse = await api.get(api.endpoints.v1.Profile.creditCards, { headers: { Authorization: `Bearer ${session.token.access_token}`, }, }) if (!apiResponse.ok) { await metricsGetCreditCards.httpError(apiResponse) return null } const apiJson = await apiResponse.json() const verifiedData = creditCardsSchema.safeParse(apiJson) if (!verifiedData.success) { metricsGetCreditCards.validationError(verifiedData.error) return null } const result = verifiedData.data.data.filter((card) => { if (onlyNonExpired) { try { const expirationDate = dt(card.expirationDate).startOf("day") const currentDate = dt().startOf("day") return expirationDate > currentDate } catch (_) { return false } } return true }) metricsGetCreditCards.success() return result } ) export async function updateStaysBookingUrl( data: Stay[], session: Session, lang: Lang ): Promise export async function updateStaysBookingUrl( data: FriendTransaction[], session: Session, lang: Lang ): Promise export async function updateStaysBookingUrl( data: Stay[] | FriendTransaction[], session: Session, lang: Lang ) { const user = await getVerifiedUser({ session, }) if (user && !("error" in user)) { return data.map((d) => { const originalString = d.attributes.confirmationNumber.toString() + "," + user.data.lastName const encryptedBookingValue = encrypt(originalString) // Get base URL with fallback for ephemeral environments (like deploy previews). const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com" // Construct Booking URL. const bookingUrl = !env.isLangLive(lang) ? new URL( getCurrentWebUrl({ path: myBookingPath[lang], lang, baseUrl, }) ) : new URL(myStay[lang], baseUrl) // Add search parameters. if (encryptedBookingValue) { bookingUrl.searchParams.set("RefId", encryptedBookingValue) } else { bookingUrl.searchParams.set("lastName", user.data.lastName) bookingUrl.searchParams.set( "bookingId", d.attributes.confirmationNumber.toString() ) } return { ...d, attributes: { ...d.attributes, bookingUrl: bookingUrl.toString(), }, } }) } return data }