feat(SW-3526): Show EB points rate and label in booking flow * feat(SW-3526): Show EB points rate and label in booking flow * feat(SW-3526) Optimized points currency code * feat(SW-3526) Removed extra multiplication for token expiry after rebase * feat(SW-3526): Updated to exhaustive check and thow if type error Approved-by: Anton Gunnarsson
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
import { z } from "zod"
|
|
|
|
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
|
|
|
|
import { env } from "../../../../env/server"
|
|
import { protectedProcedure } from "../../../procedures"
|
|
|
|
import type { Session } from "next-auth"
|
|
|
|
const outputSchema = z.object({
|
|
eurobonusNumber: z.string(),
|
|
firstName: z.string().optional(),
|
|
lastName: z.string().optional(),
|
|
linkStatus: z.enum(["LINKED", "PENDING", "UNLINKED"]),
|
|
isBlocked: z.boolean(),
|
|
enrollmentDate: z.string(),
|
|
birthdate: z.string(),
|
|
mobileNumber: z.string(),
|
|
email: z.string().email(),
|
|
points: z.object({
|
|
balances: z.array(
|
|
z.object({
|
|
expirationDate: z.string(),
|
|
points: z.number(),
|
|
})
|
|
),
|
|
total: z.number(),
|
|
}),
|
|
tier: z.string(),
|
|
tierStartDate: z.string(),
|
|
/* `null` if customer is (i.) Lifetime Gold and tier is Gold or (ii.) tier is Basic. If customer is Lifetime Gold and tier is above Gold (e.g., Diamond), there will be a tier end date */
|
|
tierEndDate: z.string().nullable(),
|
|
lifeTimeGold: z.boolean(),
|
|
tierBoostRequestedByScandic: z.boolean(),
|
|
whoBoosted: z
|
|
.enum(["NO-BOOST", "SAS", "SCANDIC", "UNKNOWN"])
|
|
.default("UNKNOWN")
|
|
.catch((ctx) => {
|
|
sasLogger.warn(`Unknown whoBoosted value received: ${ctx.input}`)
|
|
return "UNKNOWN"
|
|
}),
|
|
tierBoostDescription: z.string().optional(),
|
|
tierMatchStatus: z
|
|
.enum(["MATCHED", "FAILED", "PENDING", "UNMATCHED"])
|
|
.optional(),
|
|
})
|
|
|
|
const sasLogger = createLogger("SAS")
|
|
const url = new URL("/api/scandic-partnership/v1/profile", env.SAS_API_ENDPOINT)
|
|
|
|
export async function getEuroBonusProfileData(session: Session) {
|
|
if (session.token.loginType !== "sas") {
|
|
return {
|
|
error: {
|
|
message: `Failed to fetch EuroBonus profile, expected loginType to be "sas" but was ${session.token.loginType}`,
|
|
},
|
|
} as const
|
|
}
|
|
|
|
if (!session.token.expires_at || session.token.expires_at < Date.now()) {
|
|
return {
|
|
error: {
|
|
message: "Token expired sas",
|
|
},
|
|
} as const
|
|
}
|
|
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Ocp-Apim-Subscription-Key": env.SAS_OCP_APIM,
|
|
Authorization: `Bearer ${session?.token?.access_token}`,
|
|
},
|
|
})
|
|
|
|
if (!response.ok) {
|
|
sasLogger.error(
|
|
`Failed to get EuroBonus profile, status: ${response.status}, statusText: ${response.statusText}`
|
|
)
|
|
return {
|
|
error: {
|
|
message: "Failed to fetch EuroBonus profile",
|
|
cause: { status: response.status, statusText: response.statusText },
|
|
},
|
|
} as const
|
|
}
|
|
|
|
const responseJson = await response.json()
|
|
const data = outputSchema.safeParse(responseJson)
|
|
if (!data.success) {
|
|
sasLogger.error(
|
|
`Failed to parse EuroBonus profile, cause: ${data.error.cause}, message: ${data.error.message}`
|
|
)
|
|
return {
|
|
error: {
|
|
message: `Failed to parse EuroBonus profile: ${data.error.message}`,
|
|
cause: { status: response.status, statusText: response.statusText },
|
|
},
|
|
} as const
|
|
}
|
|
return data
|
|
}
|
|
|
|
export const getEuroBonusProfile = protectedProcedure.query(async function ({
|
|
ctx,
|
|
}) {
|
|
const verifiedSasUser = await getEuroBonusProfileData(ctx.session)
|
|
if ("error" in verifiedSasUser) {
|
|
throw new Error(verifiedSasUser.error?.message, {
|
|
cause: verifiedSasUser.error?.cause,
|
|
})
|
|
}
|
|
return verifiedSasUser.data
|
|
})
|