Ecossistema

Zod

Schemas de validação em runtime para bodies de pagamento e payloads de webhook. Dependência: zod.

Create payment

import { z } from "zod";

export const createPaymentSchema = z
  .object({
    amount_cents: z.number().int().positive().optional(),
    amount: z.number().positive().optional(),
    description: z.string().max(140).optional(),
    external_id: z.string().min(1).max(128).optional(),
    expires_in: z.number().int().min(60).max(86400).optional(),
    customer: z
      .object({
        name: z.string().optional(),
        email: z.string().email().optional(),
        document: z.string().optional(),
        phone: z.string().optional(),
      })
      .optional(),
    metadata: z.record(z.unknown()).optional(),
  })
  .refine((d) => d.amount_cents != null || d.amount != null, {
    message: "Informe amount_cents ou amount",
  });

type CreatePayment = z.infer<typeof createPaymentSchema>;

Webhook v2

import { z } from "zod";

export const webhookV2Schema = z.object({
  id: z.string(),
  type: z.string(),
  api_version: z.literal("v2"),
  created: z.number(),
  created_at: z.string(),
  livemode: z.boolean(),
  data: z.object({
    object: z.object({
      id: z.string(),
      status: z.enum(["pending", "paid", "expired", "failed", "refunded"]),
      amount_cents: z.number(),
      external_id: z.string().nullable().optional(),
    }).passthrough(),
  }),
  source: z.literal("hyzepay"),
});

// após validar HMAC:
const event = webhookV2Schema.parse(JSON.parse(rawBody));
if (event.type === "payment.paid" && event.data.object.status === "paid") {
  // liberar produto
}
Sempre valide a assinatura HMAC no raw body antes de fazer parse com Zod.