import { type NextRequest, NextResponse } from "next/server"; import { abacatePay } from "@/lib/abacatepay"; import { checkRateLimit, createRateLimitResponse, getClientIP, } from "@/lib/rate-limiter"; import { secureLogger } from "@/lib/secure-logger"; import { createDonationSchema } from "@/lib/validation-schemas"; import { DONATION_PRODUCTS } from "@/types/donation"; export async function POST(request: NextRequest) { try { // Rate limiting const ip = getClientIP(request); const rateLimit = checkRateLimit(ip, "/api/donations/create"); if (!rateLimit.allowed) { return createRateLimitResponse(rateLimit.resetAt); } const body = await request.json(); // Log detalhado para debug em produção secureLogger.info("📥 POST /api/donations/create - Recebido", { ip, body: JSON.stringify(body), timestamp: new Date().toISOString(), }); // Validação com Zod const validationResult = createDonationSchema.safeParse(body); if (!validationResult.success) { const errors = validationResult.error.issues.map((err: any) => ({ field: err.path.join("."), message: err.message, })); secureLogger.error("❌ Validação falhou", { body: JSON.stringify(body), errors: JSON.stringify(errors), timestamp: new Date().toISOString(), }); return NextResponse.json( { success: false, error: "Dados de entrada inválidos", details: errors, }, { status: 400 }, ); } const validatedData = validationResult.data; secureLogger.info("✅ Validação passou", { tier: validatedData.tier, anonymous: validatedData.anonymous, customAmount: validatedData.customAmount, timestamp: new Date().toISOString(), }); const product = DONATION_PRODUCTS[validatedData.tier as keyof typeof DONATION_PRODUCTS]; // Calcular valor final let finalAmount: number; let finalDescription: string; if (validatedData.tier === "custom") { // Converter para centavos finalAmount = Math.round(validatedData.customAmount! * 100); finalDescription = `Doação Personalizada - R$ ${validatedData.customAmount?.toFixed( 2, )}`; secureLogger.info("💰 Valor customizado calculado", { valor_original: validatedData.customAmount, valor_centavos: finalAmount, description: finalDescription, }); } else { finalAmount = product.price; finalDescription = product.name; secureLogger.info("💰 Valor fixo", { tier: validatedData.tier, amount: product.amount, price_centavos: finalAmount, }); } let pixData; // Fluxo: Anônimo ou Identificado if (validatedData.anonymous === true) { // Doação anônima - sem customer pixData = await abacatePay.createPixQrCode({ amount: finalAmount, description: finalDescription, expiresIn: 600, }); secureLogger.info("PIX criado (anônimo)", { pixId: pixData.id, amount: finalAmount, tier: validatedData.tier, }); } else { // Doação identificada - dados já validados pelo Zod pixData = await abacatePay.createPixQrCode({ amount: finalAmount, description: finalDescription, expiresIn: 600, customer: { name: validatedData.donorName, email: validatedData.donorEmail, cellphone: validatedData.donorPhone, taxId: validatedData.donorTaxId, }, }); secureLogger.info("PIX criado (identificado)", { pixId: pixData.id, amount: finalAmount, tier: validatedData.tier, donor: { name: validatedData.donorName, email: validatedData.donorEmail, phone: validatedData.donorPhone, taxId: validatedData.donorTaxId, }, }); } const response = { success: true, pixId: pixData.id, brCode: pixData.brCode, brCodeBase64: pixData.brCodeBase64, expiresAt: pixData.expiresAt, }; secureLogger.info("✅ PIX criado com sucesso", { pixId: pixData.id, expiresAt: pixData.expiresAt, }); return NextResponse.json(response); } catch (error) { secureLogger.error("❌ Erro ao criar doação", { error: error instanceof Error ? error.message : "Erro desconhecido", stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "Erro interno", }, { status: 500 }, ); } }