import {z} from 'zod' export type EarningRuleFixed = {type: 'FIXED', points: number, points_formula?: string | undefined}; export type EarningRuleProportionalOrderAmount = {type: 'PROPORTIONAL', calculation_type: 'ORDER_AMOUNT', order: {amount: {every: number, points: number, points_formula?: string | undefined}}}; export type EarningRuleProportionalOrderTotalAmount = {type: 'PROPORTIONAL', calculation_type: 'ORDER_TOTAL_AMOUNT', order: {total_amount: {every: number, points: number, points_formula?: string | undefined}}}; export type EarningRuleProportionalOrderMetadata = {type: 'PROPORTIONAL', calculation_type: 'ORDER_METADATA', order: {metadata: {every: number, points: number, points_formula?: string | undefined, property: string}}}; export type EarningRuleProportionalOrder = EarningRuleProportionalOrderAmount | EarningRuleProportionalOrderTotalAmount | EarningRuleProportionalOrderMetadata; export type EarningRuleProportionalOrderItemsQuantity = {type: 'PROPORTIONAL', calculation_type: 'ORDER_ITEMS_QUANTITY', order_items: {quantity: {every: number, points: number, points_formula?: string | undefined, object?: ('products_collection' | 'product' | 'sku') | undefined, id?: string | undefined, applicable_to?: (Array<{object?: ('products_collection' | 'product' | 'sku') | undefined, id?: string | undefined}>) | undefined}}}; export type EarningRuleProportionalOrderItemsAmount = {type: 'PROPORTIONAL', calculation_type: 'ORDER_ITEMS_AMOUNT', order_items: {amount: {every: number, points: number, points_formula?: string | undefined, object?: ('products_collection' | 'product' | 'sku') | undefined, id?: string | undefined, applicable_to?: (Array<{object?: ('products_collection' | 'product' | 'sku') | undefined, id?: string | undefined}>) | undefined}}}; export type EarningRuleProportionalOrderItemsSubtotalAmount = {type: 'PROPORTIONAL', calculation_type: 'ORDER_ITEMS_SUBTOTAL_AMOUNT', order_items: {subtotal_amount: {every: number, points: number, points_formula?: string | undefined, object?: ('products_collection' | 'product' | 'sku') | undefined, id?: string | undefined, applicable_to?: (Array<{object?: ('products_collection' | 'product' | 'sku') | undefined, id?: string | undefined}>) | undefined}}}; export type EarningRuleProportionalOrderItems = EarningRuleProportionalOrderItemsQuantity | EarningRuleProportionalOrderItemsAmount | EarningRuleProportionalOrderItemsSubtotalAmount; export type EarningRuleExpirationRules = {period_type: 'FIXED_DAY_OF_YEAR' | 'MONTH', period_value?: number | undefined, rounding_type?: ('END_OF_MONTH' | 'END_OF_QUARTER' | 'END_OF_HALF_YEAR' | 'END_OF_YEAR' | 'PARTICULAR_MONTH') | undefined, rounding_value?: number | undefined, fixed_month?: number | undefined, fixed_day?: number | undefined}; export type ValidityTimeframe = {duration?: string | undefined, interval?: string | undefined}; export type ValidityDayOfWeek = Array<0 | 1 | 2 | 3 | 4 | 5 | 6>; export type ValidityHours = {daily?: (Array<{start_time?: string | undefined, days_of_week?: (Array<0 | 1 | 2 | 3 | 4 | 5 | 6>) | undefined, expiration_time?: string | undefined}>) | undefined}; export type LoyaltiesEarningRulesCreateOrderPaidRequestBody = {validation_rule_id?: string | undefined, loyalty?: (EarningRuleFixed | EarningRuleProportionalOrder | EarningRuleProportionalOrderItems) | undefined, event?: 'order.paid' | undefined, source?: ({banner?: string | undefined}) | undefined, active?: boolean | undefined, start_date?: string | undefined, expiration_date?: string | undefined, pending_points?: ({period_type?: 'DAY' | undefined, period_value?: number | undefined} | null) | undefined, expiration_rules?: EarningRuleExpirationRules | undefined, validity_timeframe?: ValidityTimeframe | undefined, validity_day_of_week?: ValidityDayOfWeek | undefined, validity_hours?: ValidityHours | undefined, metadata?: Record | undefined}; export type EarningRuleProportionalCustomerMetadata = {type: 'PROPORTIONAL', calculation_type: 'CUSTOMER_METADATA', customer: {metadata: {every: number, points: number, points_formula?: string | undefined, property: string}}}; export type LoyaltiesEarningRulesCreateEnteredSegmentRequestBody = {validation_rule_id?: string | undefined, loyalty?: (EarningRuleFixed | EarningRuleProportionalCustomerMetadata) | undefined, segment?: ({id?: string | undefined}) | undefined, event?: 'customer.segment.entered' | undefined, source?: ({banner?: string | undefined}) | undefined, active?: boolean | undefined, start_date?: string | undefined, expiration_date?: string | undefined, expiration_rules?: EarningRuleExpirationRules | undefined, validity_timeframe?: ValidityTimeframe | undefined, validity_day_of_week?: ValidityDayOfWeek | undefined, validity_hours?: ValidityHours | undefined, metadata?: Record | undefined}; export type EarningRuleProportionalCustomEvent = {type: 'PROPORTIONAL', calculation_type: 'CUSTOM_EVENT_METADATA', custom_event: {metadata: {every: number, points: number, points_formula?: string | undefined, property: string}}}; export type LoyaltiesEarningRulesCreateCustomEventRequestBody = {validation_rule_id?: string | undefined, loyalty?: (EarningRuleFixed | EarningRuleProportionalCustomEvent) | undefined, custom_event?: ({schema_id?: string | undefined}) | undefined, event?: string | undefined, source?: ({banner?: string | undefined}) | undefined, active?: boolean | undefined, start_date?: string | undefined, expiration_date?: string | undefined, expiration_rules?: EarningRuleExpirationRules | undefined, validity_timeframe?: ValidityTimeframe | undefined, validity_day_of_week?: ValidityDayOfWeek | undefined, validity_hours?: ValidityHours | undefined, metadata?: Record | undefined}; export type LoyaltiesEarningRulesCreateTierChangeRequestBody = {validation_rule_id?: string | undefined, loyalty?: EarningRuleFixed | undefined, event?: ('customer.loyalty.tier.joined' | 'customer.loyalty.tier.left' | 'customer.loyalty.tier.upgraded' | 'customer.loyalty.tier.downgraded' | 'customer.loyalty.tier.prolonged') | undefined, loyalty_tier?: ({id?: string | undefined}) | undefined, source?: ({banner?: string | undefined}) | undefined, active?: boolean | undefined, start_date?: string | undefined, expiration_date?: string | undefined, expiration_rules?: EarningRuleExpirationRules | undefined, validity_timeframe?: ValidityTimeframe | undefined, validity_day_of_week?: ValidityDayOfWeek | undefined, validity_hours?: ValidityHours | undefined, metadata?: Record | undefined}; export type LoyaltiesEarningRulesCreateRequestBody = Array; export type CreateApiV1LoyaltiesCampaignIdEarningRulesArgs = {body: LoyaltiesEarningRulesCreateRequestBody}; export type CreateApiV1LoyaltiesCampaignIdEarningRulesOptions = {baseUrl?: string | undefined, XAppId?: string | undefined, XAppToken?: string | undefined, fetch?: typeof fetch | undefined}; export const earningRuleFixed = z.object({type: z.literal("FIXED"), points: z.number().int(), points_formula: z.string().optional()}); export const earningRuleProportionalOrderAmount = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("ORDER_AMOUNT"), order: z.object({amount: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional()})})}); export const earningRuleProportionalOrderTotalAmount = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("ORDER_TOTAL_AMOUNT"), order: z.object({total_amount: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional()})})}); export const earningRuleProportionalOrderMetadata = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("ORDER_METADATA"), order: z.object({metadata: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional(), property: z.string()})})}); export const earningRuleProportionalOrder = z.union([earningRuleProportionalOrderAmount, earningRuleProportionalOrderTotalAmount, earningRuleProportionalOrderMetadata]); export const earningRuleProportionalOrderItemsQuantity = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("ORDER_ITEMS_QUANTITY"), order_items: z.object({quantity: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional(), object: z.enum(["products_collection", "product", "sku"]).optional(), id: z.string().optional(), applicable_to: z.array(z.object({object: z.enum(["products_collection", "product", "sku"]).optional(), id: z.string().optional()})).optional()})})}); export const earningRuleProportionalOrderItemsAmount = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("ORDER_ITEMS_AMOUNT"), order_items: z.object({amount: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional(), object: z.enum(["products_collection", "product", "sku"]).optional(), id: z.string().optional(), applicable_to: z.array(z.object({object: z.enum(["products_collection", "product", "sku"]).optional(), id: z.string().optional()})).optional()})})}); export const earningRuleProportionalOrderItemsSubtotalAmount = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("ORDER_ITEMS_SUBTOTAL_AMOUNT"), order_items: z.object({subtotal_amount: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional(), object: z.enum(["products_collection", "product", "sku"]).optional(), id: z.string().optional(), applicable_to: z.array(z.object({object: z.enum(["products_collection", "product", "sku"]).optional(), id: z.string().optional()})).optional()})})}); export const earningRuleProportionalOrderItems = z.union([earningRuleProportionalOrderItemsQuantity, earningRuleProportionalOrderItemsAmount, earningRuleProportionalOrderItemsSubtotalAmount]); export const earningRuleProportionalCustomerMetadata = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("CUSTOMER_METADATA"), customer: z.object({metadata: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional(), property: z.string()})})}); export const earningRuleProportionalCustomEvent = z.object({type: z.literal("PROPORTIONAL"), calculation_type: z.literal("CUSTOM_EVENT_METADATA"), custom_event: z.object({metadata: z.object({every: z.number().int(), points: z.number().int(), points_formula: z.string().optional(), property: z.string()})})}); export const earningRuleProportional = z.union([earningRuleProportionalOrder, earningRuleProportionalOrderItems, earningRuleProportionalCustomerMetadata, earningRuleProportionalCustomEvent]); export const earningRuleEvent = z.string(); export const validityTimeframe = z.object({duration: z.string().optional(), interval: z.string().optional()}); export const validityDayOfWeek = z.array(z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(6)])); export const validityHours = z.object({daily: z.array(z.object({start_time: z.string().optional(), days_of_week: z.array(z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(6)])).optional(), expiration_time: z.string().optional()})).optional()}); export const earningRuleExpirationRules = z.object({period_type: z.enum(["FIXED_DAY_OF_YEAR", "MONTH"]), period_value: z.number().int().optional(), rounding_type: z.enum(["END_OF_MONTH", "END_OF_QUARTER", "END_OF_HALF_YEAR", "END_OF_YEAR", "PARTICULAR_MONTH"]).optional(), rounding_value: z.number().int().optional(), fixed_month: z.number().int().gte(1).lte(12).optional(), fixed_day: z.number().int().gte(1).lte(31).optional()}); export const loyaltiesEarningRulesCreateResponseBody = z.object({id: z.string(), created_at: z.string(), loyalty: z.union([earningRuleFixed, earningRuleProportional]), event: earningRuleEvent.optional(), custom_event: z.object({schema_id: z.string()}).optional(), segment: z.object({id: z.string()}).optional(), loyalty_tier: z.object({id: z.string().optional()}).optional(), pending_points: z.object({period_type: z.literal("DAY").optional(), period_value: z.number().int().gte(1).lte(90).optional()}).optional(), source: z.object({banner: z.string().optional(), object_id: z.string(), object_type: z.literal("campaign")}), object: z.literal("earning_rule"), automation_id: z.string(), start_date: z.string().optional(), expiration_date: z.string().optional(), validity_timeframe: validityTimeframe.optional(), validity_day_of_week: validityDayOfWeek.optional(), validity_hours: validityHours.optional(), metadata: z.object({}), expiration_rules: earningRuleExpirationRules.optional(), validation_rule_id: z.string().nullable(), updated_at: z.string().nullable(), active: z.boolean()}); export const createApiV1LoyaltiesCampaignIdEarningRulesResponse = z.array(loyaltiesEarningRulesCreateResponseBody); 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 Earning Rule * * POST /v1/loyalties/{campaignId}/earning-rules * * Also accepts auth: X-Voucherify-OAuth */ export const createApiV1LoyaltiesCampaignIdEarningRules = async (args: CreateApiV1LoyaltiesCampaignIdEarningRulesArgs, options?: CreateApiV1LoyaltiesCampaignIdEarningRulesOptions) => { const {baseUrl = "https://api.voucherify.io", fetch: fetchFn = fetch, XAppId, XAppToken} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (XAppId !== undefined) headers.set("X-App-Id", XAppId) if (XAppToken !== undefined) headers.set("X-App-Token", XAppToken) const url = `${baseUrl.replace(/\/+$/, '')}/v1/loyalties/${encodeURIComponent(String(args.campaignId))}/earning-rules${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(createApiV1LoyaltiesCampaignIdEarningRulesResponse, await res.json()) };