import {z} from 'zod' export type HotelAncillariesRequest = {priceValidateKey: string}; export type CreateApiV2HotelAncillariesArgs = {body: HotelAncillariesRequest}; export type CreateApiV2HotelAncillariesOptions = {baseUrl?: string | undefined, Bearer?: string | undefined, fetch?: typeof fetch | undefined}; export const paymentMethod = z.enum(["PAYMENT_METHOD_UNKNOWN", "CREDIT_CARD", "BREX_POINTS", "CASH", "QANTAS_POINTS", "VENDOR_PROGRAM_PAYMENT", "DELAYED_INVOICING", "FLIGHT_CREDITS", "QANTAS_TRAVEL_FUND", "CUSTOM_VIRTUAL_PAYMENT", "FLIGHT_PASS", "MISCELLANEOUS_CREDIT_ORDER", "NO_PREPAYMENT", "DELAYED_PAYMENT"]); export const flightPassTax = z.object({code: z.string().optional(), amount: z.lazy(() => money.optional())}); export const flightPassPaymentAmounts = z.object({totalAmount: z.lazy(() => money.optional()), baseAmount: z.lazy(() => money.optional()), totalTaxes: z.lazy(() => money.optional()), taxBreakdown: z.array(flightPassTax).optional()}); export const flightPassPaymentMetadata = z.object({flightPassNumber: z.string().optional(), flightPassAmounts: flightPassPaymentAmounts.optional()}); export const flightPassPaymentMetadataWrapper = z.object({metadataType: z.string().optional(), flightPassMetadata: flightPassPaymentMetadata}); export const money: z.ZodType = z.object({amount: z.number(), currencyCode: z.string(), convertedAmount: z.number().optional(), convertedCurrency: z.string().optional(), otherCoinage: z.array(z.object({coinageCode: paymentMethod.optional(), amount: z.number().optional(), conversionRate: z.number().optional(), preferredCurrencyConversionRate: z.number().optional(), otherCoinageMetadata: flightPassPaymentMetadataWrapper.optional()})).optional()}); export const roomFee = z.object({amount: money, feeInclusions: z.array(z.string()).optional(), displayFee: z.boolean().optional(), feeType: z.enum(["UNKNOWN", "BED_TAX", "CITY_HOTEL_FEE", "CITY_TAX", "COUNTRY_TAX", "ENERGY_TAX", "FEDERAL_TAX", "FOOD_AND_BEVERAGE_TAX", "LODGING_TAX", "MAINTENANCE_FEE", "OCCUPANCY_TAX", "PACKAGE_FEE", "RESORT_FEE", "SALES_TAX", "SERVICE_CHARGE", "STATE_TAX", "SURCHARGE", "TOTAL_TAX", "TOURISM_TAX", "VAT_GST_TAX", "SURPLUS_LINES_TAX", "INSURANCE_PREMIUM_TAX", "APPLICATON_FEE", "EXPRESS_HANDLING_FEE", "EXEMPT", "STANDARD", "ZERO_RATED", "MISCELLANEOUS", "ROOM_TAX", "EARLY_CHECKOUT_FEE", "COUNTRY_TAXES", "EXTRA_PERSON_CHARGE", "BANQUET_SERVICE_FEE", "ROOM_SERVICE_FEE", "LOCAL_FEE", "GOODS_AND_SERVICES_TAX", "VALUE_ADDED_TAX", "CRIB_FEE", "ROLLAWAY_FEE", "ASSESSMENT_LICENSE_TAX", "PET_SANITATION_FEE", "NOT_KNOWN", "CHILD_ROLLAWAY_CHARGE", "CONVENTION_TAX", "EXTRA_CHILD_CHARGE", "STANDARD_FOOD_AND_BEVERAGE_GRATUITY", "NATIONAL_GOVERNMENT_TAX", "ADULT_ROLLAWAY_FEE", "BEVERAGE_WITH_ALCOHOL", "BEVERAGE_WITHOUT_ALCOHOL", "TOBACCO", "FOOD", "TOTAL_SURCHARGES", "STATE_COST_RECOVERY_FEE", "MISCELLANEOUS_FEE", "DESTINATION_AMENITY_FEE", "REFUNDABLE_PET_FEE", "CHARITY_SUPPORT_FEE", "LOCAL_AMENITY_USAGE_MAINTENANCE_FEE", "CONVENTION_TOURISM_FEE", "DESTINATION_MARKETING_FEE", "HOTEL_DEVELOPMENT_FEE", "EVENT_FEE", "SUSTAINABILITY_FEE", "TRANSPORTATION_TRANSFER_FEE", "INSURANCE_FEE", "LOCAL_GOVERNMENT_FEE", "LOCAL_ORDINANCE_SURCHARGE", "GUARANTEED_EARLY_CHECK_IN_FEE", "GUARANTEED_LATE_CHECK_OUT_FEE", "MODIFICATION_FEE", "HOTEL_ANCILLARY_EARLY_CHECK_IN_FEE", "HOTEL_ANCILLARY_LATE_CHECK_OUT_FEE", "HOTEL_ANCILLARY_PET_FEE", "HOTEL_ANCILLARY_WIFI_FEE", "HOTEL_ANCILLARY_BREAKFAST_FEE", "HOTEL_ANCILLARY_PARKING_FEE", "HOTEL_ANCILLARY_MEAL_PLAN_FEE", "ACCESSIBILITY_EQUIPMENT_FEE", "TOURISM_MARKETING_FEE"]).optional()}); export const commission = z.object({amount: money, commissionPercent: z.number().optional()}); export const tax = z.object({amount: money, taxCode: z.string().optional(), percentage: z.number().optional(), description: z.string().optional()}); export const taxBreakdown = z.array(tax); export const hotelRoomRate = z.object({base: money, tax: money, roomFees: z.array(roomFee).optional(), commission: commission.optional(), taxBreakdown: taxBreakdown.optional(), includesCommission: z.boolean().optional()}); export const hotelAncillary = z.object({ancillaryKey: z.string(), category: z.enum(["ANCILLARY_UNKNOWN", "EARLY_CHECKIN", "LATE_CHECKOUT", "WIFI", "BREAKFAST", "PARKING", "PETS", "MEAL_PLAN", "OTHER"]), displayName: z.string(), description: z.string().optional(), dailyPrice: money, totalPrice: money, refundability: z.enum(["REFUNDABILITY_UNKNOWN", "FULLY_REFUNDABLE", "PARTIALLY_REFUNDABLE", "NON_REFUNDABLE"]), purchaseStatus: z.enum(["PURCHASE_STATUS_UNKNOWN", "AVAILABLE", "ALREADY_PURCHASED", "INCLUDED_IN_RATE"]), paymentType: z.enum(["PAYMENT_TYPE_UNKNOWN", "PREPAID", "POSTPAID"]), totalRate: hotelRoomRate.optional(), prepaidRate: hotelRoomRate.optional(), postpaidRate: hotelRoomRate.optional(), status: z.enum(["ANCILLARY_STATUS_UNKNOWN", "ACTIVE", "CANCELLED", "MODIFIED"]).optional()}); export const hotelAncillariesResponse = z.object({ancillaries: z.array(hotelAncillary).optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Get hotel ancillaries * * POST /v2/hotel/ancillaries */ export const createApiV2HotelAncillaries = async (args: CreateApiV2HotelAncillariesArgs, options?: CreateApiV2HotelAncillariesOptions) => { const {baseUrl = "https://api-ext-sboxmeta.partners.spotnana.com", fetch: fetchFn = fetch, Bearer} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (Bearer !== undefined) headers.set('authorization', `Bearer ${Bearer}`) const url = `${baseUrl.replace(/\/+$/, '')}/v2/hotel/ancillaries${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(hotelAncillariesResponse, await res.json()) };