import * as Sentry from "@sentry/nextjs" import { z } from "zod" import { createLogger } from "@scandic-hotels/common/logger/createLogger" import * as api from "../../../api" import { protectedProcedure } from "../../../procedures" import { getOTPState } from "./otp/getOTPState" import { getSasToken } from "./getSasToken" const outputSchema = z.object({ linkingState: z.enum([ "linked", "alreadyLinked", "dateOfBirthMismatch", "nameNotMatched", "blockedForRelink", "accountToNew", "error", ]), }) const sasLogger = createLogger("SAS") export const linkAccount = protectedProcedure .output(outputSchema) .mutation(async function ({ ctx }) { const sasAuthToken = await getSasToken() const { referenceId } = await getOTPState() sasLogger.debug("[SAS] link account") const apiResponse = await api.post(api.endpoints.v2.Profile.link, { headers: { Authorization: `Bearer ${ctx.session.token.access_token}`, }, body: { partner: "sas_eb", tocDate: getCurrentDateWithoutTime(), partnerSpecific: { eurobonusAccessToken: sasAuthToken, eurobonusOtpReferenceId: referenceId, }, }, }) const linkedAndBoosted = apiResponse.status === 200 const linkedWithoutBoost = apiResponse.status === 204 const linkedWithUnknownBoost = apiResponse.status === 202 const linked = linkedAndBoosted || linkedWithoutBoost || linkedWithUnknownBoost if (linked) { sasLogger.debug("[SAS] link account done") return { linkingState: "linked" } } if (apiResponse.status === 400) { const result = await apiResponse.json() const data = badRequestSchema.safeParse(result) if (!data.success) { const linkAccountBadRequestSchemaError = `[SAS] failed to parse link account bad request schema ${JSON.stringify(data.error)}` sasLogger.error(linkAccountBadRequestSchemaError) Sentry.captureMessage(linkAccountBadRequestSchemaError) return { linkingState: "error" } } sasLogger.error("[SAS] link account error with response", result) const { errors } = data.data if (errors.some((x) => x.code === "BirthDateNotMatched")) { return { linkingState: "dateOfBirthMismatch" } } if (errors.some((x) => x.code === "BlockedForRelink")) { return { linkingState: "blockedForRelink" } } if (errors.some((x) => x.code === "AccountToNew")) { return { linkingState: "accountToNew" } } if (errors.some((x) => x.code === "NameNotMatched")) { return { linkingState: "nameNotMatched" } } if (errors.some((x) => x.code === "AlreadyLinked")) { return { linkingState: "alreadyLinked" } } return { linkingState: "error" } } if (apiResponse.status === 409) { return { linkingState: "alreadyLinked" } } const errorMessage = `[SAS] link account error with status code ${apiResponse.status} and response ${await apiResponse.text()}` sasLogger.error(errorMessage) Sentry.captureMessage(errorMessage) return { linkingState: "error" } }) function getCurrentDateWithoutTime() { return new Date().toISOString().slice(0, 10) } const badRequestSchema = z.object({ errors: z.array( z.object({ code: z.enum([ "BirthDateNotMatched", "NameNotMatched", "AlreadyLinked", "BlockedForRelink", "AccountToNew", "UnknownReason", ]), details: z.string().optional(), }) ), })