import {z} from 'zod' export type PromotionDiscounts = Array<{unit: 'PERCENTAGE_OFF' | 'AMOUNT_OFF' | 'FIXED', amount: number, type: 'SKU' | 'CART' | 'SERVICE' | 'SERVICE_CART', values?: (string | null | Array>) | undefined, maximumQuantity?: number | undefined, includeOperator?: ('AND' | 'OR') | undefined, excludeOperator?: ('AND' | 'OR') | undefined, discountId?: string | undefined, isAutoAdd?: boolean | undefined, sets?: (Array<'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'>) | undefined}>; export type PromotionTargetProduct = Array<{type: 'SKU' | 'CATEGORY' | 'ATTRIBUTE' | 'PRICE_TYPE' | 'COLLECTION' | 'PREVIOUS_DISCOUNTS', values: string | Array | number, operator: 'IN' | 'NOT_IN' | 'EQUAL' | 'GREATER_THAN', discountId?: string | undefined}>; export type PromotionSegment = {id?: number | undefined, name?: string | undefined, defaultValue?: Array | undefined, value?: (Array | string) | undefined}; export type PromotionConditionDetail = {type: 'SKU' | 'QUANTITY' | 'CATEGORY' | 'ATTRIBUTE' | 'PRICE_TYPE' | 'CHANEL' | 'ORDER_VALUE' | 'USER_SEGMENT' | 'USER_TYPE' | 'DATE' | 'SKU_QUANTITY' | 'COLLECTION' | 'PRICE' | 'ORDER_LINE_ITEMS' | 'REGION' | 'PREVIOUS_DISCOUNTS', values: string | number | Array, operator: 'NOT_EQUAL' | 'EQUAL' | 'GREATER_THAN' | 'LESS_THAN' | 'GREATER_THAN_OR_EQUAL_TO' | 'LESS_THAN_OR_EQUAL_TO' | 'NOT_IN' | 'IN'}; export type PromotionCondition = Array<{id?: (string | null) | undefined, operator: 'AND' | 'OR', includeOperator?: ('AND' | 'OR') | undefined, excludeOperator?: ('AND' | 'OR') | undefined, conditions: Array, set?: ('A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z') | undefined}>; export type CouponLimits = Array<{type?: ('SITE_WIDE' | 'PER_USER' | 'ORDER') | undefined, quantity?: number | undefined}>; export type PromotionTermsAndConditions = Array<{name?: string | undefined, description?: string | undefined}>; export type PromotionMessages = Array<{title?: string | undefined, message?: string | undefined, pages?: Array | undefined, locales?: Array | undefined}>; export type ProximityMessages = {threshold?: number | undefined, localizedMessages?: (Array<{locales?: Array | undefined, message?: string | undefined}>) | undefined}; export type PromotionShipmentMethods = Array; export type PromotionPriceLists = Array; export type CouponAdditionalAttributes = Array<{name: string, values: string | Array}>; export type CreateCouponRequest = {name: string, startAt: string, endAt: string, type: 'PRODUCT' | 'CART' | 'QUANTITY' | 'SHIPPING' | 'BUYGET' | 'SPEND_GET', buyOperator?: ('AND' | 'OR') | undefined, isExclusive?: boolean | undefined, couponCodes: Array, details: Array<{discounts: PromotionDiscounts, targetProducts: PromotionTargetProduct, conditions: PromotionCondition}>, limits?: CouponLimits | undefined, termsAndConditions?: PromotionTermsAndConditions | undefined, promotionMessages?: PromotionMessages | undefined, proximityMessages?: ProximityMessages | undefined, shipmentMethodIds?: PromotionShipmentMethods | undefined, eligiblePriceLists?: PromotionPriceLists | undefined, additionalAttributes?: CouponAdditionalAttributes | undefined, note?: string | undefined, level?: number | undefined, stackingType?: ('STACKABLE' | 'EXCLUSIVE' | 'TYPE_EXCLUSIVE' | 'UNIVERSAL') | undefined, isStackable?: boolean | undefined, isAlwaysApplied?: boolean | undefined, singleMultiUse?: ('SINGLE_USE' | 'MULTI_USE') | undefined}; export type CreateApiCouponsArgs = {'x-fabric-tenant-id': string, 'x-fabric-channel-id'?: string | undefined, 'x-client-id'?: string | undefined, 'x-fabric-request-id'?: string | undefined, body: CreateCouponRequest}; export type CreateApiCouponsOptions = {baseUrl?: string | undefined, AuthorizationToken?: string | undefined, fetch?: typeof fetch | undefined}; export const createdAt = z.string(); export const updatedAt = z.string(); export const promotionMessages = z.array(z.object({title: z.string().optional(), message: z.string().optional(), pages: z.array(z.string()).optional(), locales: z.array(z.string()).optional()})); export const createCouponResponse = z.object({id: z.string(), name: z.string(), status: z.enum(["ACTIVE", "SCHEDULED", "DISABLED", "EXPIRED"]), startAt: z.string(), endAt: z.string(), type: z.enum(["PRODUCT", "CART", "QUANTITY", "SHIPPING", "BUYGET", "SPEND_GET"]), couponCodesCount: z.number().int().optional(), createdAt: createdAt, updatedAt: updatedAt, level: z.number().int().gte(0).lte(5).optional(), stackingType: z.enum(["STACKABLE", "EXCLUSIVE", "TYPE_EXCLUSIVE", "UNIVERSAL"]).optional(), isStackable: z.boolean().optional(), isAlwaysApplied: z.boolean().optional(), singleMultiUse: z.enum(["MULTI_USE", "SINGLE_USE"]).optional(), couponCodes: z.array(z.string()).optional(), promotionMessages: promotionMessages.optional(), additionalAttributes: z.array(z.object({name: z.string(), values: z.union([z.string(), z.array(z.string())]), attributeId: z.number().optional()})).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 }; /** * Create coupon * * POST /coupons */ export const createApiCoupons = async (args: CreateApiCouponsArgs, options?: CreateApiCouponsOptions) => { const {baseUrl = "https://api.fabric.inc/v3", fetch: fetchFn = fetch, AuthorizationToken} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args['x-fabric-tenant-id'] != null) headers.set("x-fabric-tenant-id", String(args['x-fabric-tenant-id'])) if (args['x-fabric-channel-id'] != null) headers.set("x-fabric-channel-id", String(args['x-fabric-channel-id'])) if (args['x-client-id'] != null) headers.set("x-client-id", String(args['x-client-id'])) if (args['x-fabric-request-id'] != null) headers.set("x-fabric-request-id", String(args['x-fabric-request-id'])) if (AuthorizationToken !== undefined) headers.set('authorization', `Bearer ${AuthorizationToken}`) const url = `${baseUrl.replace(/\/+$/, '')}/coupons${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(createCouponResponse, await res.json()) };