import { z } from 'zod'; import { VALID_EVENT_NAMES } from './utils'; const nullableString = (max: number) => z.string().max(max).nullable().optional(); const stringMap = z.record(z.string(), z.string()).optional(); export const trackRequestSchema = z.object({ eventName: z.string().max(64).optional(), event_name: z.string().max(64).optional(), eventId: nullableString(256), event_id: nullableString(256), userId: nullableString(256), user_id: nullableString(256), email: nullableString(256), phone: nullableString(32), firstName: nullableString(100), lastName: nullableString(100), city: nullableString(100), state: nullableString(100), zip: nullableString(20), currency: nullableString(3), productName: nullableString(200), pageUrl: nullableString(2048), page_url: nullableString(2048), value: z.number().finite().nonnegative().max(9_999_999).nullable().optional(), contentIds: z.array(z.string().max(512)).max(100).nullable().optional(), click_ids: stringMap, utms: stringMap, behavioral_data: z.record(z.string(), z.unknown()).optional(), consent: z.object({ ads: z.string().max(16).optional(), analytics: z.string().max(16).optional() }).nullable().optional(), }).passthrough().superRefine((payload, ctx) => { const eventName = payload.eventName ?? payload.event_name; if (!eventName) { ctx.addIssue({ code: 'custom', path: ['eventName'], message: 'eventName é obrigatório' }); } else if (!VALID_EVENT_NAMES.has(eventName)) { ctx.addIssue({ code: 'custom', path: ['eventName'], message: `eventName desconhecido: ${eventName}` }); } }); const buyerSchema = z.object({ email: z.string().email().optional(), phone: z.string().max(32).optional(), name: z.string().max(200).optional(), }).passthrough(); export const hotmartWebhookSchema = z.object({ data: z.object({ buyer: buyerSchema.optional(), purchase: z.object({ status: z.string().max(64).optional(), transaction: z.string().max(256).optional(), price: z.object({ value: z.number().finite().optional(), currency_value: z.string().max(3).optional() }).passthrough().optional(), }).passthrough().optional(), product: z.object({ id: z.union([z.string(), z.number()]).optional(), ucode: z.string().optional(), name: z.string().max(200).optional() }).passthrough().optional(), }).passthrough().optional(), }).passthrough(); export const kiwifyWebhookSchema = z.object({ order_status: z.string().max(64).optional(), order_id: z.union([z.string(), z.number()]).optional(), order_value: z.union([z.string(), z.number()]).optional(), Customer: z.object({ email: z.string().email().optional(), mobile: z.string().max(32).optional(), full_name: z.string().max(200).optional() }).passthrough().optional(), Product: z.object({ product_id: z.union([z.string(), z.number()]).optional(), product_name: z.string().max(200).optional() }).passthrough().optional(), }).passthrough(); export const tictoWebhookSchema = z.object({ status: z.string().max(64).optional(), customer: buyerSchema.optional(), order: z.object({ hash: z.string().max(256).optional(), transaction_hash: z.string().max(256).optional(), id: z.union([z.string(), z.number()]).optional(), paid_amount: z.number().finite().optional(), total: z.number().finite().optional(), amount: z.number().finite().optional(), }).passthrough().optional(), item: z.object({ product_id: z.union([z.string(), z.number()]).optional(), product_name: z.string().max(200).optional() }).passthrough().optional(), tracking: z.record(z.string(), z.unknown()).optional(), url_params: z.record(z.string(), z.unknown()).optional(), }).passthrough(); export function schemaError(error: z.ZodError): { error: string; code: string; fields: Record } { return { error: error.issues[0]?.message ?? 'Payload inválido', code: 'INVALID_PAYLOAD', fields: z.flattenError(error).fieldErrors as Record, }; }